日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

c++ sort 从大到小排序_算法的艺术:MySQL order by对各种排序算法的巧用

發布時間:2025/3/15 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c++ sort 从大到小排序_算法的艺术:MySQL order by对各种排序算法的巧用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 【精華】洞悉MySQL底層架構:游走在緩沖與磁盤之間?這篇文章中,我們介紹了索引樹的頁面怎么加載到內存中,如何淘汰,等底層細節。這篇文章我們從比較宏觀的角度來看MySQL中關鍵字的原理。本文,我們主要探索order by語句的底層原理。閱讀完本文,您將了解到:

  • order by語句有哪些排序模式,以及每種排序模式的優缺點;

  • order by語句會用到哪些排序算法,在什么場景下會選擇哪種排序算法;

  • 如何查看和分析sql的order by優化手段(執行計劃 + OPTIMIZER_TRACE日志);

  • 如何優化order by語句的執行效率?(思想:減小行大小,盡量走索引,能夠走覆蓋索引最佳,可適當增加sort buffer內存大小)

這里我們從數據結構的維度來看數據和索引,也就是都當成B+樹的的,我們需要數據的時候再從存儲引擎的B+樹中讀取。

以下是我們本文作為演示例子的表,假設我們有如下表:

索引如下:

對應的idx_d索引結構如下(這里我們做了一些夸張的手法,讓一個頁數據變小,為了展現在索引樹中的查找流程):

1、如何跟蹤執行優化

為了方便分析sql的執行流程,我們可以在當前session中開啟 optimizer_trace:

SET optimizer_trace='enabled=on';

然后執行sql,執行完之后,就可以通過以下堆棧信息查看執行詳情了:

SELECT * FROM information_schema.OPTIMIZER_TRACE\G;

以下是

1select?a,?b,?c,?d?from?t20?force?index(idx_abc)??where?a=3?order?by?d?limit?100,2;

的執行結果,其中符合a=3的有8457條記錄,針對order by重點關注以下屬性

1"filesort_priority_queue_optimization":?{??//?是否啟用優先級隊列
2??"limit":?102,???????????//?排序后需要取的行數,這里為?limit?100,2,也就是100+2=102
3??"rows_estimate":?24576,?//?估計參與排序的行數
4??"row_size":?123, ???????//?行大小
5??"memory_available":?32768,????//?可用內存大小,即設置的sort?buffer大小
6??"chosen":?true??????????//?是否啟用優先級隊列
7},
8...
9"filesort_summary":?{
10??"rows":?103,????????????????//?排序過程中會持有的行數
11??"examined_rows":?8457,??????//?參與排序的行數,InnoDB層返回的行數
12??"number_of_tmp_files":?0,???//?外部排序時,使用的臨時文件數量
13??"sort_buffer_size":?13496,??//?內存排序使用的內存大小
14??"sort_mode":?"sort_key,?additional_fields"??//?排序模式
15}

1.1、排序模式

其中 sort_mode有如下幾種形式:

  • sort_key, rowid:表明排序緩沖區元組包含排序鍵值和原始表行的行id,排序后需要使用行id進行回表,這種算法也稱為original filesort algorithm(回表排序算法);

  • sort_key, additional_fields:表明排序緩沖區元組包含排序鍵值和查詢所需要的列,排序后直接從緩沖區元組取數據,無需回表,這種算法也稱為modified filesort algorithm(不回表排序);

  • sort_key, packed_additional_fields:類似上一種形式,但是附加的列(如varchar類型)緊密地打包在一起,而不是使用固定長度的編碼。

如何選擇排序模式

選擇哪種排序模式,與max_length_for_sort_data這個屬性有關,這個屬性默認值大小為1024字節:

  • 如果查詢列和排序列占用的大小超過這個值,那么會轉而使用sort_key, rowid模式;

  • 如果不超過,那么所有列都會放入sort buffer中,使用sort_key, additional_fields或者sort_key, packed_additional_fields模式;

  • 如果查詢的記錄太多,那么會使用sort_key, packed_additional_fields對可變列進行壓縮。

1.2、排序算法

