跳到主内容

执行计划突变避坑:ANALYZE 与直方图修复实录 ​

问题背景:同一条 SQL,为什么突然走错索引 ​

线上最棘手的性能问题,往往不是 SQL 从来没快过,而是昨天还走索引,今天突然全表扫描或选错联合索引。典型现象是:业务代码没有发布,索引仍然存在,慢查询却在某个时间点集中爆发;EXPLAIN 发现 key 从预期的 idx_status_created 变成了另一个低选择性索引,rows 估算值与真实值相差几十倍。

这类问题很多时候源于 InnoDB 优化器依赖的统计信息失真。MySQL 优化器并不在每次优化时实时扫描全表,而是根据采样页数、索引基数、数据分布等信息估算成本。当大批量写入、删除、字段分布倾斜,或自动采样恰好抽到不均匀页面时,优化器可能误判条件选择率,最终造成执行计划突变。

下面通过一个订单明细表复现问题,并给出 ANALYZE TABLE、持久化统计参数和直方图的修复边界。

sql
-- 建立演示表
CREATE TABLE order_item_demo (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  tenant_id INT NOT NULL,
  status TINYINT NOT NULL,
  channel VARCHAR(20) NOT NULL,
  amount DECIMAL(12,2) NOT NULL,
  created_at DATETIME NOT NULL,
  KEY idx_status_created (status, created_at),
  KEY idx_tenant_status (tenant_id, status),
  KEY idx_channel (channel)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 写入 10 万行:95% 为已完成状态,少量待处理
INSERT INTO order_item_demo
(tenant_id, status, channel, amount, created_at)
WITH RECURSIVE seq AS (
  SELECT 1 AS n
  UNION ALL
  SELECT n + 1 FROM seq WHERE n < 100000
)
SELECT
  1 + (n % 20),
  CASE WHEN n % 20 = 0 THEN 0 ELSE 1 END,
  CASE WHEN n % 100 = 0 THEN 'OFFLINE' ELSE 'ONLINE' END,
  50 + (n % 500),
  DATE_ADD('2025-01-01', INTERVAL n MINUTE)
FROM seq;

待处理订单 status = 0 只有 5%,按 status + created_at 查询理应使用 idx_status_created:

sql
EXPLAIN SELECT id, amount
FROM order_item_demo
WHERE status = 0
  AND created_at >= '2025-02-01'
ORDER BY created_at DESC
LIMIT 20;

但在批量导入、删除或统计页采样不稳定后,优化器可能高估该索引命中行数,转而选择 idx_tenant_status,甚至进行全表扫描后排序。此时不要急着加 FORCE INDEX,因为它可能只是把统计问题掩盖成长期维护负担。

方案对比:ANALYZE、索引提示与直方图怎么选 ​

修复统计失真有三类常见手段,适用场景不同,不能混用。

第一类是重新收集表级索引统计信息,即 ANALYZE TABLE。它成本相对可控,适合批量数据变更后索引基数变化明显、SHOW INDEX 中 Cardinality 明显异常、执行计划在多个普通索引之间摇摆的场景。

第二类是调整持久化统计参数。MySQL 8.0 默认使用持久化优化器统计信息,关键参数包括 innodb_stats_auto_recalc、innodb_stats_persistent_sample_pages 和 innodb_stats_transient_sample_pages。自动重算有阈值和延迟,不能指望大批量写入后立刻得到精确统计。

第三类是列级直方图。普通索引统计主要描述索引前缀的基数,不能准确表达非索引列或高度倾斜列的数据分布。例如 channel 字段中 99% 是 ONLINE,1% 是 OFFLINE,如果查询使用不同渠道,平均选择率会严重误导优化器,此时直方图更有价值。

执行计划突变EXPLAIN 对比检查索引基数SHOW INDEX判断数据倾斜频率或范围分布ANALYZE TABLE刷新索引统计提高采样页数大表按表配置创建直方图修复列分布估算先 ANALYZE 验证 → 无效再调采样页数或建直方图,避免盲目 FORCE INDEX

免责声明

本工具与文章内容仅用于学习测试场景,优化建议仅供参考,请勿直接用于生产环境,由此产生的风险自行承担。