当前位置: 首页 > news >正文

数据库索引优化与查询优化——醍醐灌顶

索引优化与查询优化

哪些维度可以进行数据库调优

  • 索引失效、没有充分利用到索引-一索引建立
  • 关联查询太多JOIN (设计缺陷或不得已的需求) --SQL优化
  • 服务器调优及各个参数设置 (缓冲、线程数等)–调整my.cnf
  • 数据过多–分库分表

关于数据库调优的知识点非常分散。不同的 DBMS,不同的公司,不同的职位,不同的项目遇到的问题都不尽相
同。这里我们分为三个章节进行细致讲解。

虽然 SQL 查询优化的技术有很多,但是大方向上完全可以分成 物理查询优化逻辑查询优化 两大块。

  • 物理查询优化是通过 索引表连接方式 等技术来进行优化,这里重点需要掌握索引的使用。
  • 逻辑查询优化就是通过 SOL 等价变换 提升查询效率,直白一点就是说,换一种查询写法执行效率可能更高

1. 索引失效案例

MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且加快查询的速度,因此索引对查询的速度有着至关重要的影响

  • 使用索引可以快速地定位表中的某条记录,从而提高数据库查询的速度,提高数据库的性能
  • 如果查询时没有使用索引,查询语句就会扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢

大多数情况下都(默认)采用B+树来构建索引。只是空间列类型的索引使用R-树,并且MEMORY表还支持hash索引

其实,用不用索引,最终都是优化器说了算。优化器是基于什么的优化器?基于cost开销(CostBaseOptimizer),它不是基于规则(Rule-BasedOptimizer),也不是基于语义。怎么样开销小就怎么来。另外,SQL语句是否使用索引,跟数据库版本、数据量、数据选择度都有关系。


说明:SQL_NO_CACHE 是MySQL中的一个查询提示(Query Hint),用于在查询时告诉MySQL不要缓存该查询的结果。通常,MySQL会在查询结果中使用查询缓存来提高性能,如果查询缓存中已经存在相同的查询结果,MySQL将直接返回缓存中的结果,而不需要再执行实际的查询操作。

1.0 数据准备

#1. 数据准备CREATE DATABASE atguigudb2;USE atguigudb2;#建表
CREATE TABLE `class`
(`id`        INT(11) NOT NULL AUTO_INCREMENT,`className` VARCHAR(30) DEFAULT NULL,`address`   VARCHAR(40) DEFAULT NULL,`monitor`   INT     NULL,PRIMARY KEY (`id`)
) ENGINE = INNODBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8;CREATE TABLE `student`
(`id`      INT(11) NOT NULL AUTO_INCREMENT,`stuno`   INT     NOT NULL,`name`    VARCHAR(20) DEFAULT NULL,`age`     INT(3)      DEFAULT NULL,`classId` INT(11)     DEFAULT NULL,PRIMARY KEY (`id`)# CONSTRAINT `fk_class_id` FOREIGN KEY (`classId`) REFERENCES `t_class` (`id`)
) ENGINE = INNODBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8;SET GLOBAL log_bin_trust_function_creators = 1;#随机产生字符串
DELIMITER //
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGINDECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';DECLARE return_str VARCHAR(255) DEFAULT '';DECLARE i INT DEFAULT 0;WHILE i < nDOSET return_str = CONCAT(return_str, SUBSTRING(chars_str, FLOOR(1 + RAND() * 52), 1));SET i = i + 1;END WHILE;RETURN return_str;
END //
DELIMITER ;# 用于随机产生多少到多少的编号
DELIMITER //
CREATE FUNCTION rand_num(from_num INT, to_num INT) RETURNS INT(11)
BEGINDECLARE i INT DEFAULT 0;SET i = FLOOR(from_num + RAND() * (to_num - from_num + 1));RETURN i;
END //
DELIMITER ;# 创建往stu表中插入数据的存储过程
DELIMITER //
CREATE PROCEDURE insert_stu(START INT, max_num INT)
BEGINDECLARE i INT DEFAULT 0;SET autocommit = 0; #设置手动提交事务REPEATREPEAT#循环SET i = i + 1; #赋值INSERT INTO student (stuno, NAME, age, classId) VALUES ((START + i), rand_string(6), rand_num(1, 50), rand_num(1, 1000));UNTIL i = max_num END REPEAT;COMMIT; #提交事务
END //
DELIMITER ;# 执行存储过程,往class表添加随机数据
DELIMITER //
CREATE PROCEDURE `insert_class`(max_num INT)
BEGINDECLARE i INT DEFAULT 0; SET autocommit = 0;REPEATSET i = i + 1; INSERT INTO class (classname, address, monitor) VALUES (rand_string(8), rand_string(10), rand_num(1, 100000));UNTIL i = max_num END REPEAT;COMMIT;
END //
DELIMITER ;# 执行存储过程,往class表添加1万条数据
CALL insert_class(10000);# 执行存储过程,往stu表添加50万条数据
CALL insert_stu(100000, 500000);SELECT COUNT(*)
FROM class;SELECT COUNT(*)
FROM student;# 删除某表上的索引 存储过程
DELIMITER //
CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200), tablename VARCHAR(200))
BEGINDECLARE done INT DEFAULT 0;DECLARE ct INT DEFAULT 0;DECLARE _index VARCHAR(200) DEFAULT '';DECLARE _cur CURSOR FOR SELECT index_nameFROM information_schema.STATISTICSWHERE table_schema = dbname AND table_name = tablename AND seq_in_index = 1 AND index_name <> 'PRIMARY';#每个游标必须使用不同的declare continue handler for not found set done=1来控制游标的结束DECLARE  CONTINUE HANDLER FOR NOT FOUND SET done=2 ;
#若没有数据返回,程序继续,并将变量done设为2OPEN _cur;FETCH _cur INTO _index;WHILE  _index<>'' DO SET @str = CONCAT("drop index " , _index , " on " , tablename ); PREPARE sql_str FROM @str ;EXECUTE  sql_str;DEALLOCATE PREPARE sql_str;SET _index=''; FETCH _cur INTO _index; END WHILE;CLOSE _cur;
END //
DELIMITER ;# 执行存储过程
CALL proc_drop_index("atguigu2", "student");

