myisam统计信息不准的根本原因是其不维护实时行数和索引基数,仅依赖.myi文件头的静态缓存值,且不随delete/update自动更新;analyze table虽可重算但耗时长、加锁严、易失败;可行方案为强制指定索引、用count(*)验证或迁移到innodb。

MyISAM 表在大数据量下统计信息不准,根本原因不是“采样不准”,而是 MyISAM 根本不维护实时行数和索引基数——它只靠 `.MYI` 文件头里一个缓存值,且该值在大量 DELETE 或 UPDATE 后不会自动修正。
MyISAM 的 CARDINALITY 为什么永远不准
MyISAM 的 CARDINALITY 值来自索引文件头的静态估算,不依赖采样,也不随数据变更动态更新。执行 ANALYZE TABLE 时,它会扫描整个索引树并重算,但这个操作:
- 在超大表(比如几十亿行)上极其耗时,可能卡住数小时
- 扫描期间加全局读锁,阻塞所有写入
- 即使成功,下次大批量 DELETE 后又立刻失效,因为 MyISAM 不记录 MVCC 版本,也不做增量维护
- SHOW INDEX FROM t 中看到的 CARDINALITY 常年停留在建表或上次 ANALYZE 时刻,与 SELECT COUNT(DISTINCT col) 结果差几个数量级
ANALYZE TABLE 对 MyISAM 大表的实际效果有限
对 MyISAM 执行 ANALYZE TABLE 确实能刷新统计信息,但生产环境几乎不可行:
- 它不是轻量操作,而是全索引遍历,IO 密集型任务
- 没有采样控制参数(不像 InnoDB 有 innodb_stats_persistent_sample_pages),只能全量或跳过
- 如果表上有未提交的写锁(哪怕只是个慢 INSERT),ANALYZE 会一直等待,状态显示 Repair by sorting 却无进展
- 执行后若立即查 information_schema.STATISTICS,发现 CARDINALITY 没变,大概率是被并发写入中断或磁盘满导致静默失败
真正可行的替代方案只有两个
别指望靠调参让 MyISAM 统计“准起来”,它的设计就决定了统计信息是弱一致的。必须换思路:
- 强制走指定索引:在查询中显式用 USE INDEX (idx_col) 或 FORCE INDEX (idx_col),绕过优化器基于错误 CARDINALITY 的判断。例如:SELECT * FROM t FORCE INDEX (idx_status) WHERE status = 'active'
- 改用 COUNT(*) 替代依赖统计的判断:当需要确认是否该走索引时,直接跑 SELECT COUNT(*) FROM t WHERE col = ?(配合覆盖索引),比信 CARDINALITY 更可靠;虽然慢,但结果确定
- 彻底迁移存储引擎:如果业务允许,把大表转成 InnoDB,启用 innodb_stats_persistent = ON 和合理采样页数,至少统计可维护、可预测;MyISAM 在 2026 年已不适用于任何需稳定查询性能的大数据场景
最常被忽略的一点:MyISAM 的 Cardinality 是单列独立估算,完全不支持多列联合分布建模。哪怕你给 (a,b) 建了联合索引,优化器也只会分别看 a 和 b 的 CARDINALITY,然后瞎猜组合选择率——这点在迁移到 InnoDB 后用 CREATE STATISTICS(MySQL 8.0.23+)才能补足。











