MySQL高級知識(九)——order by優化

MySQL高級知識(九)——order by優化

此博客的內容主要來源於尚硅谷的視頻中,在此記錄,以備以後自己查看。

準備

  1. 創建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;

在這裏插入圖片描述

  1. 創建索引
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。

總結

  1. MySQL支持兩種方式的排序filesort和index,Using index是指MySQL掃描索引本身完成排序。index效率高,filesort效率低。

  2. 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 能使用索引最左前綴
    1. order by a
    2. order by a,b
    3. order by a,b,c
    4. order by a desc,b desc, c desc
  • 如果where使用索引的最左前綴定義常量,則order by能使用索引
    1. where a = const order by b,c
    2. where a = const and b = const order by c
    3. where a = const and b> const order by b,c
  • 不能使用索引進行排序
    1. order by a asc, b desc, c desc #排序不一致
    2. where g = const order by b,c #丟失a索引
    3. where a = const order by c #丟失b索引
    4. where a = const order by a,d #d不是索引的一部分
    5. where a in (…) order by b,c #對於排序來說,多個相等條件也是範圍查詢

如果不在索引列上做排序,則filesort有兩種算法:雙路排序單路排序

  • 雙路排序:
    1. 在MySQL4.1之前使用雙路排序,就是兩次磁盤掃描,得到最終數據。讀取行指針和order by列,對他們進行排序,然後掃描已經排好序的列表,按照列表中的值重新從列表中讀取對應的數據輸出。即從磁盤中讀取排序字段,在buffer進行排序,再從磁盤中取其他字段。
    2. 如果使用雙路排序,取一批數據要對磁盤進行兩次掃描,衆所周知,I/O操作是很耗時的,因此在MySQL4.1以後,出現了改進的算法:單路排序
  • 單路排序:
    1. 從磁盤中查詢所需要的列,按照order by列在buffer中對他們進行排序,然後掃描排序後的列表進行輸出。他的效率更高一點,避免了第二次讀取數據,並且把隨機I/O變成了順序I/O,但是會使用更多的空間,因爲它把每一行都保存在了內存中。
    2. 當讀取數據超過sort_bufffer的容量時,就會導致多次讀取數據,並創建臨時表,最後多路合併,產生多次I/O,反而增加其I/O運算。
      解決方式:
      1. 增加sort_buffer_size參數的設置——用於單路排序的內存大小
      2. 增大max_length_for_sort_data參數的設置——單單次排序字段大小
      3. 去掉select後面不需要的字段——select 後的多了,排序的時候也會帶着一起,很佔內存,所以去掉沒有用的
  1. 提升order by速度的方式:
    1. 在使用order by時候,不要用select * ,只查詢所需要的字段。
    2. 嘗試提高sort_buffer_size。
    3. 嘗試提高max_length_for_sort_data。
  2. group by與order by很類似,其實質是先排序後分組,遵照索引創建順序的最佳左前綴法則。當無法使用索引列的時候,也要對sort_buffer_size和max_length_for_sort_data參數進行調整。注意where高於having,能寫在where中的限定條件就不要去having限定了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章