首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL SQL_CALC_FOUND_ROWS vs COUNT(*) 性能實測:覆蓋索引是關鍵

     編輯:MySQL 綜合教學

前言

分頁查詢總行數時,常用 COUNT(*) 或 SQL_CALC_FOUND_ROWS。哪個更快?本文通過 100 萬行實測,揭示覆蓋索引對兩者性能的決定性影響。

一、實驗環境與數據準備

-- 表結構 A:覆蓋索引 (bb, aa)
CREATE TABLE ben (
  aa INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  bb INT UNSIGNED NOT NULL,
  cc VARCHAR(100) NOT NULL,
  KEY bar (bb, aa)  -- 覆蓋索引:查詢 bb, aa 只需掃描索引
) ENGINE=MyISAM;

-- 表結構 B:非覆蓋索引 (bb)
CREATE TABLE ben (
  aa INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  bb INT UNSIGNED NOT NULL,
  cc VARCHAR(100) NOT NULL,
  KEY bar (bb)  -- 單列索引,查詢 aa 需回表
) ENGINE=MyISAM;

-- 插入 100 萬行,bb 取值 0/1 均勻分布

二、測試代碼與結果

-- 方案 1:COUNT(*) + 分頁查詢
SELECT COUNT(*) FROM ben WHERE bb=1;
SELECT aa FROM ben WHERE bb=1 LIMIT 100, 10;

-- 方案 2:SQL_CALC_FOUND_ROWS + FOUND_ROWS()
SELECT SQL_CALC_FOUND_ROWS aa FROM ben WHERE bb=1 LIMIT 100, 10;
SELECT FOUND_ROWS();
索引類型COUNT(*) 方案 (秒)SQL_CALC_FOUND_ROWS 方案 (秒)結論
覆蓋索引0.01820.0175SQL_CALC_FOUND_ROWS 略快
非覆蓋索引0.01770.2076COUNT(*) 完勝(快 10 倍)

三、深度解析

3.1 為什麼覆蓋索引下 SQL_CALC_FOUND_ROWS 更快?

  • SQL_CALC_FOUND_ROWS 只需一次全索引掃描:掃描覆蓋索引 (bb, aa) 同時完成「計數」與「取前 110 行主鍵」;
  • COUNT(*) 需單獨掃描一次索引計數,分頁查詢再掃一次,共兩次掃描。

3.2 為什麼非覆蓋索引下 COUNT(*) 碾壓?

  • SQL_CALC_FOUND_ROWS 必須回表獲取 aa(主鍵),100 萬行回表開銷巨大;
  • COUNT(*) 在 MyISAM 中極快(直接讀取表元數據行數),InnoDB 需掃描聚簇索引但仍遠快於回表。

四、引擎差異補充:MyISAM vs InnoDB

特性MyISAMInnoDB
COUNT(*) 無 WHEREO(1) 讀元數據O(N) 掃描聚簇索引
事務支持無ACID
鎖粒度表鎖行鎖
崩壞恢復慢,易損壞Redo Log 快速恢復

作者觀點:讀多寫少、需頻繁 COUNT(*)、依賴文件級備份恢復的場景,MyISAM 仍有優勢;核心交易、需事務/行鎖/高可用場景,InnoDB 不二之選。

五、最佳實踐建議

  1. 優先建立覆蓋索引:分頁查詢列 + WHERE 列組合,使 EXPLAIN 出現 Using index;
  2. 覆蓋索引場景:優先用 SQL_CALC_FOUND_ROWS(單次掃描);
  3. 非覆蓋索引場景:用 COUNT(*) 分離計數,或改用游標分頁避免總數查詢;
  4. InnoDB 表避免無 WHERE 的 COUNT(*),考慮維護計數表或 Redis 緩存總數。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved