不用的count用法

討論背景

在select count(?) from t 這樣的查詢語句裏面,count(*), count(主鍵),count(字段)和count(1)在性能上有差別。
今天就詳細記錄一下,這幾種用法的差別。
需要注意的是,此處的討論是基於MySQL的InnoDB引擎的。

這裏,首先要弄清楚count的語義。count()是一個聚合函數,對於返回的結果集合,一行行地判斷,如果count函數的參數不是NULL, 累計值就加1,否則不加。最後返回累計值。
所以,count(*), count(主鍵)和count(1)都表示返回滿足條件的結果集的總行數;而count(字段),則表示返回滿足條件的數據行裏面,參數“字段”爲是NULL的總個數。

分析性能差異時的原則

分析性能差別的時候,記住幾個原則:

  1. server 層要什麼就給什麼;
  2. InnoDB只給必要的值;
  3. 現在的優化器只優化了count(*)的語義爲“取行數”,其他“顯而易見”的優化並沒有做。

count(主鍵)和count(1)的性能分析

對於count(主鍵)來說,InnoDB引擎會遍歷整張表,把每一行的id值都取出來,返回給server層。server 層拿到id後,判斷是不可能爲空的,就按行累加。
對於count(1)來說,InnoDB引擎遍歷整張表,但不取值。server 層對於返回的每一行,放一個數據“1”進去,判斷是不可能爲空的,按行累加。

單看這兩個用法的差別的話,你能對比出來,count(1)執行得要比count(主鍵)快。因爲從引擎返回id會涉及到解析數據行,以及拷貝字段值的操作。

count(字段)的性能分析

對於count(字段)來說:

  1. 如果這個“字段”是定義爲Not null的話,一行行地從記錄裏面讀出這個字段,判斷不能爲null, 按行累加;
  2. 如果這個“字段”定義爲允許爲Null,那麼執行的時候,判斷到有可能是Null, 還要把值取出來再判斷一下,不是Null 才累加。
    也就是前面的第一條原則:server層要什麼字段,InnoDB就返回什麼字段。

專門優化過的的count(*)

但是count(*)是例外,並不會把全部字段取出來,而是專門做了優化,不取值。
count(*) 肯定不是null, 按行累加。
看到這裏,我們會想,優化器就不能自己判斷一下嗎?
主鍵肯定非空啊,爲什麼不能按照count(*)來處理,多麼簡單的優化啊。
當然,MySQL 專業針對這個語句進行優化,也是可以的(但實際上並沒有針對這個語句進行優化)。但是這種需要專門優化的情況太多了,而且MySQL 已經優化爲count(*)了,你就直接使用這種用法就可以了。

結論

所以最終的結論:按照效率從低到高排序的話,count(字段) < count(主鍵) < count(1) < count(*)
所以取表的行數的話,建議請儘量使用count(*)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章