MySQL的“order by”是如何對200G文件進行排序的?

前言

學習編程語言的時候,我們第一個行代碼往往是“Hello World”。我們學會的第一個真正意義上的算法,我想就是冒泡排序吧。我們創建一個數組,通過N次比較,每次找出最大或最小的元素,把它放到隊列最末尾就完成將無序數組轉化成爲了有序數組,這些排序都是再內存中進行的。

我們開發系統的時候,經常會有按照某種條件就行排序的需求。你有沒有這樣的疑問:我們的數據都是存放硬盤上的,如果數據有200G,但是我的內存只有16G,它是怎麼排序的?

今天我們帶着這個疑問來看一看MySQL中“order by”是如何實現的。

全字段排序

首先我們創建一張表,裏面插入4條元素

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) CHARACTER SET utf8 DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `city` varchar(64) CHARACTER SET utf8 DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

INSERT INTO `user` VALUES (1, '張三', 18, '成都');
INSERT INTO `user` VALUES (2, '李四', 30, '西安');
INSERT INTO `user` VALUES (3, '王五', 27, '杭州');
INSERT INTO `user` VALUES (4, '趙六', 24, '深圳');

如果我們要按照年齡對用戶表進行降序排列只需要執行這條語句

SELECT id, `name`, age from `user` order by age DESC;

通過這條語句我們和容易的就得到了結果,我們查看一下它的執行計劃

Extra這個列中“Using filesort”表示需要排序,MySQL會給每一個線程分配一塊內存用於排序,這塊內存成爲sort_buffer,具體怎麼執行的我們看下圖。

1:初始化sort_buffer,確定需要字段爲name,age

2:根據條件獲取User表中的數據,存入sort_buffer中

3:對sort_buffer中的數據按照字段age進行快速排序

4:將排序結果的結果集返回給客戶端

我們將這個動作暫時稱爲“全字段排序”,其中第3步“按照age進行快速排序”這個動作是再內存中完成的。但是如果需要排序的數量過大,內存放不下,則這時候就不得不利用磁盤臨時文件輔助排序。

sort_buffer_size這個參數就是sort_buffer的大小,如果超出這個值就需要藉助文件輔助了。這時候MySQL會將需要排序的數據分成12份,沒一個單獨排序後存放在這些臨時文件中。然後再把這12個有序文件合併成一個有序大文件。

rowid排序

上面算法的執行過程中把所有的數據讀取出來,如果可以在內存中排序就使用sort_buffer,如果sort_buffer放不下就藉助臨時文件。但是又有一個問題,我們需要排序的行數不是特別多,但是要返回的數據行數特別多,這樣的話sort_buffer裏面要放的字段很多,很快就超出最大內存容量,這時候就退化成臨時文件排序性能就會降低很多。

如果單行太大,臨時文件排序效率肯定不夠好。MySQL中有一個參數max_length_for_sort_data是專門控制排序的行的長度的一個參數。它的意思是說,如果單行的長度超過了這個值,MySQL就認爲單行太大,這時候就會換一個算法。

我們name、age定義的長度是28,我們設置max_length_for_sort_data爲16,這時候我們的算法就會變成另一種。

 

1:初始化sort_buffer,確定需要字段爲id,name

2:根據條件獲取User表中的數據,存入sort_buffer中

3:對sort_buffer中的數據按照字段age進行快速排序

4:根據sort_buffer中的id回表取出name、age構造結果集返回

這個算法和之前相比多了一步回表,而“結果集”也是一個邏輯概念。MySQL服務端從排序後的sort_buffer中依次取出id,然後到原表查詢就可以,不再需要服務端再耗費內存,而是直接返回給客戶端。

天然有序

如果我們需要返回的數據本身就是有序的話,那麼就不需要order by排序了。我們可以再user表上增加age、name的鏈接索引,

alter table user add index age_name(age, name);

 

這時候我們查看一下執行計劃就會發現extra字段中沒有Using filesort就是說明不需要排序,而Using index就說明使用了覆蓋索引。覆蓋索引是指,索引上的信息已經可以滿足查詢,不需要再回到主鍵索引上去取數據。

前面基於age/name創建的聯合索引它本身有序,所以需不要再把數據讀入sort_buffer排序再返回,而是直接返回就可以了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章