information_schema.tables.table_rows对innodb不准,因其仅为采样估算值(默认仅20页),非精确计数,误差可达40%–50%;analyze table不直接更新该字段,受information_schema_stats_expiry缓存控制,默认24小时才刷新;精确行数必须用select count(*)。

为什么information_schema.tables.TABLE_ROWS对InnoDB表不准
因为InnoDB根本没存精确行数,TABLE_ROWS只是优化器用的采样估算值,不是真实计数。它基于少量随机页(默认innodb_stats_sample_pages=20)推算全表,数据倾斜或大表时误差常达40%–50%,删了10万行后还显示旧值也完全正常。
ANALYZE TABLE为什么有时也刷不出新值
执行ANALYZE TABLE只更新InnoDB内部的统计信息,不直接改information_schema.tables的返回结果——后者受information_schema_stats_expiry缓存控制,默认24小时才刷新。即使刚跑完ANALYZE,查TABLE_ROWS仍可能返回过期缓存值。
- 临时解决:运行
SET GLOBAL information_schema_stats_expiry = 0禁用缓存 - 但注意:
ANALYZE TABLE本身不持久化统计(若innodb_stats_persistent=OFF),服务重启后又回退 - 对分区表、
AUTO_INCREMENT等字段,ANALYZE也不保证同步更新
真正需要精确行数时,必须用COUNT(*)
只有SELECT COUNT(*) FROM `table_name`走真实数据扫描,结果才可靠。但要注意:
- 大表(千万级以上)会锁表、拖慢写入,别在高峰期跑
- 动态拼SQL时,表名含连字符或空格必须用反引号包裹,漏掉就语法报错
- 生成语句得显式带上
table_schema,否则跨库执行会失败 - 执行用户需有所有目标表的
SELECT权限,否则遇到第一个无权表就中断,报错ERROR 1142 (42000): SELECT command denied to user
MySQL 8.0+里查information_schema更危险
8.0默认启用derived_merge=ON,一旦在JOIN或子查询里用INFORMATION_SCHEMA.TABLES,优化器容易把它拉进嵌套循环,导致元数据反复扫描——单条SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS卡几秒很常见。
别信EXPLAIN里的rows,那是假的;真正要看的是实际执行时间是否随库中表数量线性增长。最稳的做法是客户端先查出结果再本地处理,而不是让MySQL服务器边查边联。











