文章目录
优化原则:小表驱动大表,即小的数据集驱动大的数据集
原理
select * from A where id in (select id from B)等价于
for select id from B {
for select * from A where A.id=B.id
}
当B表的数据集必须小于A表的数据集时,用in优于exists
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
当A表的数据集小于B表的数据集时,用exists优于in
order by关键字优化
order by字句尽量使用index方式排序,避免使用fileSort方式排序。
MySQL支持两种方式的排序,fileSort和index,index效率高。它指MySQL扫描索引本身完成排序。fileSort方法效率较低。
order by满足如下两种情况,会使用index方式排序
- order by 语句使用索引最左前列
- 使用where字句与order by子句条件列组合满足索引最左前列
尽可能在索引列上完成排序操作,遵照索引的最佳左前缀
fileSort排序原理
如果不在索引列上排序,fileSort有两种算法:双路排序和单路排序。
双路排序:MySQL4.1之前是使用双路排序,字面意思是两次扫描磁盘,最终得到数据。读取行指针和order by列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据传输。
简单的说就是从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
单路排序:从磁盘读取查询需要的所有列,按照order by在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据,并且把随机IO变成顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。
单路排序效率是比双路排序好,但是也有问题:
在sort_buffer中,单路排序比双路排序要多占用很多空间,因为单路排序是把所有字段都取出,所以有可能取出的数据总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取再排,从而会产生多次IO。
提高order by速度的方式
order by时select * 是一个大忌,只查询需要的字段,这点非常重要。在这里影响的是
-
当查询的字段大小之和小于max_length_for_sort_data而且排序字段不是TEXT/BLOB类型时,会使用改进后的单路排序算法,否则使用多路排序算法
-
两种算法的数据都有可能超过sort_buffer的容量,超过之后会创建tmp文件进行合并排序,导致多次IO。所以要提高sort_buffer_size。
-
尝试提高sort_buffer_size
不管哪种算法,提高这个参数都会提高效率。当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。
order by 案例总结
使用索引进行排序的例子
加入一个表索引为 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能使用索引
- where a=const order by b,c
- where a=const and b=const order by c
- where a=const and b>const order by b
不能使用索引进行排序
- order by a asc,b desc,c desc 排序不一致
- where d=const order by b,c 不符合最左前缀规则
- where a=const order by c 丢失b索引
- where a=const order by a,d d不是索引的一部分
- where a in (…) order by b,c
总结
因此 也就是说order by排序列要符合最左前缀规则或者order by排序列和where条件列 能够构成最左前缀规则也行。
group by关键字优化
group by实质是先排序后进行分组,遵照索引键的最佳左前缀,因此跟order by的优化很类似