mysql执行计划中的统计信息是如何采集的_mysql Analyze Table原理

7次阅读

mysql统计信息是抽样估算值而非精确计算,基于随机采样数据页推算基数与选择性;采样页数影响精度与性能;持久化开启时存于系统表,重启不丢失;自动更新依赖行变更比例估算,不实时;analyze table强制重采样并覆盖统计信息,适用于执行计划劣化场景;直方图可增强列值分布估算,需手动更新且依赖持久化;判断不准需结合实际执行表现与多源数据比对。

mysql执行计划中的统计信息是如何采集的_mysql Analyze Table原理

统计信息不是“算出来”的,而是“抽样估算”出来的

MySQL 的统计信息(比如 CARDINALITYTABLE_ROWS)本质上是采样估算值,不是全表扫描后精确统计的。InnoDB 默认对每个索引随机抽取若干数据页(默认 innodb_stats_persistent_sample_pages=20),再基于这些页中键值的分布推算整个索引的基数和选择性。

  • 采样页数越少,收集快但不准;越多越准但耗时长,尤其对大表可能卡住 DML
  • 持久化开启(innodb_stats_persistent=ON,默认)时,采样结果存到 mysql.innodb_table_statsmysql.innodb_index_stats 表里,重启不丢
  • 关闭持久化后,统计信息只存在内存,重启即清空,下次访问表或执行 SHOW TABLE STATUS 会触发重新采样(若 innodb_stats_on_metadata=ON
  • 自动更新由 innodb_stats_auto_recalc 控制:默认 ON,当表变更行数超过约 10% 时后台异步触发 —— 但这个“10%”是估算的计数器,不精确,且不会实时刷新

ANALYZE TABLE 干了什么?为什么有时要手动跑

ANALYZE TABLE 不是“重新计算所有行”,而是强制触发一次新的采样,并用结果覆盖当前统计信息(内存 + 持久化表)。它适用于自动更新没跟上、执行计划明显变差的场景,比如大批次 deleteINSERT 后。

  • 对大表执行 ANALYZE TABLE 会加 MDL_SHARED_NO_WRITE 锁,阻塞 DDL,但不阻塞普通 DML(如 INSERT/UPDATE/select
  • 如果表有分区,ANALYZE TABLE 默认只分析一级分区元数据,不深入每个子分区(除非 MySQL ≥ 8.0.23 且显式指定)
  • 想跳过写 binlog(避免从库重复执行),可用 ANALYZE NO_WRITE_TO_BINLOG TABLE tbl
  • 不要在高峰期对几百 GB 的表频繁跑 —— 它会读取大量随机页,可能引发 I/O 尖峰

直方图(UPDATE HISTOGRAM)是统计信息的“增强包”

基础统计信息只提供全局基数,无法描述列值分布倾斜(比如 95% 的 status='active',5% 是其他值)。这时需要直方图:ANALYZE TABLE t UPDATE HISTOGRAM ON col_name WITH 16 BUCKETS

  • 直方图只影响优化器对 WHERE 条件的选择性估算,不影响索引本身的 CARDINALITY
  • 必须配合 innodb_stats_persistent=ON 才能持久保存;否则每次重启丢失
  • 桶数(BUCKETS)不是越多越好:16–64 足够覆盖常见倾斜,超 256 容易拖慢 EXPLAIN 解析速度
  • 直方图不自动更新 —— 数据大幅变化后,得手动再跑 UPDATE HISTOGRAM,否则形同虚设

怎么判断统计信息“不准”?别只看 EXPLAIN 的 rows

EXPLAIN 中的 rows 列只是估算值,本身不准不说明问题;关键是看实际执行是否走错索引、JOIN 顺序反常、或 type 突然变成 ALL

  • 对比 SELECT count(*) FROM tINformatION_SCHEMA.TABLES.TABLE_ROWS:差 3 倍以上就值得怀疑
  • SHOW INDEX FROM t,看 CARDINALITY 是否明显偏离真实唯一值数量(比如 CARDINALITY=1 但你知道该字段几乎全唯一)
  • 执行 EXPLAIN FORMAT=json SELECT ...,检查 "rows_estimation" 下各表的 "row_count" 是否严重偏离预期
  • 注意:INFORMATION_SCHEMA.STATISTICSCARDINALITY 是内存缓存值,可能滞后;真正权威的是 mysql.innodb_index_stats 里的 n_diff_pfx01 字段

统计信息不准最麻烦的地方不在“难修”,而在“难发现”——它常常静默导致慢查询,却不像锁等待或连接数爆满那样有明显告警信号。定期用脚本比对 TABLE_ROWS 和实际 COUNT,比等业务报慢更靠谱。

text=ZqhQzanResources