MySQL学习笔记4---索引优化

5、索引优化

5.1、单表索引优化案例

CREATE TABLE IF NOT EXISTS article(
	id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
	author_id INT(10) UNSIGNED NOT NULL,
	category_id INT(10) UNSIGNED NOT NULL,
	views INT(10) UNSIGNED NOT NULL,
	comments INT(10) UNSIGNED NOT NULL,
	title VARCHAR(255) NOT NULL,
	content TEXT NOT NULL
);

INSERT INTO article(author_id,category_id,views,comments,title,content)
VALUES
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3');

SELECT * FROM article;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xs4tYsL8-1606015497079)(images4/image-20201120195949800.png)]

#查询category_id为1且comments大于1的情况下,views最多的id号和author_id

1、没有建立索引的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxmKr8Xw-1606015497081)(images4/image-20201120200405303.png)]

2、对category_id, comments, views都建立索引的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RyxP4Jyz-1606015497082)(images4/image-20201120200547131.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xRJAadl4-1606015497085)(images4/image-20201120200646193.png)]

3、只对category_id, views建立索引的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3W7rHjWB-1606015497086)(images4/image-20201120200815592.png)]

5.2、两表索引优化案例

CREATE TABLE IF NOT EXISTS class(
	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)
);
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
#重复上一条共20次

INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
#重复上一条共20次

1、从下面的explain开始分析

EXPLAIN SELECT * FROM class LEFT JOIN book on class.card=book.card

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EQTDOVGI-1606015497088)(images4/image-20201120202545884.png)]

2、只在右表上添加索引

ALTER TABLE book add INDEX idx_book_card (card)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FyageFRo-1606015497089)(images4/image-20201120203228061.png)]

3、只在左表上添加索引

ALTER TABLE class add INDEX idx_class_card (card)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uc2gX3zL-1606015497090)(images4/image-20201120204331046.png)]

  • type列由 ref 变为了 index;rows 的变化也比较明显
  • 这是由左连接的特性决定的。left join 条件用于确定如何从右表搜索行,左表一定都有
  • 所以右边是我们的关键点,一定要建立索引
  • 同样,right join 的时候应该在左表上建立索引

5.3、三表索引优化案例

CREATE TABLE IF NOT EXISTS phone(
	phoneid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	card INT(10) UNSIGNED NOT NULL,
	PRIMARY KEY(phoneid)
)ENGINE=INNODB;

INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
#重复上一条共20次

1、三表联合查询

EXPLAIN SELECT * FROM class LEFT JOIN book on class.card=book.card LEFT JOIN phone on book.card=phone.card

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPyYZ8lm-1606015497091)(images4/image-20201120205923344.png)]

2、左连接,在两个右表中都建立索引

ALTER TABLE book add INDEX idx_book_card (card)
ALTER TABLE phone add INDEX idx_phone_card (card)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ntbrO6EN-1606015497092)(images4/image-20201120210632047.png)]

  • 后两行的type都是 ref 且总的 rows 优化很好,效果不错。因此索引最好设置在经常查询的字段中
  • 尽可能减少Join语句中的NestedLoop的循环总次数,永远用小结果集驱动大的结果集
  • 优先优化NestedLoop的内层循环
  • 保证Join语句中被驱动表上Join条件字段已经被索引

5.4、索引失效(应该避免)

