你知道MySQL中 group by 怎么優化嗎

更好的閱讀體驗,請點擊 YinKai 's Blog。

? 在 MySQL 中 group by 用于按照一個或多個列對結果集進行分組。在討論 group by 怎么優化之前,我們先來看看 group by 的執行流程,這樣我們才能對癥下藥。

group by 執行流程

? 我們先用下面的 sql 語句創建一個表,并輸入一些數據,模擬真實環境。

create table t1(id int primary key, a int, b int, index(a));
delimiter ;;
create procedure idata()
begindeclare i int;set i=1;while(i<=1000)doinsert into t1 values(i, i, i);set i=i+1;end while;
end;;
delimiter ;
call idata();

? 然后我們執行下面的語句:

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

? 這個語句的邏輯是把表 t1 里的數據,按照 id%10 進行分組統計,并按照 m 的結果排序后輸出。它的 explain 結果如下:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 在 Extra 字段我們可以看到三個信息:

  • Using index:表示這個語句使用了索引覆蓋,選擇了索引 a,不需要回表
  • Using temporary,表示使用了臨時表
  • Using filesort,表示需要排序

? 這個語句的執行過程是:

  1. 創建內存臨時表,表里有兩個字段 m 和 c,主鍵是 m
  2. 掃描表 t1 的索引 a,依次取出葉子結點上的 id 值,計算 id%10 的結果,記為 x;
    • 如果臨時表中沒有主鍵為 x 的行,就插入一個記錄 (x, 1);
    • 如果臨時表中有主鍵為 x 的行,就將 x 這一行的 c 值加 1;;
  3. 遍歷完成后,再根據字段 m 做排序,得到的結果返回給客戶端。

? 這個流程的執行圖如下:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 圖中最后一步,對內存臨時表的排序過程如下:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 上面的例子,由于臨時表只有 10 行,內存可以放得下,因此只使用了內存臨時表。但內存臨時表是有大小限制的,可以通過參數 tmp_table_size 修改,默認是 16M。

? 如果我執行下面這個語句序列:

set tmp_table_size=1024;
select id%100 as m, count(*) as c from t1 group by m order by null limit 10;

? 把內存臨時表的大小限制為最大 1024 字節,并把語句改成 id % 100,這樣返回結果里有 100 行數據。但是,這時的內存臨時表大小不夠存下這 100 行數據,也就是說,執行過程中會發現內存臨時表大小到達了上限(1024 字節)。

? 那么這時候就會把內存臨時表轉成磁盤臨時表,磁盤臨時表默認使用的是 InnoDB,結果如下:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 如果這個表 t1 的數據量很大,很可能這個查詢的磁盤臨時表需要用到很大的磁盤空間,查詢生成大型臨時表,占用大量磁盤空間可能導致查詢變慢,引起磁盤空間不足,影響系統穩定性。

? 因此,這就是為什么我們需要去優化 group_by 的原因。

group by 優化方法——索引

? 要解決 group by 的優化問題,我們需要從根本上去解決問題,即執行 group by 語句創建的臨時表。

? group by 的語義邏輯,是統計不同的值出現的個數。但是由于每一行的 id%100 的結果是無序的,所以我們就需要有一個臨時表,來記錄并統計結果。

? 那我們假想出現的數據都是有序的,看看 group by 會怎么做。

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 如果我們可以確保輸入的數據都是有序的,那么計算 group by 的時候,就只需要從左往右順序掃描,依次累加,即:

  • 當碰到第一個 1 的時候,已經知道累積了 X 個 0,結果集里的第一行就是 (0,X);
  • 當碰到第一個 2 的時候,已經知道累積了 Y 個 1,結果集里的第二行就是 (1,Y);

? 按照這個邏輯執行的話,掃描到整個輸入的數據結束,就可以拿到 group by 的結果,不需要臨時表,也不需要再額外排序。

? 不難想到,InnoDB 的索引就可以滿足這個輸入有序的條件。

