您好,登錄后才能下訂單哦!
前言
排序是數據庫中的一個基本功能,MySQL也不例外。通過Order by語句即能達到將指定的結果集排序的目的,
其實不僅僅是Order by語句,Group by語句,Distinct語句都會隱含使用排序
在實際業務場景中,一些開發的大牛動不動來個orderby,SQL看起寫的非常溜,而實際業務應用導致GAME OVER......
首先介紹MySQL實現排序的內部原理,并介紹與排序相關的參數,最后結合實際給出幾個"奇怪"排序,來談談排序一致問題
1、排序實現的算法:
對于不能利用索引避免排序的 SQL,數據庫不得不自己排序功能以滿足業務需求,執行計劃中會出現"USING TEMPORARY; USING filesort",
有時候filesore并不意味著就是文件排序也有可能是內存排序,只有由參數sort_buffer_size和結果集大小確定。
MySQL內部排序主要有3種方式:常規排序、優化排序和優先隊列排序,假設表結構如下:
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`col1` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`col2` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`col3` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `col1` (`col1`,`col2`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
SELECT col1,col2,col3 FROM t1 WHERE col1="100" ORDER BY col2;
a.常規排序
(1).從表t1中獲取滿足WHERE條件的記錄
(2).對于每條記錄,將記錄的主鍵+排序鍵(id,col2)取出放入sort buffer
(3).如果sort buffer可以存放所有滿足條件的(id,col2)對,則進行排序;否則sort buffer滿后,進行排序并固化到臨時文件中。(排序算法采用的是快速排序算法)
(4).若排序中產生了臨時文件,需要利用歸并排序算法,保證臨時文件中記錄是有序的
(5).循環執行上述過程,直到所有滿足條件的記錄全部參與排序
(6).掃描排好序的(id,col2)對,并利用id去撈取SELECT需要返回的列(col1,col2,col3)
(7).將獲取的結果集返回
從上述流程來看,是否使用文件排序主要看sort buffer是否能容下需要排序的(id,col2)對,這個buffer的大小由sort_buffer_size參數控制。此外一次排序需要兩次IO,一次是撈(id,col2),第二次是撈(col1,col2,col3),由于返回的結果集是按col2排序,因此id是亂序的,通過亂序的id去撈(col1,col2,col3)時會產生大量的隨機IO。對于第二次MySQL本身一個優化,
即在撈之前首先將id排序,并放入緩沖區,這個緩存區大小由參數read_rnd_buffer_size控制,然后有序去撈記錄,將隨機IO轉為順序IO
b.優化排序
常規排序方式除了排序本身,還需要額外兩次IO。優化的排序方式相對于常規排序,減少了第二次IO。主要區別在于,放入sort buffer不是(id,col2),而是(col1,col2,col3)。由于sort buffer中包含了查詢需要的所有字段,因此排序完成后可以直接返回,無需二次撈數據。這種方式的代價在于,同樣大小的sort buffer,能存放的(col1,col2,col3)數目要小于(id,col2),如果sort buffer不夠大,可能導致需要寫臨時文件,造成額外的IO。當然MySQL提供了參數max_length_for_sort_data,
只有當排序元組小于max_length_for_sort_data時,才能利用優化排序方式,否則只能用常規排序方式
c.優先隊列排序
為了得到最終的排序結果,無論怎樣,我們都需要將所有滿足條件的記錄進行排序才能返回。那么相對于優化排序方式,
在空間層面做了優化黑盒加入了一種新的排序方式--優先隊列,這種方式采用堆排序實現,堆排序算法特征正好可以解limit M,N 這類排序的問題,雖然仍然需要所有元素參與排序,但是只需要M+N個元組的sort buffer空間即可,對于M,N很小的場景,基本不會因為sort buffer不夠而導致需要臨時文件進行歸并排序的問題。
對于升序,采用大頂堆,最終堆中的元素組成了最小的N個元素,對于降序,采用小頂堆,最終堆中的元素組成了最大的N的元素
2、排序優化與索引使用
為了優化SQL語句的排序性能,最好的情況是避免排序,合理利用索引是一個不錯的方法。
因為索引本身也是有序的,如果在需要排序的字段上面建立了合適的索引,那么就可以跳過排序的過程,提高SQL的查詢速度,\
通過一些典型SQL說明哪些可以利用索引減少排序,哪些不能,
1、select * from t1 order by col1,col2
2、select * from t1 where col1="100" order by col2
3、select *from t1 col1>"100" order by col1 asc
4、select * from t1 where col1="100" and col2>"100" order by col2
3、不能利用索引避免排序
通過索引掃描的記錄數超過30%,變全表掃描
聯合索引中,第一索引列使用范圍查詢
聯合索引中,第一查詢條件不是最左索引列
升降序不一致無法使用
排序字段在多個索引中無法使用(一個聯合索引一個單列索引,一條SQL一次只能使用一個索引)
排序字段是單獨的列無法使用索引
4、業務案例,添加合理的索引
1、業務DDL:
2、對原SQL執行計劃的查看:
3、優化后的SQL執行計劃-1
3、優化后的SQL執行計劃-2
主要對原SQL進行改寫以及添加相應的索引,即可實現SQL優化,運行效率的最優。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。