文章目錄
- count函數的語義
- count(主鍵)
- count(1)
- count(*)
- count(字段)
- 替代方案
- explain或者show table status
- 中間表或者其他數據庫計數
以下分析都是基于 select count(?) from table 這個語句來分析,不帶過濾條件。
count函數的語義
count() 是一個聚合函數,函數的參數不僅可以是字段名,也可以是其他任意表達式,該函數作用是統計符合查詢條件的記錄中,函數指定的參數不為 NULL 的記錄有多少個。
在通過 count 函數統計有多少個記錄時,MySQL 的 server 層會維護一個名叫 count 的變量。
server 層會循環向 InnoDB 讀取一條記錄,如果 count 函數指定的參數不為 NULL,那么就會將變量 count 加 1,直到符合查詢的全部記錄被讀完,就退出循環。最后將 count 變量的值發送給客戶端。
count(主鍵)
在通過 count 函數統計有多少個記錄時,MySQL 的 server 層會維護一個名叫 count 的變量。
server 層會循環向 InnoDB 讀取一條記錄,如果 count 函數指定的參數不為 NULL,那么就會將變量 count 加 1,直到符合查詢的全部記錄被讀完,就退出循環。最后將 count 變量的值發送給客戶端。
如果表里只有主鍵索引,沒有二級索引時
那么,InnoDB 循環遍歷聚簇索引,將讀取到的記錄返回給 server 層,然后讀取記錄中的 id 值,根據 id 值判斷是否為 NULL,如果不為 NULL,就將 count 變量加 1。
如果表里有二級索引時
InnoDB 循環遍歷的對象就不是聚簇索引,而是二級索引。
這是因為相同數量的二級索引記錄可以比聚簇索引記錄占用更少的存儲空間,所以二級索引樹比聚簇索引樹小,這樣遍歷二級索引的 I/O 成本比遍歷聚簇索引的 I/O 成本小,因此「優化器」優先選擇的是二級索引。
count(1)
如果表里只有主鍵索引,沒有二級索引時
InnoDB 循環遍歷聚簇索引(主鍵索引),將讀取到的記錄返回給 server 層,但是不會讀取記錄中的任何字段的值,因為 count 函數的參數是 1,不是字段,所以不需要讀取記錄中的字段值。參數 1 很明顯并不是 NULL,因此 server 層每從 InnoDB 讀取到一條記錄,就將 count 變量加 1。
可以看到,count(1) 相比 count(主鍵字段) 少一個步驟,就是不需要讀取記錄中的字段值,所以通常會說 count(1) 執行效率會比 count(主鍵字段) 高一點。
*** 如果表里有二級索引時***
InnoDB 循環遍歷的對象就二級索引。
count(*)
當你使用 count() 時,MySQL 會將 * 參數轉化為參數 0 來處理,也就是說 count() 其實等于 count(0)。
所以,count(*) 執行過程跟 count(1) 執行過程基本一樣的,性能沒有什么差異。
而且 MySQL 會對 count(*) 和 count(1) 有個優化,如果有多個二級索引的時候,優化器會使用key_len 最小的二級索引進行掃描。
只有當沒有二級索引的時候,才會采用主鍵索引來進行統計。
count(字段)
count(字段) 的執行效率相比前面的 count(1)、 count(*)、 count(主鍵字段) 執行效率是最差的。主要原因是因為它是個全表掃描操作。
替代方案
explain或者show table status
如果對于count值不是要求很精確可以通過執行 explain或者show table status來獲取值
中間表或者其他數據庫計數
如果要求比較精確可以在寫數據后通過中間表或者其他數據庫去記錄當前數量。