MySQL Execution Plan--COUNT相關測試

来源:https://www.cnblogs.com/gaogao67/archive/2019/10/27/11749827.html
-Advertisement-
Play Games

COUNT全表記錄 在MySQL中,相同的SQL不同的存儲引擎執行計劃不同: 現有測試表TB101: 對於沒有WHERE條件的COUNT(*)/COUNT(1)/COUNT(ID)/COUNT(C1)的執行計劃為: 對於沒有WHERE條件的COUNT(C2)的執行計劃為: 可以發現,對於MyISAM ...


COUNT全表記錄

在MySQL中,相同的SQL不同的存儲引擎執行計劃不同:

對於MyISAM引擎,由於使用表鎖進行併發控制,同一時間點多個併發線程執行相同查詢獲得的結果相同,且MyISAM存儲引擎專門存儲表總記錄數,因此使用COUNT(*)查詢全表記錄時能直接返回。

而對於InnoDB存儲引擎,由於使用MVCC和行鎖進行併發控制,同一時間點多個併發線程執行相同查詢獲得的結果存在差異(每個回話的READVIEW不同),且沒有專門存儲表總記錄數,因此每次查詢都需要掃描全表或掃描某個索引的全部記錄。

For transactional storage engines such as InnoDB, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count.

InnoDB does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently, SELECT COUNT(*) statements only count rows visible to the current transaction.

Prior to MySQL 5.7.18, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index. As of MySQL 5.7.18, InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, the clustered index is scanned.

Processing SELECT COUNT(*) statements takes some time if index records are not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does. However, this method may not scale well in situations where thousands of concurrent transactions are initiating updates to the same counter table. If an approximate row count is sufficient, use SHOW TABLE STATUS.

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause.

This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL。

現有測試表TB101:

CREATE TABLE `tb101` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `C1` int(11) NOT NULL,
  `C2` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM AUTO_INCREMENT=140001 DEFAULT CHARSET=utf8

對於沒有WHERE條件的COUNT(*)/COUNT(1)/COUNT(ID)/COUNT(C1)的執行計劃為:

mysql> EXPLAIN SELECT COUNT(*) FROM TB101 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
   partitions: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: Select tables optimized away
1 row in set, 1 warning (0.00 sec)

對於沒有WHERE條件的COUNT(C2)的執行計劃為:

mysql> EXPLAIN SELECT COUNT(C2) FROM TB101 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: TB101
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 140000
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

可以發現,對於MyISAM存儲引擎,在沒有WHERE條件下,如果C1列為NOT NULL,則可以將COUNT(C1)與COUNT(*)和COUNT(1)做相同的處理。

針對上面的測試,對於InnoDB存儲引擎,在沒有WHERE條件下:

1、ID列為NOT NULL主鍵,COUNT(ID)和COUNT(1)或COUNT(*)的執行計劃相同,返回結果相同。

2、C1列為NOT NULL,COUNT(C1)和COUNT(1)或COUNT(*)的執行結果相同,但執行計劃不同。

 

 

COUNT(expr)異同

1、COUNT(1)和COUNT(*)等價,兩者在執行計劃和執行效率上完全相同。

個人推薦使用COUNT(1)替換COUNT(*),原因是簡單直觀,
他人推薦使用COUNT(1),原因是符合SQL92標準,阿裡巴巴Java開發手冊推薦。

 

2、COUNT(*)和COUNT(C1)不一定等價,兩者執行計劃和執行結果會存在差異。

COUNT(*):執行返回滿足WHERE條件的行數,不考慮NULL值問題
COUNT(C1): 執行返回滿足WHERE條件且C1不等於NULL的行數,不統計C1等於NULL的行。

換種理解思路:
對於MyISAM引擎表和InnoDB引擎表,無論是顯式主鍵還是因此ROWID,都要求非空唯一,每行記錄都肯定存在一個不為NULL的列(列組),因此計算COUNT(*)時不需要考慮NULL值問題。

 

一個有趣的擴展,如果C1為NOT NULL,那麼COUNT(C1)與COUNT(1)的返回結果相同,那麼MySQL會對此進行優化麽?

現有測試表結果如下:

CREATE TABLE `tb01` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `C1` int(11) NOT NULL,
  `C2` int(11) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `IDX_C2` (`C2`)
) ENGINE=InnoDB AUTO_INCREMENT=16384 DEFAULT CHARSET=utf8

查看COUNT(*)和COUNT(C1)的執行計劃:

mysql> EXPLAIN SELECT COUNT(*) FROM TB01 WHERE C2<100 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: TB01
   partitions: NULL
         type: range
possible_keys: IDX_C2
          key: IDX_C2
      key_len: 4
          ref: NULL
         rows: 99
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT COUNT(C1) FROM TB01 WHERE C2<100 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: TB01
   partitions: NULL
         type: range
possible_keys: IDX_C2
          key: IDX_C2
      key_len: 4
          ref: NULL
         rows: 99
     filtered: 100.00
        Extra: Using index condition
1 row in set, 1 warning (0.00 sec)

