中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何優化sql中order By語句

發布時間:2022-09-27 16:09:16 來源:億速云 閱讀:143 作者:iii 欄目:MySQL數據庫

這篇文章主要介紹“如何優化sql中order By語句”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“如何優化sql中order By語句”文章能幫助大家解決問題。

在使用數據庫進行數據查詢時,難免會遇到基于某些字段對查詢的結果集進行排序的需求。在sql中通常使用orderby語句來實現。將需要排序的字段放到 該關鍵詞后,如果有多個字段的話,就用","分割。

select * from table t order by t.column1,t.column2;

上面的sql表示查詢表table中數據,然后先按照column1排序,如果column1相同的話,在按照column2排序,排序的方式默認是降序。當然排序方式也是可以指定的。在被排序字段后添加 DESC,ASE,分別表示降序和升序。

使用該orderby可以很方便的實現日常的排序操作。使用的多了,不知道你有沒有遇到過這種場景:有時候使用orderby后,sql執行效率非常慢,有時候卻比較快,由于整天被curd纏身,也沒有時間研究,反正就是覺得很神奇。趁這個周末比較閑,就來研究下,mysql中orderby是怎么實現的。

為了方便描述,我們先建立一個數據表 t1,如下:

CREATE TABLE `t1` (
  `id` int(11) NOT NULL not null auto_increment,
  `a` int(11)  DEFAULT NULL,
  `b` int(11)  DEFAULT NULL,
  `c` int(11)  DEFAULT NULL,
  PRIMARY KEY (`id`) ,
  KEY `a` (`a`) USING BTREE
) ENGINE=InnoDB;

并插入數據:

insert into t1 (a,b,c) values (1,1,3);
insert into t1 (a,b,c) values (1,4,5);
insert into t1 (a,b,c) values (1,3,3);
insert into t1 (a,b,c) values (1,3,4);
insert into t1 (a,b,c) values (1,2,5);
insert into t1 (a,b,c) values (1,3,6);

為了使索引生效,插入10000行 7,7,7,無關數據,數據量少的情況下,會直接全表掃描

insert into t1 (a,b,c) values (7,7,7);

我們現在需要查找 a=1的所有記錄,然后按照b字段進行排序。

查詢sql為

select a,b,c from t1 where a = 1 order by b limit 2;

為了防止在查詢過程中全表掃描,我們在字段a上添加了索引。

首先我們先通過語句

explain select a,b,c from t1 where a = 1 order by b lmit 2;

查看sql的執行計劃,如下所示:

如何優化sql中order By語句

在extra中我們可以看到出現了Using filesort,這個表示 該sql執行過程中,執行了排序操作,排序操作在 sort_buffer中完成,sort_buffer是mysql分配給每個線程的一個內存緩沖區,該緩沖區專門用來完成排序,大小默認是1M,其大小由變量 sort_buffer_size 進行控制。

mysql在對orderby進行實現時,根據放入到sort_buffer中的字段內容不同,進行了兩種不同實現方式:全字段排序和rowid排序。

全字段排序

首先我們先通過一張圖整體看一下sql執行過程:

如何優化sql中order By語句

mysql先根據查詢條件確定需要排序的數據集,也就是表中 a=1的數據集,即主鍵id從1到6的這些記錄。

整個sql的執行的過程如下:

1.創建并初始化sort_buffer,并確定需要放到該緩沖區中的字段,也就是a,b,c這三個字段。

2.從索引樹a中找到第一個滿足a=1的主鍵id,也就是id=1。

3.回表到id索引,取出整行數據,然后從整行數據中,取出a,b,c的值,放入到sort_buffer中。

4.從索引a中按照順序找到下一個a=1的主鍵id。

5.重復步驟3和步驟4,直到獲取到最后一個a=1的記錄,也就是主鍵id=5。

6.此時滿足條件a=1的所有記錄的 a,b,c字段,全部讀放到了sort_buffer中,然后,對這些數據按照b的值進行進行排序,排序的方式是快速排序。就是那個面試經常面到的快速排序,時間復雜度為log2n的快速排序。

7.然后從排序后的結果集中取出前2行數據。

上面是就是msql中orderby的執行流程。因為放入到sort_buffer中的數據是需要輸出的全部字段,所以這種排序被稱為全排序。

看到這里不知道你是否會有疑問?如果需要排序的數據量很大的話,sort_buffer裝不下怎么辦?

的確,如果a=1的數據行特別多,且需要存放到sort_buffer中的字段比較多,可能不止a,b,c三個字段,有些業務可能需要輸出更多字段。那么默認大小只有1M的sort_buffer很可能容納不下。

當sort_buffer容納不下的時候,mysql會創建一批臨時的磁盤文件來輔助排序。默認情況下會創建12個臨時文件,將需要排序的數據分成12份,每一份單獨排序,形成12個內部數據有序的文件,然后把這12個有序文件在合并成一個有序的大文件,最終完成數據的排序。

基于文件的排序,相比基于內存的排序,排序效率要低很多,為了提高排序的效率,應該盡量避免基于文件的排序,要想避免基于文件排序,就需要讓sort_buffer可以容納需要排序的數據量。

所以對于sort_buffer容納不下的情況,mysql進行了優化。就是在排序時候,降低存放到sort_buffer中的字段個數。

具體優化方式,就是下面的rowId排序

