优化一:小表驱动大表,意思就是小的数据集驱动大的数据集。
(1)当B表的数据集是小于A表的数据集时,用 IN 优于 EXISTS 。
SELECT * FROM A WHERE id IN(SELECT id FROM B)
-- 等价于
FOR SELECT id FROM B
FOR SELECT * FROM A WHERE A.id=B.id
(2)当A表的数据集是小于B表的数据集时,用 EXISTS 优于 IN 。
SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE B.id=A.id)
-- 等价于
FOR SELECT * FROM A
FOR SELECT * FROM B WHERE B.id=A.id
1的位置也可以是‘A’、‘B’、'C'、5、6、7......只要是一个常量就行了
注意:A表和B表的id字段应建立索引。
- EXISTS
SELECT ... FROM TABLE WHERE EXISTS (SUBQUERY)
后面接一个子查询,所以该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE 或者FALSE)来决定主查询的数据结果是否得以保留。
- 提示
1、EXISTS(SUBQUERY) 只返回TRUE 或者 FALSE ,因此子查询中的 SELECT * 也可以是 SELECT 1 或者其他,官方说法是在实际执行时会忽略 SELECT 清单,因此没有区别。
2、EXISTS 子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担忧效率问题,可以进行实际检验以确定是否有效率问题。
3、EXISTS 子查询往往也可以用条件表达式、其他子查询或者 JOIN 来替代,何种最优需要具体问题具体分析。
优化二:ORDER BY 关键字优化
(1)ORDER BY 子句,尽量使用 INDEX 方式排序,避免使用FileSort方式排序
根据下图几种情况可知,为了避免出现 FileSort 的状态,ORDER BY 后面的查询顺序应该跟索引的顺序一致,其次索引开头的字段是千万不能少的。
还有一个问题要注意 ORDER BY 的顺序默认的是升序的,如果就算 ORDER BY 后面的排序字段跟索引里的字段顺序一样,但是把升序,改成降序,那也会引起 FileSort 的状态出现
(2)尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀法则
ORDER BY 在使用时满足两种情况就会使用 INDEX 方式排序:
1、ORDER BY 语句使用索引最左前列;
2、使用 WHERE 子句与 ORDER BY 子句条件列组合满足索引最左前列
(3)如果不在索引列上,FileSort有两种算法:MySQL就要启动双路排序和单撸排序
1、双路排序:
MySQL 4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和 ORDER BY 列,对他们进行排序,然后扫描已经排好序的列表,按照列表中的值重新从列表中读取对应的数据输出。
从磁盘取排序字段,在 buffer 进行排序,再从磁盘去其他字段。
2、取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法就是单路排序:
从磁盘读取查询需要的所有列,按照 ORDER BY 列在 buffer 对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是他会使用更多的空间,因为他把每一行都保存在内存中了。
3、结论以及引申出的问题:
由于单路是后出的,总体而言好过双路,但是在使用单路的时候还是有问题的。
在sort_buffer中,方法B比方法A要占用很多空间,因为方法B是把所有字段都取出,所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取sort_buffer容量大小,再排......从而多次I/O。
这样想省一次I/O操作,反而导致了大量的I/O操作,得不偿失。
- 优化策略
增大sort_buffer_size参数的设置;
增大max_length_for_sort_data参数的设置;
- 提高 ORDER BY 的速度
1、ORDER BY 时 SELECT * 是一个大忌,只需要 Query 需要的字段就行了。不然会有以下影响:
1.1、当 QUERY 的字段大小总和小于max_length_for_sort_data而且排序字段不是 TEXT 或者 BLOB 类型时,会用改进后的算法——单路排序,否则用老算法——多路排序。
1.2、两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次I/O,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size。
2、尝试提高sort_buffer_size
不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。
3、尝试提高max_length_for_sort_data
提高这个参数,会增加用改进算的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是提高的磁盘I/O活动和低的处理器使用率。
- 小总结
为排序使用索引
MySQL两种排序方式:文件排序或扫描有序索引排序
MySQL能为排序与查询使用相同的索引
比如说设置索引 key 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 能使用索引,不产生FileSort
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
-- 丢失a索引
WHERE g = const ORDER BY b,c
-- 丢失b索引
WHERE a = const ORDER BY c
-- d不是索引的一部分
WHERE a = const ORDER BY a,d
-- 对于排序来说,多个相等条件也是范围查询
WHERE a in(...) ORDER BY b,c
优化三:GROUP BY 关键字优化
GROUP BY 实质是先排序后进行分组,遵照索引建的最佳左前缀。
当无法使用索引列,增大max_length_for_sort_data参数的设置 + 增大sort_buffer_size参数的设置。
WHERE高于HAVING,能写在WHERE限定的条件就不要去HAVING限定了。