基于參與排序的數據量的不同,可以選擇不同的排序算法:

  • 如果排序取的結果很小,小于內存,那么會使用優先級隊列進行堆排序;

  • 例如,以下只取了前面10條記錄,會通過優先級隊列進行排序:

  • 1select?a,?b,?c,?d?from?t20?force?index(idx_abc)??where?a=3?order?by?d?limit?10;
  • 如果排序limit n, m,n太大了,也就是說需要取排序很后面的數據,那么會使用sort buffer進行快速排序:

  • 如下,表中a=1的數據有三條,但是由于需要limit到很后面的記錄,MySQL會對比優先級隊列排序和快速排序的開銷,選擇一個比較合適的排序算法,這里最終放棄了優先級隊列,轉而使用sort buffer進行快速排序:

  • 1select?a,?b,?c,?d?from?t20?force?index(idx_abc)??where?a=1?order?by?d?limit?300,2;
  • 如果參與排序的數據sort buffer裝不下了,那么我們會一批一批的給sort buffer進行內存快速排序,結果放入排序臨時文件,最終使對所有排好序的臨時文件進行歸并排序,得到最終的結果;

  • 如下,a=3的記錄超過了sort buffer,我們要查找的數據是排序后1000行起,sort buffer裝不下1000行數據了,最終MySQL選擇使用sort buffer進行分批快排,把最終結果進行歸并排序:

  • 1select?a,?b,?c,?d?from?t20?force?index(idx_abc)??where?a=3?order?by?d?limit?1000,10;

2、order by走索引避免排序

執行如下sql:

1select?a,?b,?c,?d?from?t20?force?index(idx_d)?where?d?like?'t%'?order?by?d?limit?2;

我們看一下執行計劃:

發現Extra列為:Using index condition,也就是這里只走了索引。

執行流程如下圖所示:

通過idx_d索引進行range_scan查找,掃描到4條記錄,然后order by繼續走索引,已經排好序,直接取前面兩條,然后去聚集索引查詢完整記錄,返回最終需要的字段作為查詢結果。這個過程只需要借助索引。

如何查看和修改sort buffer大小?

我們看一下當前的sort buffer大小:

可以發現,這里默認配置了sort buffer大小為512k。

我們可以設置這個屬性的大小:

SET GLOBAL sort_buffer_size = 32*1024;

或者

SET sort_buffer_size = 32*1024;

下面我們統一把sort buffer設置為32k

1SET?sort_buffer_size?=?32*1024;?

3、排序算法案例

3.1、使用優先級隊列進行堆排序

如果排序取的結果很小,并且小于sort buffer,那么會使用優先級隊列進行堆排序;

例如,以下只取了前面10條記錄:

1select?a,?b,?c,?d?from?t20?force?index(idx_abc)?where?a=3?order?by?d?limit?10;

a=3的總記錄數:8520。查看執行計劃:

發現這里where條件用到了索引,order by limit用到了排序。我們進一步看看執行的optimizer_trace日志:

1"filesort_priority_queue_optimization":?{
2??"limit":?10,
3??"rows_estimate":?27033,
4??"row_size":?123,
5??"memory_available":?32768,
6??"chosen":?true??//?使用優先級隊列進行排序
7},
8"filesort_execution":?[
9],
10"filesort_summary":?{
11??"rows":?11,
12??"examined_rows":?8520,
13??"number_of_tmp_files":?0,
14??"sort_buffer_size":?1448,
15??"sort_mode":?"sort_key,?additional_fields"
16}

發現這里是用到了優先級隊列進行排序。排序模式是:sort_key, additional_fields,即先回表查詢完整記錄,把排序需要查找的所有字段都放入sort buffer進行排序。

