在亿级表上禁用 SELECT COUNT(*),应改用 information_schema.TABLES 的 TABLE_ROWS 估算值、EXPLAIN 的 rows 预估或分块 COUNT;长期方案是维护统计表或 Redis 近似计数。

直接用 COUNT(*) 会卡死,别试
在亿级表上执行 SELECT COUNT(*) FROM table_name,MySQL 往往要扫描全表(尤其是 InnoDB 引擎),可能耗时几分钟到几小时,期间还可能阻塞写入、拖慢整个库。这不是“慢一点”,而是生产环境不可接受的停摆风险。
真正能用的方案,得绕开精确计数这个陷阱:
- 如果业务允许误差 ±5%,优先看
TABLE_ROWS字段(来自information_schema.TABLES),它基于采样估算,毫秒级返回 - 如果必须精确且不能锁表,用
EXPLAIN查rows值——对 MyISAM 表准确;InnoDB 下只是粗略预估,但比全表扫快得多 - 千万别在从库上跑
COUNT(*)以为“不影响主库”——它照样占 I/O 和 Buffer Pool,可能拖垮从库同步
information_schema.TABLES 的 TABLE_ROWS 怎么查才靠谱
这个值是 InnoDB 在内存中维护的统计信息,不实时更新,但刷新频率高(通常每 10 秒或每次 ANALYZE TABLE 后)。对大多数监控、容量评估场景足够用。
查法很简单:
SELECT TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';
注意三点:
-
TABLE_ROWS对 InnoDB 是估算值,不是精确值;MyISAM 下才是真实行数 - 如果刚大批量删数据,统计可能滞后,手动触发
ANALYZE TABLE your_table可强制刷新(代价低,只读取索引页) - 权限问题:用户需有
SELECT权限访问information_schema.TABLES,某些云数据库默认关闭该权限
需要精确总数?分块 COUNT 比全表扫更可控
真要精确数又不能停服务,就得分片统计。核心思路是利用主键(最好是自增 ID)把大表切成小段,并发查,再求和。
例如按主键 id 分 100 段:
SELECT SUM(c) FROM ( SELECT COUNT(*) AS c FROM t WHERE id BETWEEN 1 AND 1000000 UNION ALL SELECT COUNT(*) AS c FROM t WHERE id BETWEEN 1000001 AND 2000000 -- ... 继续拆 ) AS tmp;
关键控制点:
- 分段区间必须连续无重叠,否则漏数或多算;建议用
SELECT MIN(id), MAX(id)先确认范围 - 每段大小建议 10 万~100 万行,太小并发压力大,太大单段仍慢
- 避免用
OFFSET分页(如LIMIT 1000000, 1000000),越往后越慢,本质还是全扫 - 务必加
WHERE id IS NOT NULL(如果主键允许 NULL),否则BETWEEN可能漏掉 NULL 行
长期看,别依赖实时总行数
亿级表本身就不该频繁查总行数——这说明设计上可能缺了轻量级统计机制。
更可持续的做法:
- 建一张
table_stats表,每次INSERT/DELETE后用触发器或应用层更新计数(注意并发冲突,用INSERT ... ON DUPLICATE KEY UPDATE) - 用 Redis 记录近似总数,写操作异步更新,允许短暂不一致,换来的是一致性极高的响应速度
- 对分区表,可查每个分区的
TABLE_ROWS再汇总,比扫全表快一个数量级
记住:行数统计不是功能需求,而是运维信号。真正重要的是变化趋势,不是那个精确到个位的数字。


