? 我們可以 MySQL5.7 版本的 generated column 機制,用來實現列數據的關聯更新。你可以用下面的方法創建一個列 z,然后在 z 列上創建一個索引

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

? 這樣,索引 z 上的數據就是類似上圖那樣有序的了。上面的 group by 語句就可以改成:

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

? 優化后的 group by 語句的 explain 結果,如下圖所示:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 從 Extra 字段可以看出,這個語句的執行不再需要臨時表了,也不需要排序了。

group by 優化方法 – 直接排序

? 如果能使用創建索引的方式來優化那再好不過,萬一要是遇到了不適合創建索引的創建,我們又該怎么辦呢?

? 當我們明確知道一個 GROUP BY 語句中涉及的數據量非常大,而 MySQL 的默認行為是首先嘗試在內存中創建臨時表,然后在內存不足的情況下將其轉為磁盤臨時表,我們可能希望直接走磁盤臨時表的方式,以避免不必要的內存消耗。MySQL 提供了一個查詢提示 SQL_BIG_RESULT 來實現這一點。

具體而言,你可以在 GROUP BY 語句中加入 SQL_BIG_RESULT 提示,告訴優化器:由于數據量較大,請直接使用磁盤臨時表。這樣,優化器會考慮在磁盤上存儲臨時表,而不是首先嘗試在內存中完成這一操作。

以下是使用 SQL_BIG_RESULT 提示的一個示例:

SELECT SQL_BIG_RESULT id % 100 AS m, COUNT(*) AS c FROM t1 GROUP BY m;

這個查詢的執行流程可以描述為:

  1. 初始化 sort_buffer,確定放入一個整型字段 m
  2. 掃描表 t1 的索引 a,依次取出其中的 id 值,將 id % 100 的值存入 sort_buffer 中。
  3. 掃描完成后,對 sort_buffer 的字段 m 進行排序。如果 sort_buffer 內存不足,將會利用磁盤臨時文件輔助排序。
  4. 排序完成后,得到一個有序數組。
  5. 根據有序數組,獲取數組中的不同值以及每個值的出現次數。

? 這樣,通過使用 SQL_BIG_RESULT 提示,你可以明確告知 MySQL 優化器,考慮到數據量很大,直接使用磁盤臨時表。

? 執行 explain 的結果如下圖:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

? 從 Extra 字段可以看到,這個語句的執行沒有再使用臨時表,而是直接用了排序算法。

小結

? 現在我們來總結一下使用 group by 需要注意的一些點:

  1. 如果對 group by 語句的結果沒有排序要求,要在語句后面加 order by null;
  2. 使用 group by 的時候,盡可能用上表的索引,確認的方法是查看 explain 結果里有沒有 Using temporary 和 Using filesort;
  3. 如果 gruop by 需要統計的數據量不大,盡量只使用內存臨時表;也可以通過適當調大 tmp_table_size 參數避免使用磁盤臨時表;
  4. 如果數據量實在太大,使用 SQL_BIG_RESULT 這個提示,來告訴優化器直接使用排序算法得到 group by 的結果。

? 最后,我們來看一看文章開頭的問題:

? MySQL中 group by 怎么優化?

  1. 盡可能保證 group by 語句上存在索引,這樣有助于數據引擎更有效地執行分組操作,我們可以通過查看執行計劃 explain 的輸出,來確認是否使用了索引。
  2. 如果內存臨時表足夠容納 group by 的結果集的話,可以適當增加內存臨時表的參數大小,使 MySQL 更傾向于使用內存臨時表,因為內存的讀寫速度遠高于磁盤,這樣可以顯著提高查詢性能。
  3. 如果 GROUP BY 的字段是通過某個表達式計算而來,考慮使用生成列,并在生成列上創建索引。
  4. 在 GROUP BY 的數據量非常大且無法通過其他手段優化時,可以使用 SQL_BIG_RESULT 提示,**讓優化器直接使用排序算法而不是創建臨時表,**這樣 MySQL 就可以直接通過遍歷數組獲取我們想要的結果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/211567.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/211567.shtml