2.1 全值匹配

# 创建索引前后
# 145 ms (execution: 126 ms, fetching: 19 ms)
# 76 ms (execution: 34 ms, fetching: 42 ms)
SELECT SQL_NO_CACHE *
FROM student
WHERE age = 30AND classId = 4 AND name = 'abcd';CREATE INDEX idx_age ON student (age);

再创建一个索引,发现使用的联合索引

CREATE INDEX idx_age_classId ON student(age, classId);
EXPLAIN SELECT SQL_NO_CACHE *
FROM student
WHERE age = 30AND classId = 4AND name = 'abcd';
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEstudentnullrefidx_age_classIdidx_age_classId10const,const1210Using where

继续创建这三个字段的联合索引,发现使用的3个的联合索引

CREATE INDEX idx_age_classId_name ON student(age, classId, name);

2.2 最佳左前缀法则(联合索引)

MySQL建立联合索引时会遵守最佳左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配;

# 使用idx_age_classId索引 顺序一致
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name='abcd';
# 没用上索引 因为没有classid开头顺序的索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid=1 AND student.name='abcd';

删掉前两个索引,保留student(age, classId, name);

发现虽然用上了索引,但是key_len=5,说明只用上了联合索引的一部分age这个字段(int4字节+1null)

先取查询条件的classid到索引第一个列匹配无结果,再取age匹配找到可用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid=1 AND age=30 AND student.name='abcd';
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEstudentnullrefidx_age_classId,idx_age_classId_nameidx_age_classId_name73const,const,const1100null

这样则完全没有使用上索引:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid=1 AND student.name='abcd';

先取查询条件的classid到索引第一个列匹配无结果,再取name匹配仍然无结果,因为索引只存在age开头的这个。

结论:MySQL可以为多个字段创建索引,一个索引可以包括16个字段。对于多列索引,过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。如果查询条件中没有使用这些字段中第1个字段时,多列(或联合)索引不会被使用

Alibaba《Java开发手册》:索引文件具有B-Tree的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引

2.3 主键插入顺序

对于一个使用InnoDB存储引擎的表来说,在我们没有显式的创建索引时,表中的数据实际上都是存储在聚簇索引的叶子节点的。而记录又是存储在数据页中的,数据页和记录又是按照记录主键值从小到大的顺序进行排序,所以如果我们插入的记录的主键值是依次增大的话,那我们每插满一个数据页就换到下一个数据页继续插,而如果我们插入的主键信息忽大忽小的话,就比较麻烦,假设某个数据页存储的记录已经满了,它存储的主键值在1~100之间

c095bab845e7571c721abba0ba8120b8

如果此时再插入一条主键值为 9 的记录,那它插入的位置就如下图:

3c7700f3a5e2f48f70bf88acf4f487b4

可这个数据页已经满了,再插进来咋办呢?我们需要把当前页面分裂成两个页面,把本页中的一些记录移动到新创建的这个页中。页面分裂和记录移位意味着什么?意味着:性能损耗!所以如果我们想尽量避免这样无谓的性能损耗,最好让插入的记录的主键值依次递增,这样就不会发生这样的性能损耗了。所以我们建议:让主键具有AUTO_INCREMENT,让存储引擎自己为表生成主键,而不是我们手动插入 ,比如: person_info 表:

CREATE TABLE person_info
(id           INT UNSIGNED NOT NULL AUTO_INCREMENT,name         VARCHAR(100) NOT NULL,birthday     DATE         NOT NULL,phone_number CHAR(11)     NOT NULL,country      varchar(100) NOT NULL,PRIMARY KEY (id),KEY idx_name_birthday_phone_number (name(10), birthday, phone_number)
);

自定义的主键列id拥有AUTO_INCREMENT属性,在插入记录时存储引擎会自动为我们填入自增的主键值。这样的主键占用空间小,顺序写入,减少页分裂。

2.4 计算、函数导致索引失效

CREATE INDEX idx_name ON student(NAME);
# 可以使用上索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';
# 无法使用索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';

第一种:索引优化生效,因为可以使用二级索引进行匹配,第二种:索引优化失效,因为使用到了函数,但是对于mysql来说,函数作用的是什么并不知道,所以不能使用索引。


CREATE INDEX idx_sno ON student(stuno);
# 无法使用索引
EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001;
# 可以使用索引
EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno = 900000;

2.5 类型转换(自动或手动)导致索引失效

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name=123;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name='123';

name=123发生类型转化,索引失效

结论:设计实体类属性时,一定要与数据库字段类型相对应。否则,就会出现类型转换的情况

1.6 范围条件右边的列索引失效

CREATE INDEX idx_age_cid_name ON student(age, classId, name);EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.classId>20 AND student.name = 'abc';

key_len=10,age和classid加起来,没有name

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEstudentnullrangeidx_age_cid_nameidx_age_cid_name10null1872810Using index condition

范围右边的列不能使用。比如:(<) (<=) (>) (>=) 和 between等

如果这种sql出现较多,应该注意联合索引建立顺序,将范围查询条件放置语句最后:

CREATE INDEX idx_age_name_classid ON student(age, name, classid);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name = 'abc' AND student.classId>20;

应用开发中范围查询,例如:金额查询,日期查询往往都是范围查询。应将查询条件放置where语句最后。

1.7 不等于(!=或者<>)索引失效

CREATE INDEX idx_name ON student(NAME);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc';
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name != 'abc';

1.8 is null可以使用索引, is not null无法使用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL;

最好在设计数据表的时候就将字段设置为NOT NULL约束,比如你可以将INT类型的字段,默认值为0,将字符类型的默认值设置为空字符串(‘’)

同理,在查询中使用NOT LIKE也无法使用索引,导致全表扫描

1.9 like以通配符%开头索引失效

在使用LIKE关键字进行查询的查询语句中,如果匹配字符串的第一个字符为”%“,索引就不会起作用。只有”%“不在第一个位置,索引才会起作用

# 使用索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name LIKE 'ab%';
# 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name LIKE '%ab%';

