MySQL5.6中limit的工作機制和order by limit優化原理
MySQL5.6中Limit的工作機制
如果你僅需要在一個結果集中返回特定的幾行,通常是使用limit,而不是取回整個結果集再捨去不需要的資料,MySQL通常按照如下的方式優化一個包含limit row_count或HAVING的語句:
◎只有limit
如果你只通過limit返回少量的行,那麼正常情況下mysql會使用全盤掃描,有些場合會使用索引,以下是使用了覆蓋索引的情況:
以下是使用全表掃描的情況:
◎order by和limit
如果你order by和limit一起使用,那麼mysql在排序結果中找到最初的row_count行之後就會完成這條語句,而不是對整個結果集進行排序。如果使用了索引排序,它就非常快地完成。如果整個filesort必須都做完的話,那麼在找到最初的row_count行之前,匹配該查詢的所有行都將被select,並且做sort操作。如果這些行找到了,mysql將不會對剩餘的結果集進行排序。
◎distinct和limit
當limit row_count和distinct一起使用時,MySQL在找到最初的unique的row_count行之後就會停止檢索。
◎group by和limit
在某些場合下,group by會用於某些key行的排序,並且計算彙總資訊,這時如果使用limit row_count的話將不會計算任何額外的grup by值。
◎SQL_CALC_FOUND_ROWS和limit
只要MySQL已經返回了需要的行數給客戶端,它將終止這個查詢,除非你在查詢中使用了SQL_CALC_FOUND_ROWS。
◎limit 0的用法
Limit 0會非常快地返回一個空結果,這個功能可被應用於檢測一條SQL的合法性。
◎臨時表和limit
如果伺服器在查詢中使用了臨時表,它會使用limit row_count語句來計算需求的空間大小。
Order by和Limit混合使用引起的問題
如果在order by語句中返回的結果集有很多行,那麼非排序的列的返回結果是不確定的,即隨機的,所以如果配合limit的話每次返回的結果集的順序是不固定的,比如下面這個例子
mysql> SELECT * FROM ratings ORDER BY category;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+
使用了limit以後,可發現id列和rating列和之前的結果集順序有出入:
mysql> SELECT * FROM ratings ORDER BY category LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 4 | 2 | 3.5 |
| 3 | 2 | 3.7 |
| 6 | 2 | 3.5 |
+----+----------+--------+
如果你有必要保證每次有相同的結果集,則需要order by你需要的那幾列了:
mysql> SELECT * FROM ratings ORDER BY category, id;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+
mysql> SELECT * FROM ratings ORDER BY category, id LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
+----+----------+--------+
Order by和limit一起使用的優化原理
從MySQL5.6.2版本以後,優化器將更加智慧地處理下面形式的查詢了
SELECT ... FROM single_table ... ORDER BY non_index_column [DESC] LIMIT [M,]N;
這種在很大的結果集中只返回很少的行數的查詢型別在web應用中非常常見,比如
SELECT col1, ... FROM t1 ... ORDER BY name LIMIT 10;
SELECT col1, ... FROM t1 ... ORDER BY RAND() LIMIT 15;
排序快取有一個引數是sort_buffer_size,如果這個引數大小足夠上面範例中的N行的排序結果集(如果M也被定義,那就是M+N行的結果集大小),那麼伺服器將會避免一個檔案排序操作,使得排序完全在記憶體中完成。
記憶體排序+limit原理
1 掃描表,在記憶體中插入那些被選擇排序的列的資料到一個排好序的佇列中,比如order by col1,col2,則插入col1和col2列的資料。如果佇列滿了,則擠出排序在末尾的資料。
2 返回佇列中的前N行記錄,如果M也被定義,則調到第M行開始返回後續的N行記錄。
檔案排序+limit原理
1掃描表,重複步驟2和3,直到表的結尾
2選中這些行數直到排序快取被填滿
3在排序快取中寫入第一個N行(如果M被定義,則M+N行)到一個排序檔案中。
兩者比較
在記憶體中排序和使用檔案排序相比,掃描表的代價幾乎是一樣的,不同的是其他的開銷:
記憶體排序的方法在插入資料到一個有序佇列中會牽扯到更多的cpu資源,而檔案排序會消耗更多的磁碟IO,優化器在考慮兩者的平衡性上會主要考慮N的值大小