此博客的內容主要來源於尚硅谷的視頻中,在此記錄,以備以後自己查看。
準備
- 創建test表
drop table if exists test;
create table test(
id int primary key auto_increment,
c1 varchar(10),
c2 varchar(10),
c3 varchar(10),
c4 varchar(10),
c5 varchar(10)
) ENGINE=INNODB default CHARSET=utf8;
insert into test(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5');
insert into test(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5');
insert into test(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5');
insert into test(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5');
insert into test(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5');
select * from test;
- 創建索引
create index idx_test_c1234 on test(c1,c2,c3,c4);
show index from test;
案例
- 案例1
explain select * from test where c1>'a1' order by c1;
分析:
-
在c1,c2,c3,c4上創建了索引,直接在c1上使用範圍,導致了索引失效,全表掃描:type=ALL,ref=Null。因爲此時c1主要用於排序,並不是查詢。
-
使用c1進行排序,出現了Using filesort。
-
解決方法:使用覆蓋索引。
- 案例1.1
explain select c1 from test where c1>'a1' order by c1,c2;
分析:
- 排序時按照索引的順序,所以不會出現Using filesort。
- 案例1.2
explain select c1 from test where c1>'a1' order by c2;
分析:
- 出現了Using filesort。原因:排序用的c2,與索引的創建順序不一致,對比Case1.1可知,排序時少了c1(帶頭大哥),因此出現Using filesort。
- 案例1.3
explain select c1 from test where c1>'a1' order by c2,c1;
分析:
- 出現了Using filesort。因爲排序索引列與索引創建的順序相反,從而產生了重排,也就出現了Using filesort。
- 案例2
explain select c1 from test order by c2;
explain select c1 from test where c2>'a2' order by c2;
分析:
- 直接使用c2進行排序,出現Using filesort,因爲不是從最左列索引開始排序的(沒有帶頭大哥)。
- 案例2.1
explain select c1 from test where c2>'a2' order by c1;
分析:
- 排序使用了索引順序(帶頭大哥在),因此不會出現Using filesort。
- 案例2.2
explain select c1 from test order by c1, c2;
explain select c1 from test order by c1 asc, c2 desc;
explain select c1 from test order by c1 desc, c2 desc;
分析:
-
order by默認升序
-
雖然排序的字段列與索引順序一樣,所有的排序字段的升降必須和帶頭大哥一樣,否則會出現Using filesort。
總結
-
MySQL支持兩種方式的排序filesort和index,Using index是指MySQL掃描索引本身完成排序。index效率高,filesort效率低。
-
order by滿足兩種情況會使用Using index。
-
order by語句使用索引最左前列準則
-
儘量在索引列上完成排序,遵循索引建立(索引創建的順序)時的最佳左前綴法則。
-
使用where子句與order by子句條件組合滿足最左前列
-
where子句中如果出現索引的範圍查詢(即explain中出現range)會導致order by 索引失效。
-
爲排序使用索引:
- 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能使用索引
- 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,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 #對於排序來說,多個相等條件也是範圍查詢
如果不在索引列上做排序,則filesort有兩種算法:雙路排序和單路排序
- 雙路排序:
- 在MySQL4.1之前使用雙路排序,就是兩次磁盤掃描,得到最終數據。讀取行指針和order by列,對他們進行排序,然後掃描已經排好序的列表,按照列表中的值重新從列表中讀取對應的數據輸出。即從磁盤中讀取排序字段,在buffer進行排序,再從磁盤中取其他字段。
- 如果使用雙路排序,取一批數據要對磁盤進行兩次掃描,衆所周知,I/O操作是很耗時的,因此在MySQL4.1以後,出現了改進的算法:單路排序
- 單路排序:
- 從磁盤中查詢所需要的列,按照order by列在buffer中對他們進行排序,然後掃描排序後的列表進行輸出。他的效率更高一點,避免了第二次讀取數據,並且把隨機I/O變成了順序I/O,但是會使用更多的空間,因爲它把每一行都保存在了內存中。
- 當讀取數據超過sort_bufffer的容量時,就會導致多次讀取數據,並創建臨時表,最後多路合併,產生多次I/O,反而增加其I/O運算。
解決方式:
- 增加sort_buffer_size參數的設置——用於單路排序的內存大小
- 增大max_length_for_sort_data參數的設置——單單次排序字段大小
- 去掉select後面不需要的字段——select 後的多了,排序的時候也會帶着一起,很佔內存,所以去掉沒有用的
- 提升order by速度的方式:
- 在使用order by時候,不要用select * ,只查詢所需要的字段。
- 嘗試提高sort_buffer_size。
- 嘗試提高max_length_for_sort_data。
- group by與order by很類似,其實質是先排序後分組,遵照索引創建順序的最佳左前綴法則。當無法使用索引列的時候,也要對sort_buffer_size和max_length_for_sort_data參數進行調整。注意where高於having,能寫在where中的限定條件就不要去having限定了。