MySQL count函數是用來獲取表中滿足一定條件的記錄數,常見用法有三種,count(*),count(1),count(field),這三種有什麼區別?在性能上有何差異?本文將通過測試案例詳細介紹和分析。
原文地址:mytecdb.com/blogDetail.php?id=81
三者有何區別:
count(field)不包含欄位值為NULL的記錄。count(*)包含NULL記錄。select(*)與select(1) 在InnoDB中性能沒有任何區別,處理方式相同。官方文檔描述如下: InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
1. 性能對比
通過案例來測試一下count(*),count(1),count(field)的性能差異,MySQL版本為5.7.19,測試表是一張sysbench生成的表,表名sbtest1,總記錄數2411645,如下:
測試SQL語句:
select count(*) from sbtest1;select count(1) from sbtest1;select count(id) from sbtest1;select count(k) from sbtest1;select count(c) from sbtest1;select count(pad) from sbtest1;
針對count(*)、count(1)和count(id),加了強制走主鍵的測試,如下:select count(*) from sbtest1 force index(primary);select count(1) from sbtest1 force index(primary);select count(id) from sbtest1 force index(primary);
另外對不同的測試SQL,收集了profile,發現主要耗時都在Sending data這個階段,記錄Sending data值。
匯總測試結果:
結果分析:
從以上測試結果來看,count(*)和count(1)性能基本一樣,默認走二級索引(k_1),性能最好,這也驗證了count(*)和count(1)在InnoDB內部處理方式一樣。count(id) 雖然也走二級索引(k_1),但是性能明顯低於count(*)和count(1),可能MySQL內部在處理count(*)和count(1)時做了額外的優化。強制走主鍵索引時,性能反而沒有走更小的二級索引好,InnoDB存儲引擎是索引組織表,行數據在主鍵索引的葉子節點上,走主鍵索引掃描時,處理的數據量比二級索引更多,所以性能不及二級索引。count(c)和count(pad)沒有走索引,性能最差,但是明顯count(pad)比count(c)好,因為pad欄位類型為char(60),小於欄位c的char(120),儘管兩者性能墊底,但是欄位小的性能相對更好些。2. count(*)改進
在5.7.18版本之前,InnoDB處理select count(*) 是通過掃描聚簇索引,來獲取總記錄數。從5.7.18版本開始,InnoDB掃描一個最小的可用的二級索引來獲取總記錄數,或者由SQL hint來告訴優化器使用哪個索引。如果二級索引不存在,InnoDB將會掃描聚簇索引。執行select count(*)在大部分場景下性能都不會太好,尤其是表記錄數特別大的情況下,索引數據不在buffer pool裡面,需要頻繁的讀磁碟,性能將更差。
3. count(*)優化思路
一種優化方法,是使用一個統計表來存儲表的記錄總數,在執行DML操作時,同時更新該統計表。這種方法適用於更新較少,讀較多的場景,而對於高並發寫操作,性能有很大影響,因為需要並發更新熱點記錄。如果業務對count數量的精度沒有太大要求,可使用show table status中的行數作為近似值。