MySQL中count(*)、count(主鍵id)、count(字段)和count(1)那種效率更高...

在select count(?) from t這樣的查詢語句里面,count(*)、count(主鍵id)、count(字段)和count(1)等不同用法的性能,有哪些差別。
需要注意的是,下面的討論還是基于InnoDB引擎的。
這里,首先你要弄清楚count()的語義。count()是一個聚合函數(shù),對于返回的結(jié)果集,一行行地判斷,如果count函數(shù)的參數(shù)不是NULL,累計值就加1,否則不加。最后返回累計值。
所以,count(*)、count(主鍵id)和count(1) 都表示返回滿足條件的結(jié)果集的總行數(shù);而count(字段),則表示返回滿足條件的數(shù)據(jù)行里面,參數(shù)“字段”不為NULL的總個數(shù)。
至于分析性能差別的時候,你可以記住這么幾個原則:
server層要什么就給什么;
InnoDB只給必要的值;
現(xiàn)在的優(yōu)化器只優(yōu)化了count(*)的語義為“取行數(shù)”,其他“顯而易見”的優(yōu)化并沒有做。
這是什么意思呢?接下來,我們就一個個地來看看。
對于count(主鍵id)來說,InnoDB引擎會遍歷整張表,把每一行的id值都取出來,返回給server層。server層拿到id后,判斷是不可能為空的,就按行累加。
對于count(1)來說,InnoDB引擎遍歷整張表,但不取值。server層對于返回的每一行,放一個數(shù)字“1”進(jìn)去,判斷是不可能為空的,按行累加。
單看這兩個用法的差別的話,你能對比出來,count(1)執(zhí)行得要比count(主鍵id)快。因為從引擎返回id會涉及到解析數(shù)據(jù)行,以及拷貝字段值的操作。
對于count(字段)來說:
如果這個“字段”是定義為not null的話,一行行地從記錄里面讀出這個字段,判斷不能為null,按行累加;
如果這個“字段”定義允許為null,那么執(zhí)行的時候,判斷到有可能是null,還要把值取出來再判斷一下,不是null才累加。
也就是前面的第一條原則,server層要什么字段,InnoDB就返回什么字段。
但是count(*)是例外,并不會把全部字段取出來,而是專門做了優(yōu)化,不取值。count(*)肯定不是null,按行累加。
看到這里,你一定會說,優(yōu)化器就不能自己判斷一下嗎,主鍵id肯定非空啊,為什么不能按照count(*)來處理,多么簡單的優(yōu)化啊。
當(dāng)然,MySQL專門針對這個語句進(jìn)行優(yōu)化,也不是不可以。但是這種需要專門優(yōu)化的情況太多了,而且MySQL已經(jīng)優(yōu)化過count(*)了,你直接使用這種用法就可以了。
所以結(jié)論是:
按照效率排序的話,count(字段)
我們提到了在不同引擎中count(*)的實現(xiàn)方式是不一樣的,也分析了用緩存系統(tǒng)來存儲計數(shù)值存在的問題。
其實,把計數(shù)放在Redis里面,不能夠保證計數(shù)和MySQL表里的數(shù)據(jù)精確一致的原因,是這兩個不同的存儲構(gòu)成的系統(tǒng),不支持分布式事務(wù),無法拿到精確一致的視圖。而把計數(shù)值也放在MySQL中,就解決了一致性視圖的問題。
InnoDB引擎支持事務(wù),我們利用好事務(wù)的原子性和隔離性,就可以簡化在業(yè)務(wù)開發(fā)時的邏輯。這也是InnoDB引擎?zhèn)涫芮嗖A的原因之一。
