国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

深入淺出MySQL優先隊列(你一定會踩到的order by limit 問題)

數據庫 MySQL
其中涉及 數據結構,PageHelper,MySQL 文檔,相關參考資料羅列在文末,如果有時間能順著文章思路親自讀一遍參考文檔,相信會有更深的收獲。

 0.先拋問題

假設字段category無索引且有重復值,order by category 和 limit 組合使用的結果會和預期不符。

問題復現:

表結構(就是兩個字段) 

  1. CREATE TABLE `ratings` (  
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,  
  3.   `category` int(11) DEFAULT NULL,  
  4.   PRIMARY KEY (`id`)  
  5. ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

對所有數據按category字段排序: select * from ratings order by category;

id category
1 1
5 1
10 1
3 2
4 2
6 2
9 2
2 3
7 3
8 3

當我們想分頁展示前5條時使用select * from ratings order by category limit 5;

期望得到的ID順序是1 5 10 3 4。

但實際結果如下:

id category
1 1
10 1
5 1
3 2
4 2

怎么肥似?MySQL 出 Bug 了?

可能有同學遇到過這個問題,百度或谷歌一下解決了,你有沒有想過,你查到的辦法是最優解嗎?別人是怎么得出這個辦法的?MySQL 為什么會這樣做,跟版本有關嗎?

先拋結論:

  1.  最優解是后面再加個列值唯一的排序字段,如:order by category,id;
  2.  MySQL 為什么這樣做?答案是為了快!(MySQL 5.6及其之后才有此優化)
  3.  次優解是對order by后面的category 加索引(為什么是次優解?看完本文你將會有答案);

下面課代表將還原一下這 3 條結論的產出過程。

1. 最優解

MySQL 文檔 8.2.1.19 LIMIT Query Optimization 中對此場景有如下描述:

If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan. In other words, the sort order of those rows is nondeterministic with respect to the nonordered columns.

One factor that affects the execution plan is LIMIT, so an ORDER BY query with and without LIMIT may return rows in different orders.

總結來說就是:

當 ORDER BY 列的字段值存在重復,那么這條 ORDER BY 語句返回的數據順序會因為LIMIT的存在而變得不一樣

這是 MySQL 默認對該場景做的優化,如果你需要保證加不加 LIMIT 順序都要一致,官方也給出了辦法:

If it is important to ensure the same row order with and without LIMIT, include additional columns in the ORDER BY clause to make the order deterministic.

就是在ORDER BY 后面再多加一個排序字段(比如 ID 字段)。

以上描述最早出現在MySQL 5.6文檔中,從這個版本開始,引入了這個針對ORDER BY LIMIT 的優化。

好了, 針對文中的場景,我們只需要select * from ratings order by category,id;即可解決。

那么問題來了,MySQL 為什么要做這么一個看似是 Bug 的優化?

2.MySQL 的 ORDER BY 邏輯

顧名思義,ORDER BY 就是排序。

執行一下explain select * from ratings order by category limit 5; 

  1. *************************** 1. row ***************************  
  2.            id: 1  
  3.   select_type: SIMPLE  
  4.         table: ratings  
  5.    partitions: NULL  
  6.          type: ALL  
  7. possible_keys: NULL  
  8.           key: NULL  
  9.       key_len: NULL  
  10.           ref: NULL  
  11.          rows: 10  
  12.      filtered: 100.00  
  13.         Extra: Using filesort  
  14. 1 row in set, 1 warning (0.00 sec) 

可以看到 Extra: Using filesort 表示需要排序。

正常情況下, MySQL 會有內存排序和外部排序兩種:

  •  如果待排序的數據量小于sort buffer size,排序就在內存中完成(快速排序);
  •  如果待排序的數據量大于sort buffer size,就使用臨時文件進行外部排序(歸并排序);

很明顯,這兩種排序都是對所有結果全部排序,講道理,不管有沒有LIMIT,都是從排完序的結果中按順序取需要的條數,有沒有LIMIT是不會影響返回的結果順序的。

但是,MySQL 5.6 版本針對 ORDER BY LIMIT做了個小優化(排序字段無索引,且列值不唯一時):優化器在遇到 ORDER BY LIMIT語句的時候,使用了priority queue。

filesort.cc 中有如下偽代碼描述該優化: 

  1. while (get_next_sortkey())  
  2.    {  
  3.      if (using priority queue)  
  4.        push sort key into queue  
  5.      else  
  6.      {  
  7.        try to put sort key into buffer;  
  8.        if (no free space in sort buffer)  
  9.        {  
  10.          do {  
  11.            allocate new, larger buffer;  
  12.            retry putting sort key into buffer;  
  13.          } until (record fits or no space for new buffer)  
  14.          if (no space for new buffer)  
  15.          {  
  16.            sort record pointers (all buffers);  
  17.            dump sorted sequence to 'tempfile';  
  18.            dump Merge_chunk describing sequence location into 'chunk_file';  
  19.          }  
  20.        }  
  21.        if (key was packed)  
  22.          tell sort buffer the actual number of bytes used;  
  23.      }  
  24.    }  
  25.    if (buffer has some elements && dumped at least once)  
  26.      sort-dump-dump as above;  
  27.    else  
  28.      don't sort, leave sort buffer to be sorted by caller. 

并在 WL#1393: Optimizing filesort with small limit 中闡述了該優化邏輯: 

  1. Many web customers have to do  
  2. "SELECT ... ORDER BY non_index_column LIMIT X",  
  3. When X *  is smaller than sort_buff_size we can use  
  4. the following algoritm to speed up the sort:  
  5. - Create a queue to hold 'limit' keys.  
  6. - Scan through the table and store the first (last if DESC) keys in the queue  
  7. - Return values from queue  
  8. This is much faster than the current algoritm that works as: 

該 WorkLog 中記錄了優化后的效果:10 to 20 times faster than a quicksort(感興趣的同學可以去閱讀原文)。

所以,就是為了快!

MySQL 認為這種場景就是求 TOP N 的問題,使用 priority queue 就能解決。

3.priority queue(優先級隊列)

priority queue 其實就是堆,Java 中有java.util.PriorityQueue類,其本質就是 堆 這種數據結構。

簡單解釋一下什么是堆:

堆是一個完全二叉樹;

堆中每一個節點的值都必須大于等于(大頂堆)或小于等于(小頂堆)其子樹中每個節點的值。

如果 MySQL 使用歸并或快排,需要把所有數據都排好序,再取LIMIT 的前幾條,剩余已排序的數據就白白浪費了。

而采用 priority queue 可以根據 LIMIT的條數維護一個堆,只需要把所有數據在這個堆里過一遍就能得到結果。

使用如下語句可以驗證 MySQL 使用了 priority queue: 

  1. SET optimizer_trace='enabled=on' 
  2. select * from ratings order by category limit 5;  
  3. SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G;  
  1. "filesort_priority_queue_optimization": {  
  2.              "limit": 5,  
  3.              "chosen": true  
  4.            }, 

可以看到 filesort_priority_queue_optimization.chosen = true

下面用流程圖還原一下 priority queue 的執行邏輯(以LIMIT 5為例):

友情提示:圖中的小頂堆以 category 值的大小排序

    1.  取前五條數據構成一個小頂堆:

    1.  取下一行數據(6,2),發現 2 小于當前堆中最大的category 3,于是把(2,3)從堆中刪掉,把(6,2) 入堆:

    1.  重復步驟 2,直至符合查詢條件的數據都經歷過比較入堆,最終堆中數據如圖:

以上就是通過 priority queue 找到 最小的 5 行 category 數據的執行過程。

最后我們將其出堆即可得到結果,每次出堆最小元素后將最后一個元素放入堆頂,按照小頂堆重新堆化,過程如圖:

可以看到,這個結果和select * from ratings order by category limit 5;的輸出一致

4.加索引為什么是次優解

顯然,按照ORDER BY 的邏輯,直接對排序字段加索引也可以省去內存排序步驟,從而解決這個問題。

但索引也不是銀彈,多出來的category索引會增加表的維護成本,如果沒有明顯的業務需要,單純為了繞過這個priority queue的優化而加索引,課代表認為有點得不償失。

尤其是當表數據量非常大的時候,索引的體量會很可觀。而且,針對文中場景,category作為分類字段,重復率會比較高,即使有按分類查詢的業務 SQL ,MySQL 也不一定會選取這條索引。

綜上,針對本場景,個人認為order by category,id才是該問題的最優解。

PS:會不會有人問:關我鳥事,我從沒寫過帶 LIMIT 的 SQL 啊!

難道你寫的 CRUD 功能都不帶分頁的嗎?PageHelper 源碼去了解一下?

5. 總結

本文案例是課代表上線過程中遭遇到的實際問題,咨詢了下周圍同學,有好幾個都遇到過此問題,網上文章大多淺入淺出,讀完有隔靴搔癢之感,無法解答心中疑惑。遂整理此文。

其中涉及 數據結構,PageHelper,MySQL 文檔,相關參考資料羅列在文末,如果有時間能順著文章思路親自讀一遍參考文檔,相信會有更深的收獲。 

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2021-07-19 11:54:15

MySQL優先隊列

2023-02-14 08:00:00

MySQL索引查詢

2022-10-08 08:09:13

MGRGreatSQL事務

2021-03-16 08:54:35

AQSAbstractQueJava

2011-07-04 10:39:57

Web

2016-10-14 13:53:05

JavascriptDOMWeb

2019-01-16 17:05:02

Python亂碼網絡

2019-08-22 10:57:42

MySQL數據索引

2009-03-16 13:44:29

雙向復制實例MySQL

2019-11-11 14:51:19

Java數據結構Properties

2022-12-02 09:13:28

SeataAT模式

2009-11-30 16:46:29

學習Linux

2017-07-02 18:04:53

塊加密算法AES算法

2019-01-07 15:29:07

HadoopYarn架構調度器

2012-05-21 10:06:26

FrameworkCocoa

2021-07-20 15:20:02

FlatBuffers阿里云Java

2022-09-26 09:01:15

語言數據JavaScript

2023-11-03 10:33:26

2018-03-15 09:13:43

MySQL存儲引擎

2023-03-20 09:48:23

ReactJSX
點贊
收藏

51CTO技術棧公眾號

亚洲成人777| 深夜福利一区二区| 日韩欧美一区二区三区四区| 四虎成人精品一区二区免费网站| 国产欧美日韩另类一区| 国产精品一区二区免费看| 99re66热这里只有精品4| 狠狠色香婷婷久久亚洲精品| 日韩一级免费看| 在线国产欧美| 国产精品91一区| 只有精品亚洲| 亚洲аv电影天堂网| 黄动漫在线看| 欧美国产精品专区| 一区二区精品在线| 激情久久久久久久| 国产不卡av在线| 亚洲精品一区二区三区在线| 日韩黄色av网站| 精品国产99久久久久久| 亚洲mv在线观看| 免费女人黄页| www激情久久| 熟女视频一区二区三区| 国内精品美女在线观看| 国产91色在线免费| 一区二区三区视频免费视频观看网站 | 精品调教chinesegay| 黄色在线小视频| 亚洲国产欧美在线人成| 国产精品欧美一区二区三区| 欧美一区二区三区视频在线观看| 老司机aⅴ毛片免费观看| 国产女同性恋一区二区| 成人免费aaa| 国产成人精品免费在线| 中文字幕中文字幕在线中心一区| 亚洲专区一区| 国模精品娜娜一二三区| 国产在线不卡| 91亚洲午夜在线| 66久久国产| 91九色在线观看| 午夜精品网站| 精品视频免费观看| 日韩精品视频网| gogogo免费高清日本写真| 奇米777欧美一区二区| 一本一生久久a久久精品综合蜜| 免费久久精品视频| 91亚洲精品国产| 91麻豆免费观看| 三上悠亚在线资源| 亚洲第一av色| 日韩黄色影院| 日韩精品一二三四区| 成人国产精品一区二区免费麻豆| 在线亚洲男人天堂| 91精品短视频| 日韩av第一页| 99久久夜色精品国产亚洲狼| 99视频国产精品免费观看| 亚洲一卡久久| 欧洲精品一区二区三区久久| 日本一区免费视频| 最新亚洲伊人网| 88在线观看91蜜桃国自产| 欧亚av在线| 国内精品久久久久影院 日本资源 国内精品久久久久伊人av | 99久久久国产精品免费调教网站| 在线成人一区二区| 宅男在线一区| 91香蕉国产在线观看| 六月天综合网| 国产精品久久久久久久乖乖| 国产精品国产自产拍高清av | 亚洲麻豆视频| 国产欧美综合一区| 国产精品久久久久久久久久久免费看| 在线免费黄色毛片| 日韩精品免费综合视频在线播放| 18国产精品| 国产亚洲一区二区三区在线播放 | 国产a级毛片一区| 亚州福利视频| 欧美人伦禁忌dvd放荡欲情| 粉嫩一区二区三区| 国产成人精品视频在线| 亚洲一二三四区| 四虎亚洲成人| 这里只有视频精品| 91社区在线观看播放| 欧美亚一区二区| 一区二区高清视频| 91啪亚洲精品| 欧美r片在线| 日韩一区二区三区在线| yw.尤物在线精品视频| 青青在线视频一区二区三区| 欧美日韩蜜桃| 亚洲 欧美 综合 另类 中字| 亚洲日本乱码在线观看| 2020国产在线视频| 欧美福利小视频| 激情久久一区| chinese少妇国语对白| 欧美视频一区二区三区| av成人免费| 精品国产二区在线| 国产精品理伦片| 99热99re6国产在线播放| 2023亚洲男人天堂| 精品一区二区在线观看| 在线黄色国产视频| 久久激情视频久久| 香蕉亚洲视频| 影音先锋可以看的网站| 亚洲欧美综合精品久久成人| 欧美一二区在线观看| 日韩成人手机在线| 91精品国产91综合久久蜜臀| 欧美男gay| 欧美 国产 日本| 精品少妇一区二区三区在线播放 | 久久久久久久久一| 九色91在线| 成人xxxxx色| 亚洲男人的天堂在线aⅴ视频| 玖玖在线播放| 久久精品国产99精品国产亚洲性色| 专区另类欧美日韩| 欧美黄页在线免费观看| 日韩精品久久久| 91久久免费观看| 免费久久精品| 成人女性文胸| 欧美美女操人视频| 成人丝袜18视频在线观看| 国产写真视频在线观看| 亚洲wwwav| 午夜精品影院在线观看| 天堂成人娱乐在线视频免费播放网站 | 欧美大尺度做爰床戏| 亚洲免费电影一区| 免费在线欧美视频| 日本中文字幕视频在线| 91久久精品视频| 亚洲国产日韩综合久久精品| 风间由美性色一区二区三区四区| 成人黄色av片| 最新的欧美黄色| 国产成人午夜精品影院观看视频 | 亚洲成人av中文| 亚洲ab电影| www.男人的天堂| 538国产精品视频一区二区| 久久久久久久久久看片| 第四色男人最爱上成人网| 午夜精品亚洲一区二区三区嫩草| 欧美午夜不卡在线观看免费| 欧美国产专区| 成人欧美一区| 免费观看成人在线| 超碰在线一区| √天堂资源地址在线官网| 好色先生视频污| 亚洲激情第一页| 亚洲 日韩 国产第一区| 在线观看免费亚洲| 欧美黄色aaaa| av电影在线观看一区二区三区| 99r国产精品视频| 5858s免费视频成人| 久久久精品午夜少妇| 8x8ⅹ拨牐拨牐拨牐在线观看| 香蕉久久夜色| 精品视频在线播放免| 99久久国产免费看| baoyu135国产精品免费| 国产福利电影网| 91久久精品一区| 日韩视频免费观看高清完整版在线观看| 日本成人中文字幕| 99久久精品一区二区成人| www欧美激情| 91中文字幕一区| 亚洲成av人乱码色午夜| 成av人片一区二区| 九九免费精品视频在线观看| 在线成年人视频| 久久精品国产一区二区三区日韩 | h片在线观看视频免费| 青青视频免费在线| 午夜精品久久久99热福利| 精品欧美aⅴ在线网站| 日韩av成人高清| 国产91精品入| 免费av网站在线观看| 福利视频一区二区三区四区|