英文地址,請注明出處:http://en.pswp.cn/news/211567.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Ubuntu 18.04使用Qemu和GDB搭建運行內核的環境

安裝busybox 參考博客&#xff1a; 使用GDBQEMU調試Linux內核環境搭建 一文教你如何使用GDBQemu調試Linux內核 ubuntu22.04搭建qemu環境測試內核 交叉編譯busybox 編譯busybox出現Library m is needed, can’t exclude it (yet)的解釋 S3C2440 制作最新busybox文件系統 https:…

block-recurrent-transformer-pytorch 學習筆記

目錄 有依賴項1&#xff1a; 沒有依賴項&#xff0c;沒有使用例子 沒有依賴項2&#xff1a; 有依賴項1&#xff1a; GitHub - dashstander/block-recurrent-transformer: Pytorch implementation of "Block Recurrent Transformers" (Hutchins & Schlag et a…

gd32和stm32的區別

gd32和stm32的區別 現在的市場上有很多種不同類型的微控制器&#xff0c;其中比較常見的有兩種&#xff0c;即gd32和stm32。兩種微控制器都是中國和歐洲的兩個公司分別推出的&#xff0c;但是它們之間有很多區別&#xff0c;本文將會深入探討這些區別。 1.起源和歷史 gd32是…

2024年網絡安全競賽-Web安全應用

Web安全應用 (一)拓撲圖 任務環境說明: 1.獲取PHP的版本號作為Flag值提交;(例如:5.2.14) 2.獲取MySQL數據庫的版本號作為Flag值提交;(例如:5.0.22) 3.獲取系統的內核版本號作為Flag值提交;(例如:2.6.18) 4.獲取網站后臺管理員admin用戶的密碼作為Flag值提交…

udp多播組播

import socket ,struct,time# 組播地址和端口號 MCAST_GRP 239.0.0.1 MCAST_PORT 8888 # 創建UDP socket對象 sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # 綁定socket對象到本地端口號 # sock.bind((MCAST_GRP, MCAST_PORT)) …

【4】PyQt輸入框

1. 單行文本輸入框 QLineEdit控件可以輸入單行文本 from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout from PyQt5.QtCore import * from PyQt5.QtGui import QIcon import sysdef init_widget(w: QWidget):# 修改窗口標題w.setWindowTitle(單行輸…

前端面試——CSS面經(持續更新)

1. CSS選擇器及其優先級 !important > 行內樣式 > id選擇器 > 類/偽類/屬性選擇器 > 標簽/偽元素選擇器 > 子/后臺選擇器 > *通配符 2. 重排和重繪是什么&#xff1f;瀏覽器的渲染機制是什么&#xff1f; 重排(回流)&#xff1a;當增加或刪除dom節點&…

【面試經典150 | 二叉樹】從中序與后序遍歷序列構造二叉樹

文章目錄 寫在前面Tag題目來源題目解讀解題思路方法一&#xff1a;遞歸 寫在最后 寫在前面 本專欄專注于分析與講解【面試經典150】算法&#xff0c;兩到三天更新一篇文章&#xff0c;歡迎催更…… 專欄內容以分析題目為主&#xff0c;并附帶一些對于本題涉及到的數據結構等內容…

Android : Room 數據庫的基本用法 —簡單應用

1.Room介紹&#xff1a; Android Room 是 Android 官方提供的一個持久性庫&#xff0c;用于在 Android 應用程序中管理數據庫。它提供了一個簡單的 API 層&#xff0c;使得使用 SQLite 數據庫變得更加容易和方便。 以下是 Android Room 的主要特點&#xff1a; 對象關系映射…

9.MySQL 索引

