MySQL資料庫索引orderby排序精講

2022-09-21 00:57:08 字數 3491 閱讀 5428

目錄

排序這個詞,我的第一感覺是幾乎所有app都有排序的地方,**商品有按照購買時間的排序、b站的評論有按照熱度排序的...

對於mysql,一說到排序,你第一時間想到的是什麼?關鍵字order by?order by的字段最好有索引?葉子結點已經是順序的?還是說盡量不要在mysql內部排序?

現在假設有一張使用者的朋友表:

create table `user` (

`id` int(10) auto_increment,

`user_id` int(10),

`friend_addr` varchar(1000),

`friend_name` varchar(100),

primary key (`id`),

key `user_id` (`user_id`)

) engine=innodb;

表中目前有兩個點需要關注下:

有一天,有個初級開發工程師小猿,收到了來自初級產品經理小汪的需求:

小汪:小猿同志,現在需要在後台加個功能,這個功能要支援根據使用者 id 能查到他所有的朋友姓名和位址,並且要求朋友的姓名是按照字典排序的。

小猿:好的,這個功能簡單,我馬上就上線。

於是小猿書寫了這樣的sql:

select friend_name,friend_addr from user where user_id=? order by name

在電光石火的瞬間,小猿趾高氣昂的上線了,這一切都很順利,直到有一天有個運營同學導致了這樣的查詢:

select friend_name,friend_addr from user where user_id=10086 order by name

然而,這個查詢竟然比平時慢很多,資料庫報了慢查詢,小猿此時慌的一b:這是怎麼回事?user_id 明明有索引啊,而且機智地我還只用了 select friend_name,friend_addr,並沒有用 select *呀。小猿此時不停地安慰自己,要淡定要淡定,然後突然想到有個explain命令,用explain來檢視下那條sql的執行計畫吧,當小猿用了explain之後,發現extra欄位裡面有個看起來很危險的字眼:using filesort。

「這個查詢竟然用到了傳說中的檔案排序,但是如果乙個人朋友不是很多,就算了用了檔案排序,應該也很快吧」,除非這個user_id=10086的朋友很多,後來小猿去查了下,這個使用者的朋友竟然有10w多個~。

陷入了沉思的小猿心想:這個鍋看來是背定了,10w資料是有點大了,還有這個 using filesort 到底是怎麼個排序原理?

有人可能說上面的問題是10w資料太大了,就算不排序也慢,這個其實是有道理的,10w資料一次性查出來,無論是mysql記憶體緩衝區的占用,還是網路頻寬的消耗都是非常大的,那如果我加了limit 1000呢?網路頻寬的問題肯定是解決了,因為資料報整體變小了,但是 using filesort 的問題其實還是沒有解決,看到這裡你可能會有疑問,using filesort 難道是在檔案中排序的?在檔案中到底是怎麼排序的?或者我這樣問:如果給你來設計排序你會怎麼處理?帶著這些疑問和思考我們來看看 using filesort 會涉及到哪些技術難點以及是如何解決的?

一切看起來很絲滑,但是 sort_buffer 占用的是記憶體空間,這就尷尬了,記憶體本身就不是無限大的,它肯定是有上限的,當然 sort_buffer 也不能太小,太小的話,意義不大。在 innodb 儲存引擎中,這個值是預設是256k。

mysql> show variables like 'sort_buffer_size';

+------------------+--------+

| variable_name | value |

+--程式設計客棧----------------+--------+

| sort_buffer_size | 262144 |

+------------------+--------+

也就是說,如果要放進 sort_buffer 中的資料是大於256k的話,那麼採用在 sort_buffer 中快排的方式肯定是行不通的,這時候,你可能會問:mysql難道不能根據資料大小自動擴充嗎?額,mysql是多執行緒模型,如果每個執行緒都擴充,那麼分給其他功能buffer就小了(比如change buffer等),就會影響其他功能的質量。

