經過explain
查看sql的執行計劃時,Extra
字段的值每每會看到Using where; Using index; Using temporary; Using filesort
,其中這次重點關注Using temporary; Using filesort
。html
Using temporary
表示因爲排序沒有走索引、使用union
、子查詢鏈接查詢、使用某些視圖等緣由(詳見internal-temporary-tables),所以建立了一個內部臨時表。注意這裏的臨時表多是內存上的臨時表,也有多是硬盤上的臨時表,理所固然基於內存的臨時表的時間消耗確定要比基於硬盤的臨時表的實際消耗小。mysql
查看sql執行時使用的是內存臨時表仍是硬盤臨時表,須要使用以下命令:算法
mysql> show global status like '%tmp%';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0 |
| Created_tmp_files | 5 |
| Created_tmp_tables | 11 |
+-------------------------+-------+
3 rows in set
複製代碼
Created_tmp_tables表示mysql建立的內部臨時表的總數(包括內存臨時表和硬盤臨時表);Created_tmp_disk_tables表示mysql建立的硬盤臨時表的總數。sql
當mysql須要建立臨時表時,選擇內存臨時表仍是硬盤臨時表取決於參數tmp_table_size和max_heap_table_size,內存臨時表的最大容量爲tmp_table_size
和max_heap_table_size
值的最小值,當所需臨時表的容量大於二者的最小值時,mysql就會使用硬盤臨時表存放數據。bash
用戶能夠在mysql的配置文件裏修改該兩個參數的值,二者的默認值均爲16M。ui
tmp_table_size = 16M
max_heap_table_size = 16M
複製代碼
查看tmp_table_size
和max_heap_table_size
值:spa
mysql> show global variables like 'max_heap_table_size';
+---------------------+----------+
| Variable_name | Value |
+---------------------+----------+
| max_heap_table_size | 16777216 |
+---------------------+----------+
1 row in set
mysql> show global variables like 'tmp_table_size';
+----------------+----------+
| Variable_name | Value |
+----------------+----------+
| tmp_table_size | 16777216 |
+----------------+----------+
1 row in set
複製代碼
若是問Using filesort
是什麼意思,大多數人應該會回答「基於硬盤的排序」或者「數據太多不適合內存,因此在硬盤上排序」。然而這些解釋是錯誤的。code
Using filesort
僅僅表示沒有使用索引的排序,事實上filesort
這個名字很糟糕,並不意味着在硬盤上排序,filesort
與文件無關。所以消除Using filesort
的方法就是讓查詢sql的排序走索引。server
filesort
使用的算法是QuickSort
,即對須要排序的記錄生成元數據進行分塊排序,而後再使用mergesort方法合併塊。其中filesort
可使用的內存空間大小爲參數sort_buffer_size的值,默認爲2M。當排序記錄太多sort_buffer_size
不夠用時,mysql會使用臨時文件來存放各個分塊,而後各個分塊排序後再屢次合併分塊最終全局完成排序。htm
mysql> show global variables like 'sort_buffer_size';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| sort_buffer_size | 262144 |
+------------------+--------+
1 row in set
複製代碼
Sort_merge_passes表示filesort
執行過的文件分塊合併次數的總和,若是該值比較大,建議增大sort_buffer_size
的值。
mysql> show global status like '%sort%';
+-------------------+---------+
| Variable_name | Value |
+-------------------+---------+
| Sort_merge_passes | 226 |
| Sort_range | 0 |
| Sort_rows | 1384911 |
| Sort_scan | 6 |
+-------------------+---------+
4 rows in set
複製代碼
filesort
使用的排序方法有兩種:
第一種方法是對須要排序的記錄生成<sort_key,rowid>
的元數據進行排序,該元數據僅包含排序字段和rowid。排序完成後只有按字段排序的rowid,所以還須要經過rowid進行回表操做獲取所須要的列的值,可能會致使大量的隨機IO讀消耗;
第二種方法是是對須要排序的記錄生成<sort_key, additional_fields>
的元數據,該元數據包含排序字段和須要返回的全部列。排序完後不須要回表,可是元數據要比第一種方法長得多,須要更多的空間用於排序。
參數max_length_for_sort_data字段用於控制filesort
使用的排序方法,當全部須要排序記錄的字段數量總和小於max_length_for_sort_data
時使用第二種算法,不然會用第一種算法。該值的默認值爲1024。
mysql> show global variables like 'max_length_for_sort_data';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| max_length_for_sort_data | 1024 |
+--------------------------+-------+
1 row in set
複製代碼
mysql> set global max_length_for_sort_data = 1024;
複製代碼