mysql關(guān)于排序底層原理解析
前言
本章詳細講下排序,排序在我們業(yè)務開發(fā)非常常見,有對時間進行排序,又對城市進行排序的。
不合適的排序,將對系統(tǒng)是災難性的,這個不是危言聳聽。
可能有些人會想,對于排序mysql 是怎么實現(xiàn)的,它的底層原理是怎么樣的,如果我加上分頁,排序是不是就會快一些。
關(guān)于這些問題,本章詳細講解。
有人經(jīng)常問我,mysql 優(yōu)化的規(guī)則,總是不假思索的說ESR,E 是 equal ,S是sort 。
可見排序有多么重要,為了講解方便,我先畫個思維導圖。

上圖標的1,2 是mysql 配置文件可以配置的。
可以通過 show variables like 'max_length_for_sort_data'; 可以具體的配置。
從圖上我們可以看到mysql 排序分為全字段排序,和 rowid 。
這是兩大類,里面又分為內(nèi)存排序,文件排序,我將從這2大類4小類講解。
全字段排序

由上圖可以看出 Extra = Using filesort 就表示了排序,但此時還不能判斷是文件排序還是內(nèi)存排序
可以根據(jù)下面介紹的方法,來確定一個排序語句是否使用了臨時文件
/* 打開optimizer_trace,只對本線程有效 */ SET optimizer_trace='enabled=on'; ? /* @a保存Innodb_rows_read的初始值 */ select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Innodb_rows_read'; ? /* 執(zhí)行語句 */ select city, name,age from t where city='杭州' order by name limit 1000; ? /* 查看 OPTIMIZER_TRACE 輸出 */ SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G ? /* @b保存Innodb_rows_read的當前值 */ select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read'; ? /* 計算Innodb_rows_read差值 */ select @b-@a;

Number_of_tmp_files>0 就表示文件排序,沒有就表示是內(nèi)存排序。
sort_buffer_size 越小,那么 Number_of_tmp_files 就會越大,文件排序用的是歸并排序,也就是把數(shù)據(jù)分給多個文件,每個文件排序后,最終合并一個文件。
上面sort_mode 可以看到,這是一個全字段排序,什么是全字段排序,就拿上面這個sql 語句來說,city ,name,age 都在文件里,對name 進行排序
這個排序的內(nèi)部是這么實現(xiàn)的:
- 初始化 sort_buffer,確定放入 name、city、age 這三個字段;
- 從索引 city 找到第一個滿足 city='杭州’ 條件的主鍵 id
- 到主鍵 id 索引取出整行,取 name、city、age 三個字段的值,存入 sort_buffer 中;
- 從索引 city 取下一個滿足 city='杭州’ 的主鍵 id;
- 重復步驟 3、4 直到 city 的值不滿足查詢條件為止
- 對 sort_buffer 中的數(shù)據(jù)按照字段 name 做快速排序;
- 按照排序結(jié)果取前 1000 行返回給客戶端。
由此我們發(fā)現(xiàn),排序會對表的所有的記錄進行排序,然后在取出1000條
rowid
如果 排序數(shù)據(jù)的長度超過了 max_length_for_sort_data 就是 rowid排序。
排序數(shù)據(jù)的長度就是指拿上面這個例子說 name、city、age 這三個字段大于 max_length_for_sort_data 就是rowid 排序。
為什么會這樣的呢,mysql 會盡量用內(nèi)存排序,字段越長,占用空間越大,未了提高排序效率,就會用rowid 排序。
rowid排序的步驟是這樣的:
- 初始化 sort_buffer,確定放入兩個字段,即 name 和 id;
- 從索引 city 找到第一個滿足 city='杭州’條件的主鍵 id
- 到主鍵 id 索引取出整行,取 name、id 這兩個字段,存入 sort_buffer 中;
- 從索引 city 取下一個記錄的主鍵 id;
- 重復步驟 3、4 直到不滿足 city='杭州’條件為止,
- 對 sort_buffer 中的數(shù)據(jù)按照字段 name 進行排序;
- 遍歷排序結(jié)果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三個字段返回給客戶端。
我們可以看到 rowid 會多訪問一次表,在mysql 看來,排序的復雜度高于回表的復雜度,這也是一種取舍。
綜上可以看出不管是內(nèi)存排序還是文件排序,都是很繁瑣的,那么有沒有對于這個問題有沒有優(yōu)化點了,在前面我們已經(jīng)講過了,索引一定是有序的,如果我們對city,name 建一個聯(lián)合索引,就不用mysql 重新排序,因為索引本身就是有序的。
就是如下所示:
alter table t add index city_user(city, name);
但是上面雖然不用mysql 用文件排序,但是還是要回表的,那還有沒有進一步的優(yōu)化呢,我們可以考慮用覆蓋索引
如下所示:
alter table t add index city_user_age(city, name, age);
這樣就不用回表了,用explain 來看 Extra using index
大家要綜合考慮吧,索引越多,索引越大,會影響插入的速度的。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺析mysql 語句的調(diào)度優(yōu)先級及改變
本篇文章是對mysql語句的調(diào)度優(yōu)先級及改變進行了詳細的分析介紹,需要的朋友參考下2013-06-06
解決出現(xiàn)secure_file_priv null的問題
這篇文章主要介紹了解決出現(xiàn)secure_file_priv null的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
數(shù)據(jù)庫SQL腳本文件導入到mysql數(shù)據(jù)庫的兩種方式
MySQL作為一種關(guān)系型數(shù)據(jù)庫管理系統(tǒng),它是在Web服務器中廣泛使用的,它把數(shù)據(jù)存儲在表中,這篇文章主要介紹了數(shù)據(jù)庫SQL腳本文件導入到mysql數(shù)據(jù)庫的兩種方式,需要的朋友可以參考下2025-04-04
巧用mysql提示符prompt清晰管理數(shù)據(jù)庫的方法
隨著管理mysql服務器越來越多,同樣的mysql>的提示符有可能會讓你輸入錯誤的命令到錯誤的數(shù)據(jù)庫,這時候需要巧用mysql的提示符,這是我的提示符root@localhost(mysql) 08:55:21> 用prompt命令實現(xiàn)(適用于windows和linux環(huán)境)2009-08-08
mysql基礎架構(gòu)教程之查詢語句執(zhí)行的流程詳解
這篇文章主要給大家介紹了關(guān)于mysql基礎架構(gòu)教程之查詢語句執(zhí)行流程的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧2018-11-11
最新Navicat?15?for?MySQL破解+教程?正確破解步驟
Navicat?for?MySQL是一個針對MySQL數(shù)據(jù)庫而開發(fā)的第三方mysql管理工具,該軟件可以用于?MySQL?數(shù)據(jù)庫服務器版本?3.21?或以上的和?MariaDB?5.1?或以上,這篇文章主要介紹了最新Navicat?15?for?MySQL破解+教程?正確破解步驟,需要的朋友可以參考下2023-04-04

