萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL COUNT(*) 效能優化:為什麼 COUNT(*) 比 COUNT(col) 快?

核心結論

場景COUNT(*)COUNT(col)說明
無 WHERE 條件極快(MyISAM 直接讀表元資料;InnoDB 掃描最小索引)較慢(需掃描指定列,檢查 NULL)MyISAM 有行數快取,InnoDB 無
有 WHERE 條件掃描索引/表過濾掃描索引/表過濾 + 額外判斷 col IS NOT NULLCOUNT(*) 顯著快於 COUNT(col)

實測數據(Discuz! 7.2 cdb_posts 表,~7.9 萬筆符合條件)

mysql> SELECT COUNT(*) FROM cdb_posts WHERE fid = 604;
+----------+
| COUNT(*) |
+----------+
|    79000 |
+----------+
1 row in set (0.03 sec)

mysql> SELECT COUNT(tid) FROM cdb_posts WHERE fid = 604;
+------------+
| COUNT(tid) |
+------------+
|    79000   |
+------------+
1 row in set (0.33 sec)  -- 慢 10 倍!

mysql> SELECT COUNT(pid) FROM cdb_posts WHERE fid = 604;
+------------+
| COUNT(pid) |
+------------+
|    79000   |
+------------+
1 row in set (0.33 sec)

為什麼 COUNT(*) 更快?

  1. 語意差異: COUNT(*) 統計「符合條件的行數」;COUNT(col) 統計「col 非 NULL 的行數」。後者需額外讀取列值並判斷 NULL。
  2. 索引選擇: COUNT(*) 通常掃描最小的二級索引(或主鍵);COUNT(col) 若 col 無索引或非覆蓋索引,可能觸發回表或全表掃描。
  3. 優化器特例: InnoDB 針對 COUNT(*) 有專門優化路徑(選擇最小索引掃描),而 COUNT(col) 不享受此優化。

MyISAM 特殊行為

  • 無 WHERE 時:COUNT(*) 直接讀取表元資料 table_rows,**O(1)** 完成。
  • 有 WHERE 時:必須全表掃描,無捷徑。

優化總結(MyISAM / InnoDB 通用)

  1. 任何情況下首選 SELECT COUNT(*) FROM table。
  2. 極力避免 SELECT COUNT(*) FROM table WHERE col = 'val':若頻繁查詢,建議建立覆蓋索引 INDEX idx_col (col) 或 INDEX idx_col_other (col, other_col) 使掃描只走索引。
  3. 嚴禁 SELECT COUNT(col) FROM table WHERE col2 = 'val':雙重打擊(掃描 + 判 NULL),改寫為 COUNT(*) + 覆蓋索引。

彩蛋:WHERE 1 vs WHERE 1=1

WHERE 1 AND col='value'   -- 極快,優化器直接忽略常數條件
WHERE 1=1 AND col='value' -- 較慢,某些版本優化器未完全消除常數表達式

建議程式碼動態拼接 SQL 時統一用 WHERE 1 作為基礎條件。

進階替代方案:近似計數

  • EXPLAIN SELECT COUNT(*) ... 觀察 rows 估算值(InnoDB 統計資訊,誤差 ±10%)。
  • 資料倉庫場景:維護 summary_table 定時 INSERT ... ON DUPLICATE KEY UPDATE cnt=cnt+1 累加。
  • Redis 計數器:高併發寫入場景,寫入時 INCR key,讀取 GET key,定期同步落庫。
copyright © 萬盛學電腦網 all rights reserved