CREATE TABLE staffs(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名',
`age` INT NOT NULL DEFAULT 0 COMMENT'年龄',
`pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
`add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
)CHARSET utf8 COMMENT'员工记录表';
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW());
#填加索引
ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`)

5.4.1、全值匹配

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h441qiUS-1606015497093)(images4/image-20201120214849645.png)]

  • 结论:全值匹配指的是,查询的字段按照顺序在索引中都可以匹配到!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NVMc9t0G-1606015497094)(images4/image-20201120221147453.png)]

  • SQL 中查询字段的顺序,跟使用索引中字段的顺序,没有关系。优化器会在不影响 SQL 执行结果的前提下,给你自动地优化,自动按照设定索引的顺序优化SQL语句。

5.4.2、最佳左前缀法则

在这里插入图片描述
在这里插入图片描述

  • 查询字段与索引字段顺序的不同会导致,索引无法充分使用,甚至索引失效!

原因:使用复合索引,需要遵循最佳左前缀法则,即如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。

  • 结论 : 过滤条件要使用索引必须按照索引建立时的顺序 , 依次满足 , 一旦跳过某个字段 , 索引后面的字段都无法被使用。

5.4.3、 不要在索引列上做任何计算

不在索引列上做任何操作(计算、函数、(自动 or 手动)类型转换),会导致索引失效而转向全表扫描。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TDNQqyff-1606015497097)(images4/image-20201121101723258.png)]

在查询列上使用函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tK9wsMh7-1606015497098)(images4/image-20201121101923355.png)]

  • 结论:等号左边无计算!等号右边无转换!

5.4.4、 索引列上不能有范围查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7UmD0ox-1606015497099)(images4/image-20201121103039574.png)]

  • 结论:之后的索引将失效,所以将可能做范围查询的字段的索引顺序放在最后

5.4.5、尽量使用覆盖索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8P78st86-1606015497100)(images4/image-20201121103905043.png)]

结论:尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *,就是直接在B+树上取值,而避免去磁盘中读取。

5.4.6、使用不等于(!= 或者<>)的时候

mysql 在使用不等于(!= 或者<>)时,有时会无法使用索引会导致全表扫描。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SPdiH7kq-1606015497101)(images4/image-20201121104752177.png)]

5.4.7、字段的 is not null 和 和 is null

is null, is not null 也无法使用索引

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5jU1oHsH-1606015497102)(images4/image-20201121105235318.png)]

5.4.8、 like 的前后模糊匹配

like以通配符开头(’$abc…’)mysql索引失效会变成全表扫描操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2lN24rk-1606015497103)(images4/image-20201121110222132.png)]

问题:解决like’%字符串%'索引不被使用的方法?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2KwoxBD-1606015497104)(images4/image-20201121112112049.png)]

  • 结论:使用覆盖索引解决 like’%字符串%’ 索引失效问题

5.4.9、字符串不加单引号索引失效

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9BPFkjF-1606015497105)(images4/image-20201121130703284.png)]

5.4.10、少用or,用它连接时会索引失效

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlINik42-1606015497106)(images4/image-20201121130956164.png)]

解决办法:使用 union all 或者 union 来替代

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GtI9dwXd-1606015497107)(images4/image-20201121131144324.png)]

5.4.11、练习

假设 index(a,b,c)

Where 语句 索引是否被使用
where a = 3 Y,使用到 a
where a = 3 and b = 5 Y,使用到 a,b
where a = 3 and b = 5 and c = 4 Y,使用到 a,b,c
where b = 3 或者 where b = 3 and c = 4 或者 where c = 4 N
where a = 3 and c = 5 使用到 a, 但是 c 不可以,b 中间断了
where a = 3 and b > 4 and c = 5 使用到 a 和 b, c 不能用在范围之后,b 断了
where a is null and b is not null is null 支持索引 但是 is not null 不支持,所以 a 可以使用索引,但是 b 不可以使用
where a <> 3 不能使用索引
where abs(a) =3 不能使用索引
where a = 3 and b like ‘kk%’ and c = 4 Y,使用到 a,b,c
where a = 3 and b like ‘%kk’ and c = 4 Y,只用到 a
where a = 3 and b like ‘%kk%’ and c = 4 Y,只用到 a
where a = 3 and b like ‘k%kk%’ and c = 4 Y,使用到 a,b,c

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1PPzcCX0-1606015497108)(images4/image-20201121133714621.png)]

注意:虽然 name 字段使用了模糊匹配,但是 % 处于右边,可以使用索引(且全部都用到了,根据 key_len 判断),只是性能由 ref 变为了 index。

5.4.12、口诀

全职匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE 百分写最右,覆盖索引不写*;
不等空值还有 OR,索引影响要注意;
VAR 引号不可丢,SQL 优化有诀窍。

6、排序分组优化

create table tblA(
#id int primary key not null auto_increment,
age int,
birth timestamp not null
);
insert into tblA(age, birth) values(22, now());
insert into tblA(age, birth) values(23, now());
insert into tblA(age, birth) values(24, now());

create index idx_A_ageBirth on tblA(age, birth);
select * from tblA;

6.1、顺序错,必排序

MySQL支持二种方式的排序,FileSort和Index,Index效率高。它指MySQL扫描索引本身完成排序。FileSort方式效率较低。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zjlTBFO-1606015497109)(images4/image-20201121154011329.png)]

ORDER BY满足两情况,会使用Index方式排序:

  • ORDER BY语句使用索引最左前列
  • 使用where子句与OrderBy子句条件列组合满足索引最左前列

6.2、方向反,必排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PokTdcXN-1606015497110)(images4/image-20201121154507228.png)]

如果可以用上索引的字段都使用正序或者逆序,实际上是没有任何影响的,无非将结果集调换顺序;如果排序的字段,顺序有差异,就需要将差异的部分,进行一次倒置顺序,因此还是需要手动排序的!

6.3、MySQL 的排序算法

①双路排序

MySQL 4.1 之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和 orderby 列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。

从磁盘取排序字段,在 buffer 进行排序,再从磁盘取其他字段。

简单来说,取一批数据,要对磁盘进行了两次扫描,众所周知,I\O 是很耗时的,所以在 mysql4.1 之后,出现了第二种改进的算法,就是单路排序。

②单路排序

从磁盘读取查询需要的所有列,按照 order by 列在 buffer 对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机 IO 变成了顺序 IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。

③单路排序的问题

由于单路是后出的,总体而言好过双路。但是存在以下问题:

在 sort_buffer 中,方法 B 比方法 A 要多占用很多空间,因为方法 B 是把所有字段都取出, 所以有可能取出的数据的总大小超出了 sort_buffer 的容量,导致每次只能取 sort_buffer 容量大小的数据,进行排序(创建 tmp 文件,多路合并),排完再取取 sort_buffer 容量大小,再排……从而多次 I/O。

结论: 本来想省一次 I/O 操作,反而导致了大量的 I/O 操作,反而得不偿失。

6.4、优化策略

①增大 sort_butter_size 参数的设置

不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的 1M-8M 之间调整。

②增大 max_length_for_sort_data 参数的设置

mysql 使用单路排序的前提是排序的字段大小要小于 max_length_for_sort_data。
提高这个参数,会增加用改进算法的概率。但是如果设的太高,数据总容量超出 sort_buffer_size 的概率就增大,明显症状是高的磁盘 I/O 活动和低的处理器使用率。(1024-8192 之间调整)。

③减少 select 后面的查询的字段。
当 Query 的字段大小总和小于 max_length_for_sort_data 而且排序字段不是 TEXT|BLOB 类型时,会用改进后的算法——单路排序, 否则用老算法——多路排序。
两种算法的数据都有可能超出 sort_buffer 的容量,超出之后,会创建 tmp 文件进行合并排序,导致多次 I/O,但是用单路排序算法的风险会更大一些,所以要提高 sort_buffer_size。

6.5、Group By 优化

  • groupby 实质是先排序后进行分组,遵照索引建的最佳左前缀
  • 当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置
  • where高于having,能写在where限定的条件就不要去having限定了。

7、截取查询分析

7.1、慢查询日志

7.1.1、是什么

(1)MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。
(2)具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上的语句。
(3)由他来查看哪些SQL超出了我们的最大忍耐时间值,比如一条sql执行超过5秒钟,我们就算慢SQL,希望能收集超过5秒的sql,结合之前explain进行全面分析。

7.1.2、怎么用

默认情况下,MySQL 数据库没有开启慢查询日志,需要我们手动来设置这个参数。
当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件。

SQL语句 描述 备注
SHOW VARIABLES LIKE ‘%slow_query_log%’; 查看慢查询日志是否开启 默认情况下 slow_query_log 的值为 OFF,表示慢查询日志是禁用的
set global slow_query_log=1; 开启慢查询日志
SHOW VARIABLES LIKE ‘long_query_time%’; 查看慢查询设定阈值 单位秒
set long_query_time=1 设定慢查询阈值 单位秒

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehxP0ySk-1606015497111)(images4/image-20201121161643344.png)]

7.1.3、日志分析工具 mysqldumpslow

参数 描述
-s 是表示按照何种方式排序
-c 访问次数
l 锁定时间
r 返回记录
t 查询时间
al 平均锁定时间
ar 平均返回记录数
at 平均查询时间
-t 即为返回前面多少条的数据
-g 后边搭配一个正则匹配模式,大小写不敏感的
得到返回记录集最多的 10 个 SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/slow.log
得到访问次数最多的 10 个 SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log
得到按照时间排序的前 10 条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/slow.log
另外建议在使用这些命令时结合 | 和 more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/slow.log | more

7.2、Show Profile

7.2.1、开启 profile

查看 profile 是否开启:show variables like '%profiling%'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tA9X6sKD-1606015497112)(images4/image-20201121170908922.png)]

如果没有开启,可以执行 set profiling=1 开启!

7.2.2、使用 profile

执行 show prifiles 命令,可以查看最近的几次查询。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpqsCZoH-1606015497113)(images4/image-20201121171404436.png)]

根据 Query_ID,可以进一步执行 show profile cpu,block io for query Query_Id 来查看 sql 的具体执行步骤。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tkl0gXbX-1606015497114)(images4/image-20201121172204651.png)]

需要注意的结论:

  • converting HEAP to MyISAM-----查询结果太大,内存都不够用了往磁盘上搬了。
  • Creating tmp table-----创建临时表,拷贝数据到临时表,用完再删除
  • Copying to tmp table on disk-----把内存中临时表复制到磁盘,危险!!!
  • locked

猜你喜欢

转载自blog.csdn.net/qq_42372017/article/details/109923895
今日推荐