【MySQL 讀書筆記】「order by」是怎麼工做的?

針對排序來講,order by 是咱們使用很是頻繁的關鍵字。結合以前咱們對索引的瞭解再來看這篇文章會讓咱們深入理解在排序的時候,是如何利用索引來達到少掃描表或者使用外部排序的。優化

先定義一個表輔助咱們後面理解spa

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `city` varchar(16) NOT NULL,
  `name` varchar(16) NOT NULL,
  `age` int(11) NOT NULL,
  `addr` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `city` (`city`)
) ENGINE=InnoDB;

這時咱們寫一條查詢語句線程

select city,name,age from t where city='杭州' order by name limit 1000  ;

根據上面的表定義來看,city=xxx 可使用到咱們定義的一個索引。可是 order by name 明顯咱們沒有索引,因此確定須要先用索引查詢到 city=xxx 而後再進行回表查詢,最後再排序。code

 

全字段排序blog

在 city 字段上面建立索引以後,咱們使用執行計劃來查看這個語句 排序

能夠看到有索引的狀況下 咱們這裏仍是使用了 "Using filesort" 表示須要排序,MySQL 會給每一個線程分配一塊內存用於排序 稱爲 sort_buffer。索引

咱們在執行上面 select 語句的時候一般經歷了這樣一個過程內存

1. 初始化 sort_buffer, 確認放入 name, city, age 這三個字段。ci

2. 從索引 city 找到第一個知足 city='杭州'條件的主鍵 id。it

3. 回表取到 name, city, age 三個字段值,存入 sort_buffer 中。

4. 從索引 city 取下一個主鍵 id 記錄。

5. 重複 3-4 步驟,直到 city 不知足條件。

6. 對 sort_buffer 中的數據按照字段 name 作快速排序。

7. 排序結果取前 1000 行返回給客戶端。

這被咱們稱爲全字段排序。

按照 name 排序這個動做便可能在內存中完成,也能夠能使用外部文件排序。這取決於 sort_buffer_size 。sort_buffer_size 的默認值是 1048576 byte 也就是 1M,若是要排序的數據量小於 1m 排序就在內存中完成,若是排序數據量大,內存放不下,則使用磁盤臨時文件輔助排序。

 

Rowid 排序

若是單行很大,須要的字段所有放進 sort_buffer 效果就不會很好。

MySQL 中專門用於控制排序的行數據長度有個參數 max_length_for_sort_data 默認是1024,若是超過了這個值就會使用 rowid 排序。那麼執行上面語句的流程就變成了

1. 初始化 sort_buffe 肯定放入兩個字段即 name 和 id 。

2. 從索引 city 找到第一個知足 city = '杭州'條件的主鍵 id。

3. 回表取 name 和 id 兩個字段 存入 sort_buffer 中。

4. 取下個知足條件的記錄 重複 2 3 步驟。

5. 對 sort_buffer 中的 name 進行排序。

6.遍歷結果取前 1000 行。而後按照 id 再回一次表取的結果字段返回給客戶端。

其實並非全部 oder by 語句都須要進行上面的二次排序操做。從上面分析的執行過程,咱們能夠注意到。MySQL 之因此須要生成臨時表,是由於要在臨時表上作排序,是由於以前咱們取得的是數據是無序的。

若是咱們對剛纔的索引修改一下,使得他是一個聯合索引,那麼第二個字段咱們拿到的值其實就是有序的了。

聯合索引知足這麼一個條件,當咱們的第一個索引字段是相等的狀況下,第二個字段是有序的。

這能保證若是咱們創建 (city,name) 索引的話,當咱們在搜索 city='杭州'的狀況的是時候找到的目標第二個字段 name 實際上是有序的。因此查詢過程能夠簡化成。

1. 從索引 (city, name) 找到第一個知足 city = '杭州'條件的主鍵 id 。

2. 回表取到 name city age 三個值返回。

3. 取下一個 id 。

4. 重複2 3 兩個步驟直到 1000 條記錄,或者是不知足 city = '杭州'條件結束。

也由於查詢過程均可以使用到索引的有序性,因此再也不須要排序也不須要時使用 sort buffer 了。

更近一步的優化就是以前說過的索引覆蓋,將須要查詢的字段也覆蓋進索引中,再省掉回表的步驟,可讓整個查詢的速度更快。

 

 

Reference:

本讀書筆記皆來自發布在極客時間的 林曉斌(丁奇)的 MySQL 實戰45講:

極客時間版權全部: https://time.geekbang.org/ 版權全部: 

https://time.geekbang.org/column/article/73479

相關文章
相關標籤/搜索