using filesort
一般人的回答是: “當行數據太大,導致內存無法容下這些數據產生的臨時表時,他們就會被放入磁盤中排序。” 很不幸,這個答案是錯的 ,臨時表在太大的時候確實會到磁盤離去,但是EXPLAIN不會顯示這些。
The truth is, filesort is badly named. Anytime a sort can’t be performed from an index, it’s a filesort. It has nothing to do with files. Filesort should be called “sort.” It is quicksort at heart.
那么事實是, filesort 這個名字取得太搓逼了。 filesort的意思是只要一個排序無法使用索引來排序,就叫filesort。他和file沒半毛錢關系。filesort應該叫做sort。(筆者補充一下:意思是說如果無法用已有index來排序,那么就需要數據庫服務器額外的進行數據排序,這樣其實是會增加性能開銷的。)
另外不光order by會出現filesort group by沒有使用索引一樣會出現
檢查sort狀態
show session status like '%sort%';
sort_merge_passes 由於sort buffer不夠大,不得不將需要排序的數據進行分段,然后再通過sort merge的算法完成整個過程的merge總次數,一般整個參數用來參考sort buffer size 是否足夠。
sort range session/global級別(單位:次) 通過range scan完成的排序總次數。
sort rows session/global 級別(單位:row) 排序的總行數。
sort scan 通過掃描表完成的排序總次數。
當無法避免排序操作時,又該如何來優化呢?很顯然,應該盡可能讓 MySQL 選擇使用第二種單路算法來進行排序。這樣可以減少大量的隨機IO操作,很大幅度地提高排序工作的效率。
1. 合理設置索引 讓排序字段使用上索引排序
2. 加大 max_length_for_sort_data 參數的設置
3. 去掉不必要的返回字段
當內存不是很充裕時,不能簡單地通過強行加大上面的參數來強迫 MySQL 去使用改進版的排序算法,否則可能會造成 MySQL 不得不將數據分成很多段,然后進行排序,這樣可能會得不償失。此時就須要去掉不必要的返回字段,讓返回結果長度適應 max_length_for_sort_data 參數的限制。
4. 增大 sort_buffer_size 參數設置
增大 sort_buffer_size 並不是為了讓 MySQL選擇改進版的排序算法,而是為了讓MySQL盡量減少在排序過程中對須要排序的數據進行分段,因為分段會造成 MySQL 不得不使用臨時表來進行交換排序。
Using temporary
內部臨時表產生的時機有以下幾種:
- 使用 ORDER BY 子句和一個不一樣的 GROUP BY 子句(經過筆者實驗,應該是GROUP BY一個無索引列,就會產生臨時表),或者 ORDER BY 或 GROUP BY 的列不是來自JOIN語句序列的第一個表,就會產生臨時表(經筆者實驗,應該是使用JOIN時, GROUP BY 任何列都會產生臨時表)
- DISTINCT 和 ORDER BY 一起使用時可能需要臨時表(筆者實驗是只要用了DISTINCT(非索引列),都會產生臨時表)
用了 SQL_SMALL_RESULT, mysql就會用內存臨時表。定義:SQL_BIG_RESULT
orSQL_SMALL_RESULT
can be used withGROUP BY
orDISTINCT
to tell the optimizer that the result set has many rows or is small, respectively. ForSQL_BIG_RESULT
, MySQL
有些情況服務器會直接使用磁盤臨時表
- 表里存在BLOB或者TEXT的時候(這是因為MEMORY引擎不支持這兩種數據類型,這里筆者補充一下,並非只要查詢里含有BLOB和TEXT類型的列就會產生磁盤臨時表,按照高性能MYSQL里的話,應該這么說:“Because the Memory storage engine doesn't support the BLOB and TEXT types, queries that use BLOB or TEXT columns and need an implicit temporary table will have to use on-disk MyISAM temporry tables, even for only a few rows.”也就是說如果我們的查詢中包含了BLOB和TEXT的列,而且又需要臨時表,這時候臨時表就被強制轉成使用磁盤臨時表,所以此書一直在提醒我們,如果要對BLOB和TEXT排序,應該使用SUBSTRING(column, length)將這些列截斷變成字符串,這樣就可以使用in-memory臨時表了
- GROUP BY 或者 DISTINCT 子句大小超過 512 Bytes
- 使用了UNION 或 UNION ALL 並且 SELECT 的列里有超過512 Bytes的列
如果內置內存臨時表創建后變得太大,MySQL會自動將它轉換成磁盤臨時表。內存臨時表的大小取決與 tmp_table_size參數和max_heap_table_size參數的值。用 CREATE TABLE 產生的內存臨時表的大小取決與 max_heap_table_size來決定是否要將其轉換成磁盤臨時表
當服務器生成一個內存臨時表,Created_tmp_tables狀態變量值會增加,當服務器創建了一個磁盤臨時表時,Created_tmp_disk_tables狀態變量值會增加。(這幾個變量可以通過 show status命令查看得到)
Tips: internal temporaray table 的大小受限制的是tmp_table_size和max_heap_table_size的最小值;而 user-created temporary table的大小只受限與max_heap_table_size,而與tmp_table_size無關