網上關於 COUNT(*) 與 COUNT(col) 效能的說法眾說紛紜。本文通過實測數據(7.9 萬行測試表),揭示 無 WHERE 時等價、有 WHERE 時 COUNT(*) 完勝 COUNT(col) 的鐵律,並給出生產環境優化三大原則。
| SQL | 耗時 | 結果 |
|---|---|---|
SELECT COUNT(*) FROM cdb_posts WHERE fid = 604; | 0.03 秒 | 79,000 |
SELECT COUNT(tid) FROM cdb_posts WHERE fid = 604; | 0.33 秒 | 79,000 |
SELECT COUNT(pid) FROM cdb_posts WHERE fid = 604; | 0.33 秒 | 79,000 |
結論:帶 WHERE 時,COUNT(*) **快 10 倍以上**。
COUNT(*) 直接讀取元數據,O(1)WHERE 時,COUNT(*)、COUNT(col)、COUNT(1) **執行計劃一致**,效能等價COUNT(*):優化器傾向選擇**最小的二級索引**掃描(如單列索引),僅讀取索引葉子節點,極快COUNT(col):若 col 無索引或非覆蓋索引,需**回表讀取聚簇索引**判斷 col IS NOT NULL,IO 放大 10 倍+COUNT(1) / COUNT(*):語義完全等價,MySQL 內部統一重寫為 COUNT(*)| 函數 | 統計對象 | NULL 處理 |
|---|---|---|
COUNT(*) | 行數 | 包含 NULL 行 |
COUNT(col) | 該欄非 NULL 個數 | 忽略 NULL |
COUNT(DISTINCT col) | 該欄非 NULL 去重個數 | 忽略 NULL |
SELECT COUNT(*) FROM table:語義清晰、優化器最友好、執行計劃最優SELECT COUNT(*) FROM table WHERE col = 'val':若極高頻,考慮計數表(觸發器/應用層維護)、Redis 計數器、物化視圖異步刷新SELECT COUNT(col) FROM table WHERE col2 = 'val':除非 col 恰好在覆蓋索引中,否則必回表,效能災難EXPLAIN SELECT * FROM table 中 rows 欄位、information_schema.TABLES.TABLE_ROWS(InnoDB 為估算值)table_counts(table_name, cnt),寫入時同步增減(注意事務一致性)COUNT(*) 非慢,慢在「無索引覆蓋的 WHERE」與「錯誤使用 COUNT(col)」。牢記「首選 COUNT(*)、WHERE 必上覆蓋索引、高頻計數異步化」三大原則,即可在生產環境游刃有餘。