目錄 ???????概述 概念&#xff1a; 單列索引 普通索引 創建索引 查看索引 刪除索引 唯一索引 創建唯一索引 刪除唯一索引 主鍵索引 組合索引 創建索引 全文索引 概述 使用全文索引 空間索引 內部原理 相關算法&#xff1a; hash算法 二叉樹算法 …

Spring基于XML文件配置AOP

AOP AOP&#xff0c;面向切面編程&#xff0c;是對面向對象編程OOP的升華。OOP是縱向對一個事物的抽象&#xff0c;一個對象包括靜態的屬性信息&#xff0c;包括動態的方法信息等。而AOP是橫向的對不同事物的抽象&#xff0c;屬性與屬性、方法與方法、對象與對象都可以組成一個…

12.10多種編碼方式,編碼方案選擇策略(遞歸級聯),PDE,RLE代碼

作者如何選擇和設計編碼方案&#xff0c;以實現高效的解壓縮和高壓縮比&#xff1f;BtrBlocks是否適用于所有類型的數據&#xff1f; 選擇和設計編碼方案&#xff1a; 結合多種高效編碼方案&#xff1a;BtrBlocks 通過選擇一組針對不同數據分布的高效編碼方案&#xff0c;實現…

js判斷是否對象自身為空

文章目錄 一、前言二、JSON.stringify三、for in 配合 hasOwnProperty四、Object.keys五、Object.getOwnPropertyNames六、Object.getOwnPropertyNames 結合 Object.getOwnPropertySymbols七、Reflect.ownKeys八、最后 一、前言 如何判斷一個對象為空&#xff1f; 先上結論&a…

MySql復習筆記03(小滴課堂) 事務,視圖,觸發器,存儲過程

mysql 必備核心知識之事務的詳細解析&#xff1a; 創建一個數據庫表&#xff1a; 添加數據并開啟事務。 添加數據并查詢。 登錄另一臺服務器發現查不到這個表中的數據。 這是因為事務開啟了&#xff0c;但是沒有提交&#xff0c;只是把數據存到了內存中&#xff0c;還沒有寫入…

以為回調函數是同步的(js的問題)

回調函數可以用來處理 JavaScript 的異步操作&#xff0c;但是選用 Promise、async/await 更好&#xff0c;因為多重回調函數會導致回調地獄。 回調函數不是**同步的**&#xff0c;它是延時操作執行完畢后會被調用的一個函數。 比如全局方法 "setTimeout" &#xf…

CString 的 Replace 函數

Replace 使用測試 CString mSectNameNew L"槽a*b*c*d";CString mSectNameNew2 L"Ca*b*c*d";CString mSectNameNew3 L"[a*b*c*d";mSectNameNew.Replace(_T("M"), _T("C")); // 不會替換mSectNameNew.Re…

JOSEF 沖擊繼電器 ZC-23A DC48V 柜內安裝,板前帶座

系列型號 ZC-23沖擊繼電器&#xff1b;ZC-23A沖擊繼電器&#xff1b; ZC-23B沖擊繼電器 一、用途 沖擊繼電器ZC-23A DC48V 柜內安裝板前帶座 (以下簡稱繼電器)&#xff0c;廣泛用于直流操作的繼電器保護及自動控制回路中&#xff0c;作為集中控制信號元件。 二、主要技術參…

C#動態調用C++DLL中的函數

DLL中導出的函數 typedef void (*HQ_MSG_CALLBACK)(void *h, int nMsg, int nMsgType, int nReqNo, const char *szData, int nSize); void SetMsgFunc(void *h, HQ_MSG_CALLBACK pmsgCallBack);C#動態調用上述函數 public delegate void CALLBACK(IntPtr h, int nMsg, int n…

信息處理技術員

目錄 信息處理技術員工作內容 信息處理技術員崗位面試試題舉例 信息處理技術員考試 信息處理技術員工作內容 信息處理技術員是負責處理和管理信息系統的專業人員。他們的主要工作內容包括以下幾個方面&#xff1a; 1.系統維護和管理&#xff1a;信息處理技術員負責維護和管…