Alibaba《Java开发手册》:【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决

1.10 OR前后存在非索引的列,索引失效

在WHERE子句中,如果在OR前的条件列进行了索引,而OR后的条件列没有进行索引,那么索引会失效。也就是说,OR前后的两个条件中的列都是索引时,查询中才使用索引

因为OR的含义就是两个只要满足一个即可,因此只有一个条件列进行了索引是没有意义的,只要有条件列没有进行索引,就会进行全表扫描,因此索引的条件列也会失效

CREATE INDEX idx_age ON student(age);
# 因为classid字段没有索引,所以没有使用索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=10 OR classid=100;# 因为age字段和name字段上都有索引,所以查询中使用了索引。
# 这里使用到了index_merge,简单说index_merge就是对age和name分别进行了扫描,然后将这两个结果集进行了合并。这样的好处就是避免了全表扫描
CREATE INDEX idx_name ON student(name);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=10 OR name= 'able';

1.11 数据库和表的字符集统一使用utf8mb4

统一使用utf8mb4(5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。不同的字符集进行比较前需要进行转换会造成索引失效

1.12 一般性建议

对于单列索引,尽量选择针对当前query过滤性更好的索引

在选择组合索引的时候,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好

在选择组合索引的时候,尽量选择能够包含当前query中的where子句中更多字段的索引

在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面

总之,书写SQL语句时,尽量避免造成索引失效的情况

2. 关联查询优化

2.0 数据准备

CREATE TABLE IF NOT EXISTS type
(id   INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,card INT(10) UNSIGNED NOT NULL,PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS book
(bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,card   INT(10) UNSIGNED NOT NULL,PRIMARY KEY (bookid)
);# 20条
INSERT INTO type(card)
VALUES (FLOOR(1 + (RAND() * 20)));
# 20条
INSERT INTO book(card)
VALUES (FLOOR(1 + (RAND() * 20)));

2.1 左外连接

没有索引type全是all:

EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEtypenullALLnullnullnullnull20100null
1SIMPLEbooknullALLnullnullnullnull20100Using where; Using join buffer (hash join)
CREATE INDEX Y ON book (card);
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEtypenullALLnullnullnullnull20100null
1SIMPLEbooknullrefYY4atguigu2.type.card1100Using index

可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。这是由左连接特性决定的。LEFT JOIN条件用于确定如何从右表搜索行,左边一定都有,所以右边是我们的关键点,一定需要建立索引;

2.2 内连接

对于内连接来讲,如果表的连接条件中只能有一个字段有索引,则有索引的字段会被作为被驱动表

对于内连接来说,在两个表的连接条件都存在索引的情况下,会选择小表作为驱动表——小表驱动大表

CREATE INDEX Y ON book (card);
DROP INDEX Y ON book;EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card = book.card;CREATE INDEX X ON type (card);
DROP INDEX X ON type;

2.3 JOIN语句原理

JOIN方式连接多个表,本质就是各个表之间数据的循环匹配。MySQL5.5版本之前,MySQL只支持一种表间关联方式,就是嵌套循环(Nested Loop Join)。如果关联表的数据量很大,则join关联的执行时间会非常长。MySQL5.5以后的版本中,MySQL通过引入BNLJ算法来优化嵌套执行

(1) 驱动表和被驱动表

  • 驱动表就是主表,被驱动表就是从表、非驱动表
  • 对于内连接来说
  • SELECT * FROM A JOIN B ON …
  • A一定是驱动表吗?不一定,优化器会根据你查询语句做优化,决定先查哪张表。先查询的那张表就是驱动表,反之就是被驱动表。通过EXPLAIN关键字可以查看
  • 对于外连接来说
  • SELECT FROM A LEFT JOIN B ON …
    #或
    SELECT
    FROM B RIGHT JOIN A ON …
  • 通常认为A就是驱动表,B就是被驱动表。但也未必。测试如下:
CREATE TABLE a(f1 INT, f2 INT, INDEX(f1))ENGINE=INNODB;
CREATE TABLE b(f1 INT, f2 INT)ENGINE=INNODB;INSERT INTO a VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6);INSERT INTO b VALUES(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);# 测试1 使用了索引f1
EXPLAIN SELECT * FROM a LEFT JOIN b ON(a.f1=b.f1) WHERE (a.f2=b.f2);
# 测试2 没有使用索引
EXPLAIN SELECT * FROM a LEFT JOIN b ON(a.f1=b.f1) AND (a.f2=b.f2);

(2) Simple Nested-Loop Join(简单嵌套循环连接)

算法相当简单,从表A中取出一条数据1,遍历表B,将匹配到的数据放到result…以此类推,驱动表A中的每一条记录与被驱动表B的记录进行判断

d90c5fae255ae5bb4c07d5271de14d74

可以看到这种方式效率是非常低的,以上述表A数据100条,表B数据1000条计算,则A*B=10万次。开销统计如下:

MySQL-第10章-索引优化和查询优化 - 图24

当然mysql肯定不会这么粗暴的去进行表的连接,所以就出现了后面的两种对Nested-Look Join优化算法

(3) Index Nested-Loop Join(索引嵌套循环连接)

Index Nested-Loop Join其优化的思路主要是为了减少内层表数据的匹配次数,所以要求被驱动表上必须有索引才行。通过外层表匹配条件直接与内层表索引进行匹配,避免和内层表的每条记录去进行比较,这样极大的减少了对内层表的匹配次数

9f9a582f70a5354a14ac7fff0b3eb489

驱动表中的每条记录通过被驱动表的索引进行访问,因为索引查询的成本是比较固定的,故mysql优化器都倾向于使用记录数少的表作为驱动表(外表)

84855c0392f298f7947444cfcb0bb71b

如果被驱动表加索引,效率是非常高的,但如果索引不是主键索引,所以还得进行一次回表查询。相比,被驱动表的索引是主键索引,效率会更高

(4) Block Nested-Loop Join(块嵌套循环连接)

如果存在索引,那么会使用index的方式进行join,如果join的列没有索引,被驱动表要扫描的次数太多了。每次访问被驱动表,其表中的记录都会被加载到内存中,然后再从驱动表中取一条与其匹配,匹配结束后清除内存,然后再从驱动表中加载一条记录,然后把被驱动表的记录再加载到内存匹配,这样周而复始,大大增加了IO的次数。为了减少被驱动表的IO次数,就出现了Block Nested-Loop Join的方式

不再是逐条获取驱动表的数据,而是一块一块的获取,引入了join buffer缓冲区,将驱动表join相关的部分数据列(大小受join buffer的限制)缓存到join buffer中,然后全表扫描被驱动表,被驱动表的每一条记录一次性和join buffer中所有驱动表记录进行匹配(内存中操作),将简单嵌套循环中的多次比较合并成一次,降低了被驱动表的访问频率

注意:这里缓存的不只是关联表的列,SELECT后面的列也会缓存起来

在一个有N个join关联的sql中会分配N-1个join buffer。所以查询的时候尽量减少不必要的字段,可以让join buffer中可以存放更多的列

MySQL-第10章-索引优化和查询优化 - 图27

MySQL-第10章-索引优化和查询优化 - 图28

参数设置:

block_nested_loop:通过show variables like ‘%optimizer_switch%’查看block_nested_loop状态。默认是开启的

join_buffer_size:驱动表能不能一次加载完,要看join buffer能不能存储所有数据,默认情况下join_buffer_size=256k

join_buffer_size的最大值在32位系统可以申请4G,而在64位操作系统下可以申请大于4G的Join Buffer空间(64位Windows除外,其大值会被截断为4G并发出警告)

(5) Join小结

整体效率比较:INLJ>BNLJ>SNLJ

永远用小结果集驱动大结果集(其本质就是减少外层循环的数据数量)(小的度量单位指的是 表行数*每行大小)

SELECT t1.b, t2.* FROM t1 straight_join t2 ON (t1.b=t2.b) WHERE t2.id<=100; #推荐SELECT t1.b, t2.* FROM t2 straight_join t1 ON (t1.b=t2.b) WHERE t2.id<=100; #不推荐

为被驱动表匹配的条件增加索引(减少内存表的循环匹配次数)

增大join buffer size的大小(一次缓存的数据越多,那么内层包的扫表次数就越少)

减少驱动表不必要的字段查询(字段越少,join buffer所缓存的数据就越多)

(6) Hash Join

从MySQL的8.0.20版本开始将废弃BNLJ,因为从MySQL8.0.18版本开始就加入了hash join默认都会使用hash join

Nested Loop:对于被连接的数据子集较小的情况,Nested Loop是个较好的选择

Hash Join是做大数据集连接时的常用方式,优化器使用两个表中较小(相对较小)的表利用Join Key在内存中建立散列表,然后扫描较大的表并探测散列表,找出与Hash表匹配的行

这种方式适用于较小的表完全可以放于内存中的情况,这样总成本就是访问两个表的成本之和

在表很大的情况下并不能完全放入内存,这时优化器会将它分割成若干不同的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要求有较大的临时段从而尽量提高I/O的性能

它能够很好的工作于没有索引的大表和并行查询的环境中,并提供最好的性能。大多数人都说它是Join的重型升降机。Hash Join只能应用于等值连接(如WHERE A.COL1=B.COL2),这是由Hash的特点决定的

MySQL-第10章-索引优化和查询优化 - 图29

3. 子查询优化

MySQL从4.1版本开始支持子查询,使用子查询可以进行SELECT语句的嵌套查询,即一个SELECT查询的结果作为另一个SELECT语句的条件。子查询可以一次性完成很多逻辑上需要多个步骤才能完成的SQL操作。

子查询是 MySQL 的一项重要的功能,可以帮助我们通过一个 SQL 语句实现比较复杂的查询。但是,子查询的执行效率不高。原因:

执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表,然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。这样会消耗过多的CPU和IO资源,产生大量的慢查询。

子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响。

对于返回结果集比较大的子查询,其对查询性能的影响也就越大。在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询 不需要建立临时表 ,其速度比子查询要快 ,如果查询中使用索引的话,性能就会更好。

在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快,如果查询中使用索引的话,性能就会更好。


举例:查询学生表中是班长的学生信息

使用子查询创建班级表中班长的索引

CREATE INDEX idx_moniitor ON class (monitor);EXPLAIN
SELECT *
FROM student stu1
WHERE stu1.`stuno` IN (SELECT monitor FROM class c WHERE monitor IS NOT NULL);

推荐:使用多表查询

EXPLAIN SELECT stu1.* FROM student stu1 JOIN class c ON stu1.stuno = c.monitor WHERE c.monitor IS NOT NULL;

举例:取所有不为班长的同学

# 不推荐
EXPLAIN
SELECT SQL_NO_CACHE a.*
FROM student a
WHERE a.stuno NOT IN (SELECT monitor FROM class b WHERE monitor IS NOT NULL);
# 推荐
EXPLAIN
SELECT SQL_NO_CACHE a.*
FROM student aLEFT OUTER JOIN class b ON a.stuno = b.monitor
WHERE b.monitor IS NULL;

结论:尽量不要使用NOT IN 或者 NOT EXISTS,用LEFT JOIN xxx ON xx WHERE xx IS NULL替代

4. 排序优化

4.1 排序优化

问题:在 WHERE 条件字段上加索引,但是为什么在 ORDER BY 字段上还要加索引呢?

回答:在MySQL中,支持两种排序方式,分别是FileSort和Index排序

  • Index排序中,索引可以保证数据的有序性,不需要再进行排序,效率更高
  • FileSort排序则一般再内存中进行排序,占用CPU较多。如果待排结果较大,会产生临时文件I/O到磁盘进行排序的情况,效率较低

优化建议

  • SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中 避免全表扫描 ,在 ORDER BY 子句 避免使用 FileSort 排序 。当然,某些情况下全表扫描,或者 FileSort 排序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率
  • 尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列;如果不同就使用联合索引。
  • 无法使用 Index 时,需要对 FileSort 方式进行调优

4.2 测试

删除student、class索引

以下是否能使用到索引,能否去掉using filesort

过程一:没有使用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age, classid;EXPLAIN SELECT SQL_NO_CACHE * FROM student ORDER BY age, classid LIMIT 10;

过程二:创建索引,但order by时不limit,索引失效

CREATE INDEX idx_age_classid_name ON student(age,classid,NAME);

SELECT SQL_NO_CACHE * FROM student ORDER BY age, classid;为何没有使用索引?实际上SQL在执行时,优化器会考虑成本问题,虽然有索引,但是此索引是一个二级索引,那么如果通过索引排完序后需要回表查询其他的所有列信息。干脆直接在内存中做排序发现花费的时间还要少,所以就没有使用索引。(注意:并不是任何情况下有索引就一定会使用,优化器是考虑时间成本进行选择最优的执行计划)。如果sql换成SELECT SQL_NO_CACHE age, classid FROM student ORDER BY age, classid;就会使用上索引。这里不需要回表(覆盖索引)


过程三:order by时顺序错误,索引失效

创建索引age,classid,stuno 以下哪些索引失效

CREATE INDEX idx_age_classid_stuno ON student(age,classid,stuno);
# 失效
EXPLAIN SELECT * FROM student ORDER BY classid LIMIT 10;
# 失效
EXPLAIN SELECT * FROM student ORDER BY classid,NAME LIMIT 10;
# 有效
EXPLAIN SELECT * FROM student ORDER BY age,classid,stuno LIMIT 10;
# 有效
EXPLAIN SELECT * FROM student ORDER BY age,classid LIMIT 10;
# 有效
EXPLAIN SELECT * FROM student ORDER BY age LIMIT 10;

过程四:order by时规则不一致,索引失效

顺序错,不索引;方向反,不索引

CREATE INDEX idx_age_classid_stuno ON student(age,classid,stuno);
# 失效 方向反
EXPLAIN SELECT * FROM student ORDER BY age DESC, classid ASC LIMIT 10;
# 失效 最左前缀法则
EXPLAIN SELECT * FROM student ORDER BY classid DESC, NAME DESC LIMIT 10;
# 失效 方向反
# 没有使用索引是因为,最后还要按照classid逆序,所以不如直接文件排序。
EXPLAIN SELECT * FROM student ORDER BY age ASC, classid DESC LIMIT 10;
# 有效
EXPLAIN SELECT * FROM student ORDER BY age DESC, classid DESC LIMIT 10;

结论:ORDER BY子句,尽量使用index方式排序,避免使用FileSort方式排序


过程五:无过滤,不索引

# 虽然使用了索引,但是key_len都是5,并没有使用到ORDER BY后面的,是因为经过WHERE的筛选剩下的数据不是太多,所以就没有使用
EXPLAIN SELECT * FROM student WHERE age=45 ORDER BY classid;
EXPLAIN SELECT * FROM student WHERE age=45 ORDER BY classid,name;
# 前者没有使用索引,后者使用了索引,前者是因为先进行排序的,再去过滤后,最后回表查询出所有的字段信息,花费的时间会更多。
# 后者因为只取前十条,其中索引排完序再筛选完后取前十条会更快一些
EXPLAIN SELECT * FROM student WHERE classid=45 ORDER BY age;
EXPLAIN SELECT * FROM student WHERE classid=45 ORDER BY age limit 10;

4.3 小结

INDEX a_b_c(a,b,c)

order by 能使用索引最左前缀
- ORDER BY a
- ORDER BY a,b
- ORDER BY a,b,c
- ORDER BY a DESC, b DESC, c DESC

如果WHERE使用索引的最左前缀定义为常量,则order by能使用索引
- WHERE a=const ORDER BY b,c
- WHERE a=const AND b=const ORDER BY c
- WHERE a=const ORDER BY b,c
- WHERE a=const AND b>const ORDER BY b,c

不能使用索引进行排序
- ORDER BY a ASC, b DESC, c DESC /排序不一致/
- WHERE g=const ORDER BY b,c /丢失a索引/
- WHERE a=const ORDER BY c /丢失b索引/
- WHERE a=const ORDER BY a,d /d不是索引的一部分/
- WHERE a in (…) ORDER BY b,c /对于排序来说,多个相等条件也是范围查询/

4.4 filesort算法:双路排序和单路排序

在MySQL中,当需要进行ORDER BY或GROUP BY操作时,可能会使用到filesort算法。filesort算法用于对查询结果集进行排序,以满足ORDER BY或GROUP BY子句的要求。根据不同的场景和配置,MySQL中的filesort算法分为双路排序(Two-Phase Sort)和单路排序(One-Phase Sort)两种。

双路排序(Two-Phase Sort):

双路排序是默认情况下的排序算法,它执行两个排序阶段。首先,MySQL会尝试使用索引完成排序,如果存在适合的索引来满足ORDER BY或GROUP BY条件,那么排序就是在索引的帮助下完成的。如果索引无法满足排序需求,MySQL会使用双路排序。

在双路排序中,MySQL首先尝试使用内存(sort_buffer)进行排序。如果排序所需的内存超出了sort_buffer的设置,MySQL将使用磁盘临时文件进行排序。这样,双路排序使用了两种资源:内存和磁盘。通常情况下,双路排序是比较高效的排序算法,因为它充分利用了内存和磁盘的优势。

单路排序(One-Phase Sort):

单路排序是一种特殊的排序算法,它只使用内存来完成排序,而不涉及磁盘临时文件。单路排序通常在查询需要排序的数据较小时,MySQL可以保证所有排序数据都在sort_buffer内存中进行排序,从而避免了使用磁盘临时文件。

当查询需要排序的数据量较小时,MySQL会优先选择单路排序,因为单路排序避免了磁盘I/O,相对较快。但是,如果排序数据量较大,超出了sort_buffer的设置,MySQL会回退到双路排序。

在实际使用中,可以通过调整sort_buffer的大小来影响MySQL在排序时选择单路排序还是双路排序。如果想强制使用单路排序,可以将sort_buffer设置为一个较大的值,但这也会增加内存的消耗。综合考虑查询的性能需求和系统的资源情况,选择合适的排序算法和合理设置sort_buffer是优化查询性能的重要一环。

5. GROUP BY优化

  • group by 使用索引的原则几乎跟order by一致 ,group by 即使没有过滤条件用到索引,也可以直接使用索引。
  • group by 先排序再分组,遵照索引建的最佳左前缀法则
  • 当无法使用索引列,增大max_length_for_sort_data和sort_buffer_size参数的设置
  • where效率高于having,能写在where限定的条件就不要写在having中了
  • 减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。Order by、groupby、distinct这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。
  • 包含了order by、group by、distinct这些查询的语句,where条件过滤出来的结果集请保持在1000行以内,否则SQL会很慢

6. 优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见由非常头疼地问题就是limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大

EXPLAIN SELECT * FROM student LIMIT 2000000,10

优化思路一:在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容

EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a WHERE t.id=a.id;

# 查根据name排序的分页,在name上创建索引查找id,再根据id找具体数据,避免了使用聚簇索引数据量大的问题# 优化前 498 ms (execution: 465 ms, fetching: 33 ms)
SELECT *
FROM student s
ORDER BY name
limit 490000,10;# 优化后95 ms (execution: 72 ms, fetching: 23 ms) 
CREATE INDEX stu_name ON student(name);WITH order_name (id) AS (select id from student ORDER BY name limit 490000,10)
SELECT *
FROM student sINNER JOIN order_name o ON s.id = o.id;

优化思路二:该方案适用于主键自增的表,可以把limit查询转换成某个位置的查询

EXPLAIN SELECT * FROM student WHERE id>2000000 LIMIT 10;

7. 优先考虑覆盖索引

7.1 什么时覆盖索引

理解方式一:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了满足查询结果的数据就叫做覆盖索引

理解方式二:非聚簇复合索引的一种形式,它包括在查询里的SELECT、JOIN和WHERE子句用到的所有列(即建索引的字段正好是覆盖查询条件中所涉及的字段)。

简单说就是,索引列+主键包含SELECT 到 FROM之间查询的列。

7.2 覆盖索引的利弊

好处:

避免Innodb表进行索引的二次查询(回表):

Innodb是以聚集索引的顺序来存储的,对于innodb来说,二级索引在叶子节点中所保存的是行的主键信息,如果是用二级索引查询数据,在查找到相应的键值后,还需要通过主键进行二次查询才能获取我们真实所需要的数据

在覆盖索引中,二级索引的键值中可以获取所要的数据,避免了对主键的二次查询,减少了IO操作,提升了查询效率

可以把随机IO变成顺序IO加快查询效率(实际就是砍掉了回表时的随机IO,只留下了二级索引查询的顺序IO)

由于覆盖索引是按键值的顺序存储的,对于IO密集型的范围查找来说,对比随机从磁盘读取每一行的数据IO要少的多,因此利用覆盖索引在访问时也可以把磁盘的随机读取的IO转变成索引查找的顺序IO

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段

弊端:索引字段的维护总是有代价的。因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了。(DBA或数据架构师考虑的)

8. 给字符串添加索引

# 教师表
create table teacher
(ID    bigint unsigned primary key,email varchar(64),) engine = innodb;

讲师要使用邮箱登录,所以业务代码中一定会出现类似于这样的语句:
select col1, col2 from teacher where email=’xxx’;

如果email这个字段上没有索引,那么这个语句就只能做全表扫描

可以使用字符串前缀创建索引,详见3.2.8:

https://blog.csdn.net/a2272062968/article/details/131917628

9. 索引下推ICP

索引下推(Index Condition Pushdown,ICP)是MySQL 5.6版本中引入的一项优化技术,它在某些情况下可以提高查询性能。ICP的主要目标是减少MySQL在执行查询时需要访问表的行数,从而减少IO操作和提高查询效率。

在传统的查询执行中,MySQL首先使用索引进行条件过滤,然后再到表中检索相应的行数据。ICP通过在索引上应用查询的其他条件,来减少对表的实际访问。当MySQL发现可以通过索引直接满足查询的所有条件时,ICP将会停止对表的行数据访问,从而避免了额外的IO操作。

ICP主要适用于复合索引,即包含多个列的索引。当查询中涉及到索引的所有列,并且查询的条件都可以在索引上进行计算时,ICP就会发挥作用。

以下是ICP的一些优点和适用条件:

优点:

  • 减少了对表的实际访问,从而减少了IO操作,提高了查询性能。
  • 在某些情况下,可以避免对表的临时文件和临时表的创建和使用。

适用条件:

  • 查询涉及的索引是复合索引,包含多个列。
  • 查询涉及的索引的所有列都要在查询中使用,并且查询条件可以在索引上进行计算。
  • 表的存储引擎支持ICP,目前InnoDB和MyISAM存储引擎支持ICP。

ICP是MySQL中的一个自动优化特性,不需要显式地启用。在执行查询时,MySQL的优化器会自动判断是否可以使用ICP来优化查询计划。对于适合使用复合索引和满足ICP条件的查询,ICP会帮助提高查询性能,减少不必要的IO操作,从而加快查询的执行速度。

10. 普通索引vs唯一索引

普通索引和唯一索引应该怎么选择?其实,这两类索引在查询能力上是没差别的,主要考虑的是对更新性能的影响。所以,建议你尽量选择普通索引

在实际使用中会发现,普通索引和change buffer的配合使用,对于 数据量大 的表的更新优化还是很明显的

如果所有的更新后面,都马上伴随着对这个记录的查询,那么你应该关闭change buffer。而在其他情况下,change buffer都能提升更新性能

由于唯一索引用不上change buffer的优化机制,因此如果业务可以接受,从性能角度出发建议优先考虑非唯一索引。但是如果”业务可能无法确保”的情况下,怎么处理呢

  • 首先,业务正确性优先。我们的前提是“业务代码已经保证不会写入重复数据”的情况下,讨论性能问题。如果业务不能保证,或者业务就是要求数据库来做约束,那么没得选,必须创建唯一索引。这种情况下,本节的意义在于,如果碰上了大量插入数据慢、内存命中率低的时候,给你多提供一个排查思路。
  • 然后,在一些“归档库”的场景,你是可以考虑使用唯一索引的。比如,线上数据只需要保留半年,然后历史数据保存在归档库。这时候,归档数据已经是确保没有唯一键冲突了。要提高归档效率,可以考虑把表里面的唯一索引改成普通索引

11. 其他查询优化策略

11.1 EXISTS和IN的区分

问题:不太理解那种情况下使用EXISTS,那种情况用IN。选择的标准是能否使用表的索引吗?

回答:索引是个前提,其实选择与否还是要看表的大小。可以将选择的标准理解为小表驱动大表。在这种方式下效率是最高的

例如:

SELECT * FROM A WHERE cc IN (SELECT cc FROM B)
SELECT * FROM A WHERE EXISTS (SELECT cc FROM B WHERE B.cc=A.cc)

当A小于B时,用EXISTS。因为EXISTS的实现,相当于外表循环,实现的逻辑类似于:

for i in A
for j in B
if j.cc == i.cc then …

当B小于A时用IN,因为实现的逻辑类似于:

for i in B
for j in A
if j.cc == i.cc then …

哪个表小就用哪个表来驱动,A表小就用EXISTS,B表小就用IN

11.2 COUNT(*)与COUNT(具体字段)效率

问:在MYSQL中统计数据表的行数,可以使用三种方式:SELECT COUNT(*)、SELECT COUNT(1)、SELECT COUNT(具体字段),使用这三者之间的查询效率是怎样的?

答:

前提:如果要统计的是某个字段的非空数据行数,则另当别论,毕竟比较执行效率的前提是结果一样才可以

环节1:COUNT(*)和COUNT(1)都是对所有结果进行COUNT,COUNT(*)和COUNT(1)本质上并没有区别(二者执行时间可能略有差别,不过还是可以把它俩的执行效率看成是相等的)。如果有WHERE子句,则是对所有符合筛选条件的数据进行统计;如果没有WHERE子句,则是对数据表的数据行数进行统计

环节2:如果是MyISAM存储引擎,统计数据表的行数只需要O(1)的复杂度,这是因为每张MyISAM的数据表都有一个meta信息存储了row_count值,而一致性则由表级锁来保证。如果是InnoDB存储引擎,因为InnoDB支持事务,采用行级锁和MVCC机制,所以无法像MyISAM一样,维护一个row_count变量,因此需要采用扫描全表,进行循环+计数的方式来完成统计

环节3:在InnoDB引擎中,如果采用COUNT(具体字段)来统计数据行数,要尽量采用二级索引。因为主键采用的索引是聚簇索引,聚簇索引包含的信息多,明显会大于二级索引(非聚簇索引)。对于COUNT(*)和COUNT(1)来说,它们不需要查找具体的行,只是统计行数,系统会自动采用占用空间更小的二级索引来进行统计。如果有多个二级索引,会使用key_len小的二级索引进行扫描。当没有二级索引的时候,才会采用主键索引进行统计

11.3 关于SELECT(*)

在表查询中,建议明确字段,不要使用*作为查询的字段列表,推荐使用SELECT<字段列表>查询。

  • MySQL在解析的过程中,会通过查询数据字典将*按序转换成所有列名,这会大大的消耗资源和时间
  • 无法使用覆盖索引

11.4 LIMIT 1对优化的影响

针对的是会扫描全表的SQL语句,如果你可以确定结果集只有一条,那么加上LIMIT 1的时候,当找到一条结果的时候就不会继续扫描了,这样会加快查询速度

如果数据表已经对字段建立了唯一索引,那么可以通过索引进行查询,不会全表扫描的话,就不需要加上LIMIT 1了

11.5 多使用COMMIT

只要有可能,在程序中尽量多使用 COMMIT,这样程序的性能得到提高,需求也会因为 COMMIT 所释放的资源而减少。

COMMIT 所释放的资源:

  • 回滚段上用于恢复数据的信息
  • 被程序语句获得的锁
  • redo / undo log buffer 中的空间
  • 管理上述 3 种资源中的内部花费

相关文章:

数据库索引优化与查询优化——醍醐灌顶

索引优化与查询优化 哪些维度可以进行数据库调优 索引失效、没有充分利用到索引-一索引建立关联查询太多JOIN (设计缺陷或不得已的需求) --SQL优化服务器调优及各个参数设置 (缓冲、线程数等)–调整my.cnf数据过多–分库分表 关于数据库调优的知识点非常分散。不同的 DBMS&a…...

Student and Teacher network(学生—教师网络)与知识蒸馏

Student and Teacher network指一个较小且较简单的模型&#xff08;学生&#xff09;被训练来模仿一个较大且较复杂的模型&#xff08;教师&#xff09;的行为或预测。教师网络通常是一个经过训练在大型数据集上并在特定任务上表现良好的模型。而学生网络被设计成计算效率高且参…...

FPGA——PLD的区别以及各自的特点

目录 一、概述二、PLD的优点三、PLD的分类1、PROM&#xff08;可编程只读存储器&#xff09;&#xff1a;2、PAL&#xff08;可编程阵列逻辑&#xff09;3、GAL&#xff08;通用阵列逻辑&#xff09;4、CPLD &#xff08;复杂PLD&#xff09;5、FPGA&#xff08;现场可编程门阵…...

八、Kafka时间轮与常见问题

Kafka与时间轮 Kafka中存在大量的延时操作。 1、发送消息-超时重试机制 2、ACKS 用于指定分区中必须要有多少副本收到这条消息&#xff0c;生产者才认为写入成功&#xff08;延时 等&#xff09; Kafka并没有使用JDK自带的Timer或者DelayQueue来实现延迟的功能&#xff0c;而…...

Web端即时通讯技术(SEE,webSocket)

目录 背景简介个人见解被动推送轮询简介实现 长轮询&#xff08;comet&#xff09;简介实现 比较 主动推送长连接&#xff08;SSE&#xff09;简介实现GETPOST 效果 webSocket简介WebSocket的工作原理:WebSocket的主要优点:WebSocket的主要缺点: 实现用法一用法二 **效果** 比较…...

脑电信号处理与特征提取——4.脑电信号的预处理及数据分析要点(彭微微)

目录 四、脑电信号的预处理及数据分析要点 4.1 脑电基础知识回顾 4.2 伪迹 4.3 EEG预处理 4.3.1 滤波 4.3.2 重参考 4.3.3 分段和基线校正 4.3.4 坏段剔除 4.3.5 坏导剔除/插值 4.3.6 独立成分分析ICA 4.4 事件相关电位&#xff08;ERPs&#xff09; 4.4.1 如何获…...

分析npm run serve之后发生了什么?

首先需要明白的是&#xff0c;当你在终端去运行 npm run ****&#xff0c;会是什么过程。 根据上图的一个流程&#xff0c;就可以衍生出很多问题。 1&#xff0c;为什么不直接运行vue-cli-service serve? 因为直接运行 vue-cli-service serve&#xff0c;会报错&#xff0c…...

LINUX上操作redis 用shell7

LINUX上操作redis 用shell7 步骤1&#xff1a;连接到Linux服务器步骤2&#xff1a;安装和配置Redis步骤3&#xff1a;连接到Redis服务器步骤4&#xff1a;操作Redis数据步骤5&#xff1a;断开与Redis服务器的连接 步骤1&#xff1a;连接到Linux服务器 首先&#xff0c;需要使用…...

Python的threading模块

为引入多线程的概念&#xff0c;下面是一个例子&#xff1a; import time, datetimestartTime datetime.datetime(2024, 1, 1, 0, 0, 0) while datetime.datetime.now() < startTime:time.sleep(1)print(Program now starting on NewYear2024) 在等待time.sleep()的循环调…...

HTML5 的离线储存怎么使用,工作原理

TML5提供了一种称为离线储存&#xff08;Offline Storage&#xff09;的功能&#xff0c;它允许网页在离线时缓存和存储数据&#xff0c;以便用户可以在没有网络连接的情况下访问这些数据。离线储存是通过使用Web Storage API或者应用程序缓存&#xff08;Application Cache&am…...

FTP文件传输协议与DHCP

基本概念 主机之间传输文件是IP网络的一个重要功能 互联网早期&#xff0c;最通用方式就是使用FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;以及&#xff08;Trivial File Transfer Protocol&#xff0c;简单文件传输协议&#xff09; FTP采用…...

【UE5 多人联机教程】06-显示玩家名称

效果 可以看到玩家输入各自的名称&#xff0c;会显示到自己控制的角色头上。但是目前有一个BUG就是&#xff0c;当客户端加入游戏时会多创建一个服务端的角色。 步骤 1. 打开“BP_ThirdPersonCharacter”&#xff0c;添加一个控件组件&#xff0c;用于显示玩家名称 作为网格体…...

Rust vs Go:常用语法对比(五)

题图来自 Rust vs Go 2023[1] 81. Round floating point number to integer Declare integer y and initialize it with the rounded value of floating point number x . Ties (when the fractional part of x is exactly .5) must be rounded up (to positive infinity). 按规…...

Flutter 扩展函数项目实用之封装SizedBox

Flutter里扩展函数可以用简化代码写法&#xff0c;关键字为extension&#xff0c;伪代码写法如下&#xff1a; extension 扩展类名 on 扩展类型 { //扩展方法 } 在Flutter页面里实现控件间距会常用到SizedBox&#xff0c;可使用扩展函数封装来达到简化代码的目的&#xff0…...

EMC学习笔记(二十)EMC常用元件简单介绍(二)

EMC常用元件简单介绍&#xff08;二&#xff09; 1.瞬态抑制二极管&#xff08;TVS&#xff09;2.气体放电管3.半导体放电管 电磁兼容性元件是解决电磁干扰发射和电磁敏感度问题的关键,正确选择和使用这些元件是做好电磁兼容性设计的前提。由于每一种电子元件都有它各自的特性,…...

基本排序算法

目录 一&#xff0c;插入排序 二&#xff0c;希尔排序 三&#xff0c;选择排序 四&#xff0c;冒泡排序 五&#xff0c;快排 5.1 Hoare法 5.2 挖坑法 5.3 指针法 5.4 非递归写法 六&#xff0c;归并排序 6.1 递归 6.2 非递归 一&#xff0c;插入排序 基本思想&…...

python调用百度ai将图片/pdf识别为表格excel

python调用百度ai将图片识别为表格excel 表格文字识别(异步接口)图片转excel 表格文字识别V2图片/pdf转excel通用 表格文字识别(异步接口) 图片转excel 百度ai官方文档&#xff1a;https://ai.baidu.com/ai-doc/OCR/Ik3h7y238 使用的是表格文字识别(异步接口)&#xff0c;同步…...

Ansible最佳实践之Playbook管理滚动更新

写在前面 理解不足小伙伴帮忙指正 傍晚时分&#xff0c;你坐在屋檐下&#xff0c;看着天慢慢地黑下去&#xff0c;心里寂寞而凄凉&#xff0c;感到自己的生命被剥夺了。当时我是个年轻人&#xff0c;但我害怕这样生活下去&#xff0c;衰老下去。在我看来&#xff0c;这是比死亡…...

基于Citespace、vosviewer、R语言的文献计量学可视化分析及SCI论文高效写作方法教程

详情点击链接&#xff1a;基于Citespace、vosviewer、R语言的文献计量学可视化分析技术及全流程文献可视化SCI论文高效写作方法 前言 文献计量学是指用数学和统计学的方法&#xff0c;定量地分析一切知识载体的交叉科学。它是集数学、统计学、文献学为一体&#xff0c;注重量…...

【MATLAB】GM(1,1) 灰色预测模型及算法

一、灰色预测模型概念 灰色预测是一种对含有不确定因素的系统进行预测的方法。 灰色预测通过鉴别系统因素之间发展趋势的相异程度&#xff0c;即进行关联分析&#xff0c;并对原始数据进行生成处理来寻找系统变动的规律&#xff0c;生成有较强规律性的数据序列&#xff0c;然后…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...