统计信息是什么
优化器靠索引基数、行数估算等统计信息来挑选执行计划。InnoDB 提供两种统计存储方式:持久化(innodb_stats_persistent,落盘到 mysql.innodb_table_stats 与 innodb_index_stats,8.0 默认开启)和非持久化(只存在内存,重启后重新采样)。统计信息失真,执行计划就会跑偏,表现为「同样的 SQL 昨天还很快,今天突然走全表」。
基本信息
| 持久化开关 | innodb_stats_persistent,8.0 默认 ON,统计落盘更稳定 |
|---|---|
| 自动重算 | innodb_stats_auto_recalc,默认 ON,表变更约 10% 后触发 |
| 采样页数 | innodb_stats_persistent_sample_pages,默认 20,越大越准也越慢 |
| 落盘表 | mysql.innodb_table_stats、mysql.innodb_index_stats |
| 手动重建 | ANALYZE TABLE 表名 |
典型使用场景
- 大批量导入后查询突然变慢,怀疑统计信息还停留在导入前
- 删除大量历史数据后计划仍按旧行数估算,选了错误的索引
- 上线前对大表做一次统计刷新,避免升级后计划抖动
- 用 EXPLAIN 的 rows 估算与实际行数对比,判断统计是否可信
- 重建索引或改表结构后校验基数,确认优化器能选到新索引
查看与重建
查看与重建
SHOW VARIABLES LIKE 'innodb_stats%';
ANALYZE TABLE appdb.orders; -- 重建该表统计信息
SHOW INDEX FROM appdb.orders; -- 看 Cardinality 估算值
SELECT table_name, n_rows, last_update
FROM mysql.innodb_table_stats WHERE table_name='orders';
SELECT index_name, stat_name, stat_value, sample_size
FROM mysql.innodb_index_stats WHERE table_name='orders';
EXPLAIN ANALYZE SELECT * FROM appdb.orders WHERE user_id=1; -- 8.0.18+ 看估算与实际差异统计字段读法
| 字段 | 含义 | 正常与异常 |
|---|---|---|
| n_rows | 表行数估算值 | 与实际相差一个数量级说明统计已过期 |
| sample_size | 采样页数 | 默认 20;调大更准但 ANALYZE 更慢 |
| last_update | 统计最后更新时间 | 必须晚于最近一次大批量变更才可信 |
| Cardinality | 索引唯一值估算 | 接近 0 或明显偏离实际会导致选错索引 |
| stat_name | 索引统计项名,如 n_diff_pfx01 | 前缀基数差异大时联合索引顺序可能要调整 |
自动重算有阈值也有延迟:只有表变更行数超过约 10% 才触发,而且是后台异步进行。一次性导入几千万行后立刻执行查询,往往等不到自动重算,需要手动 ANALYZE。
8.0 的 ANALYZE TABLE 走 Online DDL,默认 ALGORITHM=INPLACE、LOCK=NONE,不阻塞并发 DML,但采样会带来随机读 IO,仍建议放在低峰执行。
统计信息只是优化器的输入之一,不是万能药:如果 SQL 写法本身不合理(缺索引、函数包列、大范围排序),重建统计也救不回来,先用 EXPLAIN ANALYZE 看清真正的瓶颈。