從上面執行計劃可以發現,在處理COUNT(*)時,僅需要使用IDX_C2即可完成查詢,因此Extra為Using index,而在處理COUNT(C1)時,需要使用IDX_C2進行過濾後再執行回表查詢,因此Extra為Using index condition。

針對上面的測試,MyISAM存儲引擎和InnoDB存儲引擎的測試結果相同。

 

COUNT(DISTINC ...)操作

MySQL官網解釋為:

COUNT(DISTINCT expr,[expr...])

Returns a count of the number of rows with different non-NULL expr values.

In MySQL, you can obtain the number of distinct expression combinations that do not contain NULL by giving a list of expressions. In standard SQL, you would have to do a concatenation of all expressions inside COUNT(DISTINCT ...).

在MySQL中允許執行:

SELECT COUNT(DISTINCT ID,C1) FROM TB02;

但不允許執行:

SELECT COUNT(ID,C1) FROM TB02;

 

總結

1、對於InnoDB和MyISAM存儲引擎,COUNT(1)和COUNT(*)在任何場景下都等價,執行性能和執行計劃相同。

2、在查詢全表記錄(沒有WHERE條件)時,對於MyISAM存儲引擎,存儲引擎存儲表總記錄數,無需掃描數據因此查詢可以很快返回,對於InnoDB存儲引擎,需要掃描全表或某個索引的全部記錄因此查詢可能比較耗時。

3、對於MyISAM存儲引擎,在沒有WHERE條件情況下,如果列C1為NOT NULL,那麼COUNT(C1)和COUNT(*)執行操作相同。

4、對於InnoDB存儲引擎,如果列C1為主鍵,那麼COUNT(C1)和COUNT(*)執行計劃和執行效率相同,如果C1為NOT NULL,那麼COUNT(C1)和COUNT(*)執行計劃和執行效率不一定相同,只有在查詢使用C1列上索引時才可能相同。

 

參考鏈接:

https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_count

https://dev.mysql.com/doc/refman/5.7/en/create-index.html

https://mp.weixin.qq.com/s/IOHvtel2KLNi-Ol4UBivbQ

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0x00 前言 最近使用 CFW 過程中使用 Fiddle Web Debug 設置 Store 的迴環代理的過程中發現無論是否使用代理,Store 都無法訪問網路的問題,在最下麵的提示中出現了 0x00000193 的錯誤代碼,在經過一系列的嘗試後發現可能是更改系統地區和DNS的問題。現在將解決該 ...
  • 關於WSL的,Win10 的Linux子系統如何安裝,就不贅述了,Win10商店裡就有,至於win7和win8.1想裝這個估計也不行,所以跳過。 最近處於好奇,也懶得弄VMware的虛擬機(那玩意兒占記憶體太多了),就整了個來玩下,興奮之餘也測試了一些命令行分屏工具,後來決定下來用byobu。 一、直 ...
  • 磁碟存儲 liunx 存儲方式 一切皆文件 設備類型: 1. 塊設備: block liunx 表現為b開頭文件,可以隨機讀寫,存在緩存,存取單位是“塊”如:磁碟 2. 字元設備: char 不存在緩存,存取單位是“字元”,如:鍵盤 設備文件: 關聯至一個設備驅動程式,進而能夠跟與之對應硬體設備進行 ...
  • [TOC] 1. 埠號 在同一臺主機或設備上,可能有多個進程同時在使用TCP或UDP協議,埠號的作用就是區分這些不同的進程,即每個進程使用各自不同的埠號。 對於TCP協議和UDP協議,埠號都是用unsigned short類型表示,即埠號的範圍為0 65535,這65536個埠號被分為3 ...
  • 一.centos的安裝 centos分為桌面版本和命令版本;在這裡我使用的是命令版本,因為這個版本比較小,推薦使用電腦配置不高的使用centos命令版本。 這裡選擇安裝程式光碟映像文件,文件就是centos7的iso文件。 虛擬機的名稱和位置自行設置; 虛擬機的記憶體根據自己電腦的配置設置,我這裡設置 ...
  • Linux操作系統 linux系統的主要構成: 1、硬體交互層:系統的底層,為內核層提供基礎,由管理外圍設備的軟體構成(外設包括終端控制器和存儲設備控制器) 2、內核層:系統核心,包括進程管理和文件子系統 3、系統介面層:實現操作系統命令,視窗系統和系統函數的調用功能,為應用層提供命令介面,圖形介面 ...
  • Linux常用命令 Linux常用的目錄操作命令 pwd 命令查看用戶的當前目錄 cd 改變當前路徑 cd 絕對路徑或者相對路徑 cd ~ 切換到當前用戶的宿主目錄 cd - 切換到上一次所在的目錄 cd .. 上一級目錄 ls 命令顯示文件或目錄信息 mkdir 創建目錄 -p 創建嵌套的多級目錄 ...
  • 一、單端、全差分、偽差分 此部分轉載https://www.cnblogs.com/alifpga/p/7976531.html 單端信號: 單端信號(single-end)是相對於差分信號而言的,單端輸入指信號有一個參考端和一個信號端構成,參考端一般為地端。 差分信號: 差分(Differenti ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...