所以這個執行流程如下圖所示:

  • 通過where條件a=3掃描到8520條記錄;

  • 回表查找記錄;

  • 把8520條記錄中需要的字段放入sort buffer中;

  • 在sort buffer中進行堆排序;

  • 在排序好的結果中取limit 10前10條,寫入net buffer,準備發送給客戶端。

  • 3.2、內部快速排序

    如果排序limit n, m,n太大了,也就是說需要取排序很后面的數據,那么會使用sort buffer進行快速排序。MySQL會對比優先級隊列排序和歸并排序的開銷,選擇一個比較合適的排序算法。

    如何衡量究竟是使用優先級隊列還是內存快速排序?
    一般來說,快速排序算法效率高于堆排序,但是堆排序實現的優先級隊列,無需排序完所有的元素,就可以得到order by limit的結果。
    MySQL源碼中聲明了快速排序速度是堆排序的3倍,在實際排序的時候,會根據待排序數量大小進行切換算法。如果數據量太大的時候,會轉而使用快速排序。

    有如下SQL:

    1select?a,?b,?c,?d?from?t20?force?index(idx_abc)??where?a=1?order?by?d?limit?300,2;

    我們把sort buffer設置為32k:

    1SET?sort_buffer_size?=?32*1024;?

    其中a=1的記錄有3條。查看執行計劃:

    可以發現,這里where條件用到了索引,order by limit 用到了排序。我們進一步看看執行的optimizer_trace日志:

    1"filesort_priority_queue_optimization":?{
    2??"limit":?302,
    3??"rows_estimate":?27033,
    4??"row_size":?123,
    5??"memory_available":?32768,
    6??"strip_additional_fields":?{
    7????"row_size":?57,
    8????"sort_merge_cost":?33783,
    9????"priority_queue_cost":?61158,
    10????"chosen":?false??//?對比發現快速排序開銷成本比優先級隊列更低,這里不適用優先級隊列
    11??}
    12},
    13"filesort_execution":?[
    14],
    15"filesort_summary":?{
    16??"rows":?3,
    17??"examined_rows":?3,
    18??"number_of_tmp_files":?0,
    19??"sort_buffer_size":?32720,
    20??"sort_mode":?""
    21}

    可以發現這里最終放棄了優先級隊列,轉而使用sort buffer進行快速排序。

    所以這個執行流程如下圖所示:

  • 通過where條件a=1掃描到3條記錄;

  • 回表查找記錄;

  • 把3條記錄中需要的字段放入sort buffer中;

  • 在sort buffer中進行快速排序;

  • 在排序好的結果中取limit 300, 2第300、301條記錄,寫入net buffer,準備發送給客戶端。

  • 3.3、外部歸并排序

    當參與排序的數據太多,一次性放不進去sort buffer的時候,那么我們會一批一批的給sort buffer進行內存排序,結果放入排序臨時文件,最終使對所有排好序的臨時文件進行歸并排序,得到最終的結果。

    有如下sql:

    1select?a,?b,?c,?d?from?t20?force?index(idx_abc)?where?a=3?order?by?d?limit?1000,10;

    其中a=3的記錄有8520條。執行計劃如下:

    image-20200614171147989

    可以發現,這里where用到了索引,order by limit用到了排序。進一步查看執行的optimizer_trace日志:

    1"filesort_priority_queue_optimization":?{
    2??"limit":?1010,
    3??"rows_estimate":?27033,
    4??"row_size":?123,
    5??"memory_available":?32768,
    6??"strip_additional_fields":?{
    7????"row_size":?57,
    8????"chosen":?false,
    9????"cause":?"not_enough_space"??//?sort?buffer空間不夠,無法使用優先級隊列進行排序了
    10??}
    11},
    12"filesort_execution":?[
    13],
    14"filesort_summary":?{
    15??"rows":?8520,
    16??"examined_rows":?8520,
    17??"number_of_tmp_files":?24,??//?用到了24個外部文件進行排序
    18??"sort_buffer_size":?32720,
    19??"sort_mode":?""
    20}

    我們可以看到,由于limit 1000,要返回排序后1000行以后的記錄,顯然sort buffer已經不能支撐這么大的優先級隊列了,所以轉而使用sort buffer內存排序,而這里需要在sort buffer中分批執行快速排序,得到多個排序好的外部臨時文件,最終執行歸并排序。(外部臨時文件的位置由tmpdir參數指定)

    其流程如下圖所示:

    4、排序模式案例

    4.1、sort_key, additional_fields模式

    sort_key, additional_fields,排序緩沖區元組包含排序鍵值和查詢所需要的列(先回表取需要的數據,存入排序緩沖區中),排序后直接從緩沖區元組取數據,無需再次回表。

    上面 2.3.1、2.3.2節的例子都是這種排序模式,就不繼續舉例了。

    4.2、模式

    sort_key, packed_additional_fields:類似上一種形式,但是附加的列(如varchar類型)緊密地打包在一起,而不是使用固定長度的編碼。

    上面2.3.3節的例子就是這種排序模式,由于參與排序的總記錄大小太大了,因此需要對附加列進行緊密地打包操作,以節省內存。

    4.3、模式

    前面我們提到,選擇哪種排序模式,與max_length_for_sort_data[2]這個屬性有關,max_length_for_sort_data規定了排序行的最大大小,這個屬性默認值大小為1024字節:

    也就是說如果查詢列和排序列占用的大小小于這個值,這個時候會走sort_key, additional_fields或者sort_key, packed_additional_fields算法,否則,那么會轉而使用sort_key, rowid模式。

    現在我們特意把這個值設置小一點,模擬sort_key, rowid模式:

    1SET?max_length_for_sort_data?=?100;

    這個時候執行sql:

    1select?a,?b,?c,?d?from?t20?force?index(idx_abc)?where?a=3?order?by?d?limit?10;

    這個時候再查看sql執行的optimizer_trace日志:

    1"filesort_priority_queue_optimization":?{
    2??"limit":?10,
    3??"rows_estimate":?27033,
    4??"row_size":?49,
    5??"memory_available":?32768,
    6??"chosen":?true
    7},
    8"filesort_execution":?[
    9],
    10"filesort_summary":?{
    11??"rows":?11,
    12??"examined_rows":?8520,
    13??"number_of_tmp_files":?0,
    14??"sort_buffer_size":?632,
    15??"sort_mode":?""
    16}

    可以發現這個時候切換到了sort_key, rowid模式,在這個模式下,執行流程如下:

  • where條件a=3掃描到8520條記錄;

  • 回表查找記錄;

  • 找到這8520條記錄的id和d字段,放入sort buffer中進行堆排序;

  • 排序完成后,取前面10條;

  • 取這10條的id回表查詢需要的a,b,c,d字段值;

  • 依次返回結果給到客戶端。

  • 可以發現,正因為行記錄太大了,所以sort buffer中只存了需要排序的字段和主鍵id,以時間換取空間,最終排序完成,再次從聚集索引中查找到所有需要的字段返回給客戶端,很明顯,這里多了一次回表操作的磁盤讀,整體效率上是稍微低一點的。

    5、order by優化總結

    根據以上的介紹,我們可以總結出以下的order by語句的相關優化手段:

    • order by字段盡量使用固定長度的字段類型,因為排序字段不支持壓縮;

    • order by字段如果需要用可變長度,應盡量控制長度,道理同上;

    • 查詢中盡量不用用select *,避免查詢過多,導致order by的時候sort buffer內存不夠導致外部排序,或者行大小超過了max_length_for_sort_data導致走了sort_key, rowid排序模式,使得產生了更多的磁盤讀,影響性能;

    • 嘗試給排序字段和相關條件加上聯合索引,能夠用到覆蓋索引最佳。


    這篇文章的內容就差不多介紹到這里了,能夠閱讀到這里的朋友真的是很有耐心,為你點個贊。

    本文為arthinking基于相關技術資料和官方文檔撰寫而成,確保內容的準確性,如果你發現了有何錯漏之處,煩請高抬貴手幫忙指正,萬分感激。

    大家可以關注我的博客:itzhai.com?獲取更多文章,我將持續更新后端相關技術,涉及JVM、Java基礎、架構設計、網絡編程、數據結構、數據庫、算法、并發編程、分布式系統等相關內容。

    如果您覺得讀完本文有所收獲的話,可以關注我的賬號,或者點個贊吧,碼字不易,您的支持就是我寫作的最大動力,再次感謝!

    關注我的公眾號,及時獲取最新的文章。

    更多文章

    JVM系列專題:公眾號發送?JVM

    References

    [1]: 滴滴云. MySQL 全表 COUNT(*) 簡述. zhihu.com. Retrieved from https://zhuanlan.zhihu.com/p/54378839

    [2]: MySQL. 8.2.1.14 ORDER BY Optimization. Retrieved from https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html

    [3]: MySQL:排序(filesort)詳細解析. Retrieved from https://www.jianshu.com/p/069428a6594e

    [4]: MYSQL實現ORDER BY LIMIT的方法以及優先隊列(堆排序). Retrieved from http://blog.itpub.net/7728585/viewspace-2130920/

    ·END·訪問IT宅(itzhai.com)查看我的博客更多文章

    掃碼關注及時獲取新內容↓↓↓

    Java架構雜談

    Java后端技術架構 · 技術專題 · 經驗分享

    blog: itzhai.com

    碼字不易,如有收獲,點個「贊」哦~

    總結

    以上是生活随笔為你收集整理的c++ sort 从大到小排序_算法的艺术:MySQL order by对各种排序算法的巧用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。