MySQL

58-MySQL索引优化与查询优化-ORDER BY和GROUP

2022-11-03  本文已影响0人  紫荆秋雪_文

一、排序优化(ORDER BY)

1、在WHERE条件字段上加索引,但是为什么在ORDER BY字段上还要加索引

在MySQL中,支持两种排序方式,分别是FileSortIndex排序

2、优化建议

二、实战

1、删除student表class表的索引

CALL proc_drop_index('atguigudb2','student');
CALL proc_drop_index('atguigudb2','class');
SHOW INDEX FROM student;
SHOW INDEX FROM class;

2、ORDER BY中没有索引

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

3、ORDER BY中时不添加 LIMIT,索引失效

3.1、添加索引

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

3.2、ORDER BY中时不添加 LIMIT

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

3.3、ORDER BY中时不添加 LIMIT,但是返回字段为索引列时(覆盖索引)

EXPLAIN
SELECT SQL_NO_CACHE age, classid, name, id
FROM student
ORDER BY age, classid;

3.4、ORDER BY中时添加 LIMIT

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

3.5、小结

由于使用了LIMIT后,查询有效数量,使用idx_age_classid_name索引后及时需要额外的回表操作,但是由于回表数量有限,相比全表扫描成本更低,所以选择使用所以

4、ORDER BY 时顺序错误,索引失效

4.1、索引顺序 索引顺序!.png

4.2、不遵守最前左原则,索引失效

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

由于idx_age_classid_name索引字段顺序为age、classid、name,但是上述ORDER BY后的排序字段都没有用到age字段,违反了最前左原则,造成索引失效

4.3、排序字段与索引字段不同,索引失效

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

由于idx_age_classid_name索引字段顺序为age、classid、name,但是上述ORDER BY后的排序字段age, classid, stuno由于无法匹配所以没有索引可用

4.4、排序字段部分匹配,使用索引

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

5、ORDER BY 时规则不一致, 索引失效 (顺序错,不索引;方向反,不索引)

5.1、索引顺序 索引顺序!.png

5.2、ORDER BY 排序字段升降序不一致

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

索引idx_age_classid_name在创建时每个字段都是已升序的方式创建的,而上述实例中排序字段都是有升序又有降序,造成索引失效

5.3、解决ORDER BY 排序字段升降序不一致

EXPLAIN
SELECT *
FROM student
ORDER BY age DESC, classid ASC
LIMIT 10;
CREATE INDEX idx_age_classid ON student (age DESC, classid ASC);

5.4、ORDER BY 排序字段的升降序与索引相同或相反,索引可用

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

6、无过滤,不索引

6.1、删除索引

CALL proc_drop_index('atguigudb2', 'student');

6.2、无索引

EXPLAIN
SELECT *
FROM student
WHERE age = 45
ORDER BY classid;

6.2、为 ORDER BY 字段创建索引

CREATE INDEX idx_cid ON student (classid);
idx_cid 索引.png

6.3、在 WHERE 没有索引的情况下,不会使用 ORDER BY 索引

EXPLAIN
SELECT *
FROM student
WHERE age = 45
ORDER BY classid;

6.4、为WHERE字段创建索引

CREATE INDEX idx_age ON student (age);
EXPLAIN
SELECT *
FROM student
WHERE age = 45
ORDER BY classid;

虽然使用了索引idx_age,但是依然使用FILESORT

6.5、为WHERE字段ORDER BY创建联合索引

CREATE INDEX idx_age_classid_name ON student (age, classid, name);
EXPLAIN
SELECT *
FROM student
WHERE age = 45
ORDER BY classid;

只有为WHERE字段ORDER BY创建联合索引才能解决FILESORT

6.6、 ORDER BY 字段顺序与索引顺序不匹配,造成FILESORT

EXPLAIN
SELECT *
FROM student
WHERE age = 45
ORDER BY name, classid;

6.7、 ORDER BY 字段索引和WHERE字段索引分别独立

CREATE INDEX idx_cid ON student (classid);
EXPLAIN
SELECT *
FROM student
WHERE classid = 45
ORDER BY age;
EXPLAIN
SELECT *
FROM student
WHERE classid = 45
ORDER BY age
LIMIT 10;

6.8、 ORDER BY 字段有索引和WHERE字段没有索引

DROP INDEX idx_cid ON student;
EXPLAIN
SELECT *
FROM student
WHERE classid = 45
ORDER BY age;
EXPLAIN
SELECT *
FROM student
WHERE classid = 45
ORDER BY age
LIMIT 10;

6.9、小结

INDEX a_b_c(a,b,c)

7、测试filesort和index排序

ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序

7.1、删除索引

CALL proc_drop_index('atguigudb2', 'student');

7.2、查询年龄为30岁的,且学生编号小于101000的学生,按用户名称排序

EXPLAIN
SELECT SQL_NO_CACHE *
FROM student
WHERE age = 30
  AND stuno < 101000
ORDER BY name;

type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。必须优化

7.3、为了去掉filesort可以创建索引

CREATE INDEX idx_age_name ON student (age, name);
EXPLAIN
SELECT SQL_NO_CACHE *
FROM student
WHERE age = 30
  AND stuno < 101000
ORDER BY name;

7.4、 尽量让where的过滤条件和排序使用上索引

CREATE INDEX idx_age_stuno_name ON student (age, stuno, name);
EXPLAIN
SELECT SQL_NO_CACHE *
FROM student
WHERE age = 30
  AND stuno < 101000
ORDER BY name;

7.5、小结

8、 filesort算法:双路排序和单路排序

排序的字段若如果不在索引列上,则filesort会有两种算法双路排序单路排序

8.1、双路排序 (慢)

8.2、单路排序 (快)

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

8.3、结论及引申出的问题

8.3.1、由于单路是后出的,总体而言好过双路

8.3.2、单路排序的问题

8.4、优化策略

8.4.1、尝试提高 sort_buffer_size

无论用哪种算法,提高这个参数都会提高效率,要根据系统的能力提高,因为这个参数是每个进程(connection)的1~8M之间调整。MySQL5.7,InnoDB存储引擎默认值是1MB

SHOW VARIABLES LIKE '%sort_buffer_size%';
image.png

8.4.2、尝试提高 max_length_for_sort_data

SHOW VARIABLES LIKE '%max_length_for_sort_data%';
image.png

8.4.3、Order by 时select * 是一个大忌。最好只Query需要的字段

三、GROUP BY

上一篇 下一篇

猜你喜欢

热点阅读