阿里MySQL面試題】內(nèi)部臨時表

      網(wǎng)友投稿 859 2025-04-02

      sort buffer、內(nèi)存臨時表和join buffer,都是用來存放語句執(zhí)行過程中的中間數(shù)據(jù),以輔助SQL語句的執(zhí)行。在排序的時候用到了sort buffer,在使用join語句的時候用到了join buffer。


      union 執(zhí)行流程

      示例表

      創(chuàng)建數(shù)據(jù)

      執(zhí)行如下SQL:

      這條語句用到了union,它的語義是,取這兩個子查詢結(jié)果的并集。重復的行只保留一行。

      key=PRIMARY,說明第二個子句用索引id。

      Extra字段,表示在對子查詢的結(jié)果集做union時,使用了臨時表(Using temporary)

      該語句的執(zhí)行流程:

      創(chuàng)建一個內(nèi)存臨時表,該臨時表只有一個整型字段f,并且f是主鍵字段

      執(zhí)行第一個子查詢,得到1000這個值,并存入臨時表中

      執(zhí)行第二個子查詢:

      拿到第一行id=1000,試圖插入臨時表中。但由于1000這個值已經(jīng)存在于臨時表了,違反了唯一性約束,所以插入失敗,然后繼續(xù)執(zhí)行

      取到第二行id=999,插入臨時表成功。

      從臨時表中按行取出數(shù)據(jù),返回結(jié)果,并刪除臨時表,結(jié)果中包含兩行數(shù)據(jù)分別是1000和999。

      union 執(zhí)行流程

      這里內(nèi)存臨時表用于暫存數(shù)據(jù),而且計算過程還用上了臨時表主鍵id的唯一性約束,實現(xiàn)了union語義。

      若把上面語句的union改成union all,就失去了“去重”語義。執(zhí)行時,就依次執(zhí)行子查詢,得到的結(jié)果直接作為結(jié)果集的一部分,發(fā)給客戶端。也就不需要臨時表了。

      union all的執(zhí)行計劃

      Extra=Using index,只使用了覆蓋索引,沒有用臨時表。

      group by 執(zhí)行流程

      把t1里的數(shù)據(jù),按照 id%10 進行分組統(tǒng)計,并按m的結(jié)果排序后輸出。

      group by 的執(zhí)行計劃

      在Extra字段里面,我們可以看到三個信息:

      Using index,使用覆蓋索引,選擇了索引a,不需回表

      Using temporary,使用臨時表

      Using filesort,需要排序

      group by執(zhí)行流程:

      【阿里MySQL面試題】內(nèi)部臨時表

      創(chuàng)建內(nèi)存臨時表,表里有字段m、c,主鍵m

      掃描t1的索引a,依次取出葉子節(jié)點上的id值,計算id%10的結(jié)果,記為x;

      如果臨時表中沒有主鍵為x的行,就插入一個記錄(x,1)

      如果表中有主鍵為x的行,就將x這一行的c值加1

      遍歷完成后,根據(jù)m排序,得到結(jié)果集返回給客戶端

      圖中最后一步,對內(nèi)存臨時表的排序

      內(nèi)存臨時表排序流程

      臨時表的排序過程就是圖中虛線框

      如果你的需求并不需要對結(jié)果進行排序,那你可以在SQL語句末尾增加order by null,也就是改成:

      select id%10 as m, count(*) as c from t1 group by m order by null;

      這樣就跳過了最后排序,直接從臨時表取數(shù)據(jù)返回:

      group + order by null 的結(jié)果(內(nèi)存臨時表)

      由于t1中的id值從1開始,因此返回的結(jié)果集中第一行是id=1;掃描到id=10的時候才插入m=0

      由于臨時表只有10行,內(nèi)存可以放得下,因此全程只使用內(nèi)存臨時表。

      內(nèi)存臨時表的大小是有限制的,參數(shù)tmp_table_size就是控制這個內(nèi)存大小的,默認16M。

      若執(zhí)行

      把內(nèi)存臨時表的大小限制為最大1024K,并把語句改成id % 100,這樣返回結(jié)果里有100行數(shù)據(jù)。但這時內(nèi)存臨時表大小存不下這100行。

      此時會把內(nèi)存臨時表轉(zhuǎn)成磁盤臨時表,磁盤臨時表默認使用的引擎是InnoDB。 這時,返回的結(jié)果如圖:

      group + order by null 的結(jié)果(磁盤臨時表)

      若t1的數(shù)據(jù)量很大,可能該查詢需要的磁盤臨時表就會占用大量磁盤空間。

      優(yōu)化group by

      索引

      無論內(nèi)存臨時表還是磁盤臨時表,group by都需要構(gòu)造一個帶唯一索引的表,執(zhí)行代價較高。若表數(shù)據(jù)量較大,上面這個group by執(zhí)行就很慢。

      group by是統(tǒng)計不同的值出現(xiàn)的個數(shù)。但由于每行的id%100結(jié)果無序,所以需要有一個臨時表,來記錄并統(tǒng)計結(jié)果。

      若掃描過程可保證出現(xiàn)的數(shù)據(jù)有序,是不是簡單了?

      假設,現(xiàn)在有一個類似如下這么一個數(shù)據(jù)結(jié)構(gòu),我們來看看group by可以怎么做。

      group by算法優(yōu)化-有序輸入

      所以,若確保輸入數(shù)據(jù)有序,則計算group by時,就只需從左到右,順序掃描,依次累加:

      當碰到第一個1時,已經(jīng)知道累積了X個0,結(jié)果集里的第一行就是(0,X)

      當碰到第一個2的時候,已經(jīng)知道累積了Y個1,結(jié)果集里的第二行就是(1,Y);

      按照這個邏輯執(zhí)行的話,掃描到整個輸入的數(shù)據(jù)結(jié)束,即可拿到group by的結(jié)果,無需臨時表,也無需額外排序。

      InnoDB索引剛好滿足這個輸入有序。

      Mysql 5.7支持generated column,以實現(xiàn)列數(shù)據(jù)的關聯(lián)更新。

      創(chuàng)建一個列z,然后在z創(chuàng)建索引(≤5.6,也可以創(chuàng)建普通列和索引)。

      alter table t1 add column z int generated always as (id % 100), add index (z);

      這樣,索引z上的數(shù)據(jù)就有序了。上面的group by即可改成:

      select z, count(*) as c from t1 group by z;

      group by 優(yōu)化的執(zhí)行計劃

      從 Extra 可知該語句不再需要臨時表,也無需排序。

      直接排序

      若可以通過加索引完成group by自然很棒。但若碰上不適合創(chuàng)建索引的場景,還是要做排序。

      此時group by怎么優(yōu)化?

      若我們明知道,一個group by需要放到臨時表上的數(shù)據(jù)量很大,卻還是要“先放到內(nèi)存臨時表,插入一部分數(shù)據(jù)后,發(fā)現(xiàn)內(nèi)存臨時表不夠用了再轉(zhuǎn)成磁盤臨時表”,就很蠢了

      那這Mysql有無直接走磁盤臨時表的方法?

      有的。

      在group by加入SQL_BIG_RESULT這個提示(hint),就可以告訴優(yōu)化器:這個語句涉及的數(shù)據(jù)量很大,請直接用磁盤臨時表。

      MySQL的優(yōu)化器一看,磁盤臨時表是B+樹存儲,存儲效率不如數(shù)組。所以,既然你告訴我數(shù)據(jù)量很大,那從磁盤空間考慮,還是直接用數(shù)組存。

      因此,下面這個語句

      select SQL_BIG_RESULT id % 100 as m, count(*) as c from t1 group by m;

      執(zhí)行流程:

      初始化sort_buffer,確定放入一個整型字段,記為m

      掃描t1的索引a,依次取出里面的id值, 將 id%100值存入sort_buffer

      掃描完成后,對sort_buffer的字段m做排序(如果sort_buffer內(nèi)存不夠用,就會利用磁盤臨時文件輔助排序)

      排序完成后,就得到了一個有序數(shù)組。

      根據(jù)有序數(shù)組,得到數(shù)組里不同值,以及每個值的出現(xiàn)次數(shù)。

      使用 SQL_BIG_RESULT的執(zhí)行流程

      SQL_BIG_RESULT的explain

      該語句沒有使用臨時表,而直接用排序算法。

      所以

      MySQL什么時候會使用內(nèi)部臨時表?

      若語句執(zhí)行過程可以一邊讀數(shù)據(jù),一邊直接得到結(jié)果,就無需額外內(nèi)存,否則就需額外內(nèi)存,保存中間結(jié)果;

      join_buffer是無序數(shù)組

      sort_buffer是有序數(shù)組

      臨時表是二維表結(jié)構(gòu)

      若執(zhí)行邏輯需要用到二維表特性,就會優(yōu)先考慮使用臨時表。比如我們的例子中:

      union需要用到唯一索引約束

      group by還需要用到另外一個字段來存累積計數(shù)

      MySQL

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:圖文演示excel怎樣給單元格添加下拉列表
      下一篇:DevSecOps工具與平臺交互的橋梁 -- SARIF進階
      相關文章
      亚洲国产美国国产综合一区二区| 亚洲视频在线免费| 亚洲国产精品无码AAA片| 久久精品国产精品亚洲人人| 亚洲福利在线播放| 国产产在线精品亚洲AAVV| 亚洲国产精品ⅴa在线观看| 亚洲最大中文字幕无码网站| 亚洲天堂一区在线| 亚洲成人动漫在线观看| 亚洲最大在线视频| 亚洲黄色免费网址| 伊人久久综在合线亚洲2019| 亚洲国产日韩一区高清在线| 亚洲国产精品线在线观看| 久久精品国产亚洲AV网站| 久久亚洲精品人成综合网| 久久国产亚洲高清观看| 亚洲男人天堂影院| 亚洲va精品中文字幕| 亚洲娇小性色xxxx| 亚洲中文字幕久久精品无码A| 亚洲欧美日韩综合久久久| 亚洲AV成人无码网天堂| 激情婷婷成人亚洲综合| 亚洲福利中文字幕在线网址| 亚洲无线一二三四区手机| 亚洲愉拍99热成人精品热久久| 亚洲日产无码中文字幕| 亚洲AV无一区二区三区久久| 亚洲av女电影网| 亚洲福利电影一区二区?| 亚洲欧洲日本在线观看| 亚洲avav天堂av在线网毛片| 午夜亚洲av永久无码精品 | 国产亚洲成在线播放va| 亚洲精品无码久久久| 亚洲乱码中文字幕久久孕妇黑人| 亚洲春色在线视频| 亚洲国产理论片在线播放| 亚洲欧美日韩综合久久久|