RowId 排序

在全字段排序實現中,排序的過程中,要把需要輸出的字段全部放到sort_buffer中,當輸出的字段比較多的時候,可以放到sort_buffer中的數據行就會變少。也就增大了sort_buffer無法容納數據的風險,直至出現基于文件的排序。

rowId排序對全字段排序的優化手段,主要是減少了放到sort_buffer中字段個數。

在rowId排序中,只會將需要排序的字段和主鍵Id放到sort_buffer中。

select a,b,c from t1 where a = 1 order by b limit 2;

在rowId的排序中的執行流程如下:

1.初始化并創建sort_buffer,并確認要放入的的字段,id和b。

2.從索引樹a中找到第一個滿足a=1的主鍵id,也就是id=1。

3.回表主鍵索引id,取出整行數據,從整行數據中取出id和b,存入sort_buffer中。

4.從索引a中取出下一條滿足a=1的 記錄的主鍵id。

5.重復步驟3和4,直到最后一個滿足a=1的主鍵id,也就是a=6。

6.對sort_buffer中的數據,按照字段b排序。

7.從sort_buffer中的有序數據集中,取出前2個,因為此時取出的數據只有id和b,要想獲取a和c字段,需要根據id字段,回表到主鍵索引中取出整行數據,從整行數據中獲取需要的數據。

根據rowId排序的執行步驟,可以發現:相比全字段排序,rowId排序的實現方式,減少了存放到sort_buffer中的數據量,降低了基于文件的外部排序的可能性。

那rowid排序有不足的地方嗎?肯定有的,要不然全字段排序就沒有存在的意義了。rowid排序不足之處在于,在最后的步驟7中,增加了回表的次數,不過這個回表的次數,取決于limit后的值,如果返回的結果集比較小的話,回表的次數還是比較小的。

mysql是如何在全字段排序和rowId排序的呢?其實是根據存放的sort_buffer中每行字段的長度決定的,如果mysql認為每次放到sort_buffer中的數據量很大的話,那么就用rowId排序實現,否則使用全字段排序。那么多大算大呢?這個大小的閾值有一個變量的值來決定,這個變量就是 max_length_for_sort_data。如果每次放到sort_buffer中的數據大小大于該字段值的話,就使用rowId排序,否則使用全字段排序。

orderby的優化

上面講述了orderby的兩種排序的方式,以及一些優化策略,優化的目的主要就是避免基于磁盤文件的外部排序。因為基于磁盤文件的排序效率要遠低于基于sort_buffer的內存排序。

但是當數據量比較大的時候,即使sort_buffer比較大,所有數據全部放在內存中排序,sql的整體執行效率也不高,因為排序這個操作,本身就是比較消耗性能的。

試想,如果基于索引a獲取到所有a=1的數據,按照字段b,天然就是有序的,那么就不用執行排序操作,直接取出來的數據,就是符合結果的數據集,那么sql的執行效率就會大幅度增長。

其實要實現整個sql執行過程中,避免排序操作也不難,只需要創建一個a和b的聯合索引即可。

alter table t1 add index a_b (a,b);

添加a和b的聯合索引后,sql執行流程就變成了:

1.從索引樹(a,b)中找到第一個滿足a=1的主鍵id,也就是id=1。

2.回表到主鍵索引樹,取出整行數據,并從中取出a,b,c,直接作為結果集的一部分返回。

3.從索引樹(a,b)上取出下一個滿足a=1的主鍵id。

4.重復步驟2和3,直到找到第二個滿足a=1的主鍵id,并回表獲取字段a,b,c。

此時我們可以通過查看sql的執行計劃,來判斷sql的執行過程中是否執行了排序操作。

explain select a,b from t1 where a = 1 order by b lmit 2;

如何優化sql中order By語句

通過查看執行計劃,我們發現extra中已經沒有了using filesort了,也就是沒有執行排序操作了。

其實還可以通過覆蓋索引,對該sql進一步優化,通過在索引中覆蓋字段c,來避免回表的操作。

alter table t1 add index a_b_c (a,b,c);

添加索引a_b_c后,sql的執行過程如下:

1.從索引樹(a,b,c)中找到第一個滿足a=1的索引,從中取出a,b,c。直接作為結果集的一部分直接返回。

2.從索引(a,b,c)中取出下一個,滿足a=1的記錄作為結果集的一部分。

3.重復執行步驟2,直到查到第二個a=1或者不滿足a=1的記錄。

此時通過查看執行sql的的還行計劃可以發現 extra中只有 Using index。

explain select a,b from t1 where a = 1 order by b lmit 2;

如何優化sql中order By語句

關于“如何優化sql中order By語句”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

汨罗市| 莎车县| 礼泉县| 奇台县| 电白县| 江北区| 新乐市| 杭锦旗| 抚顺市| 五峰| 库车县| 广南县| 昌宁县| 招远市| 抚顺县| 思南县| 襄汾县| 鄄城县| 陇川县| 壶关县| 濮阳县| 镇远县| 托克逊县| 夏邑县| 宝鸡市| 灵台县| 呼玛县| 鱼台县| 江陵县| 和平县| 舞阳县| 嘉禾县| 兴山县| 赣榆县| 农安县| 遵化市| 石家庄市| 东丽区| 大安市| 鄂托克旗| 绥滨县|