一:概述
- order by 用於 SQL 語句中的排序。
- 以 select city,name,age from t where city='杭州' order by name limit 1000 ; 舉例,來了解下排序的工作原理。
- 為了避免其他因素的影響,我們為 city 字段加上索引。
二:分析排序
- 分析
- 使用 explain 命令來看看這個語句的執行情況。
-
- 可以看到,在 Extra 這個字段中的“Using filesort”表示的就是需要排序。
- 在排序時候,MySQL 會給每個線程分配一塊內存用於排序,稱為 sort_buffer。
二:全字段排序(排序字段未使用索引)
- 什么時候使用全字段排序?
- 字段較少,數據量較小,排序可在內存中完成,Mysql 的大部分不走索引的排序都是使用 全字段排序完成的。
- 全字段索引排序流程
- 初始化 sort_buffer,確定放入 name、city、age 這三個字段。
- 從索引 city 找到第一個滿足 city='杭州’條件的主鍵 id。
- 到主鍵 id 索引取出整行,取 name、city、age 三個字段的值,存入 sort_buffer 中;
- 從索引 city 取下一個記錄的主鍵 id;
- 重復步驟 3、4 直到 city 的值不滿足查詢條件為止。
- 對 sort_buffer 中的數據按照字段 name 做快速排序;
- 按照排序結果取前 1000 行返回給客戶端。
- 流程細節
- 整個的排序動作,可能在內存中完成,也可能需要使用外部排序,這取決於排序所需的內存和參數 sort_buffer_size。
- sort_buffer_size,就是 MySQL 為排序開辟的內存(sort_buffer)的大小。
- 如果要排序的數據量小於 sort_buffer_size,排序就在內存中完成。
- 但如果排序數據量太大,內存放不下,則不得不利用磁盤臨時文件輔助排序。外部排序一般使用歸並排序算法。
三: rowid 排序(排序字段未使用索引)
- 什么時候使用 rowid 排序?
- 在 全字段排序 中,只對原表的數據讀了一遍,剩下的操作都是在 sort_buffer 和臨時文件中執行的。
- 但是存在一個問題,如果查詢要返回的字段很多,sort_buffer 放的字段數太多,這樣內存里能夠同時放下的行數很少,要分成很多個臨時文件,排序的性能會很差。
- Mysql 認為 全字段排序代價太大,於是使用 rowid 算法排序。
- rowid 排序流程
- 初始化 sort_buffer,確定放入兩個字段,即 name 和 id。
- 從索引 city 找到第一個滿足 city='杭州’條件的主鍵 id。
- 到主鍵 id 索引取出整行,取 name、id 這兩個字段,存入 sort_buffer 中。
- 從索引 city 取下一個記錄的主鍵 id。
- 重復步驟 3、4 直到不滿足 city='杭州’條件為止。
- 對 sort_buffer 中的數據按照字段 name 進行排序。
- 遍歷排序結果,取前 1000 行,並按照 id 的值回到原表中取出 city、name 和 age 三個字段返回給客戶端。
- 流程細節
- 對比 全字段排序流程你會發現,rowid 排序多訪問了一次表 的主鍵索引。
四: 全字段排序 對比 rowid 排序?
- 如果 MySQL 實在是擔心排序內存太小,會影響排序效率,才會采用 rowid 排序算法,這樣排序過程中一次可以排序更多行,但是需要再回到原表去取數據。
- 對於 InnoDB 表來說,rowid 排序會要求回表多造成磁盤讀,因此不會被優先選擇。
五:索引排序(排序字段使用索引)
- 新建立排序字段索引
- 還是上面的 SQL 查詢, 這里建立 city,name 的聯合索引。
- 再看索引排序流程
- 從索引 (city,name) 找到第一個滿足 city='杭州’條件的主鍵 id。
- 到主鍵 id 索引取出整行,取 name、city、age 三個字段的值,作為結果集的一部分直接返回;
- 從索引 (city,name) 取下一個記錄主鍵 id;
- 重復步驟 2、3,直到查到第 1000 條記錄,或者是不滿足 city='杭州’條件時循環結束。
六:排序字段加索引的優點
- 在排序字段有索引的情況下,查詢過程不需要臨時表,也不需要排序。
- 同時,也不會掃描全部符合條件的行數,而是找到適合條件既會返回數據。
七:其他在排序中中需要注意的。
- 無條件查詢如果只有order by create_time,即便create_time上有索引,也不會使用到。
- 因為優化器認為走二級索引再去回表成本比全表掃描排序更高。所以選擇走全表掃描,然后根據老師講的兩種方式選擇一種來排序
- 無條件查詢但是是order by create_time limit m.如果m值較小,是可以走索引的.
- 因為優化器認為根據索引有序性去回表查數據,然后得到m條數據,就可以終止循環,那么成本比全表掃描小,則選擇走二級索引。
- 即便沒有二級索引,mysql針對order by limit也做了優化,采用堆排序。