這時就得換種方式來排序了,沒錯,此時就是真正的檔案排序了,也就是磁碟的臨時檔案,mysql會採用歸併排序的思想,把要排序的資料分成若干份,每乙份資料在記憶體中排序後會放入臨時檔案中,最終對這些已經排序好的臨時檔案的資料再做一次合併排序就ok了,典型的分而治之原理,它的具體步驟如下:

通過上面的排序流程我們知道,如果要排序的資料很大,超過 sort_buffer 的大小,那麼就需要檔案排序,檔案排序涉及到分批排序與合併,很耗時,造成這個問題的根本原因是 sort_buffer 不夠用,不知道你發現沒有我們的 friend_name 需要排序,但是卻把 friend_addr 也塞進了 sort_buffer 中,這樣單行資料的大小就等於 friend_name 的長度 + friend_addr 的長度,能否讓 sort_buffer 中只存 friend_name 字段,這樣的話,整體的利用空間就大了,不一定用得到到臨時檔案。沒錯,這就是接下來要說的另一種排序優化rowid排序。

rowid 排序的思想就是把不需要的資料不要放到 sort_buffer 中,讓 sort_buffer 中只保留必要的資料,那麼你認為什麼是必要的資料呢?只放 friend_name?這肯定不行,排序完了之後,friend_addr 怎麼辦?因此還要把主鍵id放進去,這樣排完之後,通過 id 再回次表,拿到 friend_addr 即可,因此它的大致流程如下:

這裡面其實有幾點需要注意的:

那麼問題來了,兩種方式,mysql 該如何選擇?得根據某個條件來判斷走哪種方式吧,這個條件就是進 sort_buffer 單行的長度,如果長度太大(friend_name + friend_addr的長度),就會採用 rowid 這種方式,否則第一種,長度的標準是根據 max_length_for_sort_data 來的,這個值預設是1024位元組:

mysql> show variables like 'max_length_for_sort_data';

+--------------------------+-------+

| variable_name | value |

+--------------------------+-------+

| max_length_for_sort_data | 1024 |

+--------------------------+-------+

其實不管是上面哪種方法,他們都需要回表+排序,回表是因為二級索引上沒有目標字段,排序是因為資料不是有序的,那如果二級索引上有目標字段並且已經是排序好的了,那不就兩全其美了嘛。

沒錯,就是聯合索引,我們只需要建立乙個 (user_id,friend_name,friend_addr)的聯合索引即可,這樣我就可以通過這個索引拿到目標資料,並且friend_name已經是排序好的,同時還有friend_addr欄位,一招搞定,不需要回表,不需要再次排序。因此對於上述的sql,它的大致流程如下:

資料庫mysql索引 資料庫 mysql索引

mysql 索引 mysql索引的建立對於mysql的高效執行是很重要的,索引可以大大提高mysql的檢索速度。打個比方,如果合理的設計且使用索引的mysql是一輛蘭博基尼的話,那麼沒有設計和使用索引的mysql就是乙個人力三輪車。索引分單列索引和組合索引。單列索引,即乙個索引只包含單個列,乙個表可...

mysql更新索引庫 Mysql資料庫索引增刪改查

一.索引的作用 一般的應用系統,讀寫比例在10 1左右,而且插入操作和一般的更新操作很少出現效能問題,遇到最多的,也是最容易出問題的,還是一些複雜的查詢操作,所以查詢語句的優化顯然是重中之重。在資料量和訪問量不大的情況下,mysql訪問是非常快的,是否加索引對訪問影響不大。但是當資料量和訪問量劇增的...

MySQL資料庫索引

mysql資料庫索引 目錄 1 myisam與innodb的區別 2 索引的優缺點 3 如何選用索引 4 檢視索引 5 雜湊索引 6 b 樹 7 索引分類 1 myisam與innodb的區別 1 show engines 圖1.1 截圖1 a mysql資料表主要支援如圖所示的儲存引擎,分為 事務...