MySQL升级后存储过程变慢的主因是优化器行为、统计信息和默认配置变化,首要操作是执行ANALYZE TABLE更新统计信息;ALTER PROCEDURE不重建执行计划,因存储过程无传统编译缓存。

MySQL升级后存储过程变慢,不是因为要“重新编译”,而是优化器行为、统计信息和默认配置变了——直接执行 ANALYZE TABLE 是最该优先做的动作。
为什么 ALTER PROCEDURE 不起作用
MySQL 存储过程没有传统编译缓存,ALTER PROCEDURE 只改元数据(比如注释或 SQL SECURITY),不会触发执行计划重建。升级后性能掉下去,和“没重编译”完全无关。常见错误是看到慢就去反复 ALTER PROCEDURE,浪费时间还掩盖了真正问题。
- 执行计划缓存依赖的是内部 SQL 语句 + 参数值 + 会话环境(如
optimizer_switch、字符集、统计信息),不是过程本身 - 8.0 中哪怕写了
DETERMINISTIC,只要用了NOW()或用户变量@var,就会被标记为非确定性,导致每次调用都重新解析 - 真正影响首次执行和复用率的,是表的统计信息是否新鲜、索引选择是否合理、隔离级别是否加重 MVCC 开销
ANALYZE TABLE 必须做,但得讲究方式
ANALYZE TABLE 重采样索引基数和数据分布,直接影响优化器对 JOIN 顺序、索引下推、临时表策略的判断。升级后不做这步,90% 的慢过程都卡在这儿。
- 只对高频访问的表执行:
ANALYZE TABLE orders, customers, order_items;,别用mysqlcheck --analyze --all-databases,容易锁表且没必要 - InnoDB 表在 8.0+ 支持并发采样,但仍建议低峰期执行;5.7 下是阻塞操作,务必避开业务高峰
- 确认
innodb_stats_auto_recalc=ON(8.0 默认开,5.7.6+ 也是),但升级后首次必须手动触发,自动机制不会回填旧数据分布 - 避免和
OPTIMIZE TABLE混用:后者整理碎片,对执行计划无影响,还可能引发长锁
查执行计划比猜原因更直接
不要靠感觉判断哪条 SQL 慢,把存储过程里关键语句拎出来,补上实际参数值,用 EXPLAIN FORMAT=TREE(8.0)或 EXPLAIN FORMAT=JSON(5.7+)看真实路径。
- 重点盯
rows是否远超实际匹配数、key是否用了预期索引、Extra有没有Using filesort或Using temporary - 对比升级前后计划差异:8.0 默认开启
hash_join=on,若过程里有小表 JOIN 大表,可能从嵌套循环变成哈希连接,内存不足时反而更慢 - 临时验证可关特性:
SET SESSION optimizer_switch='hash_join=off';再调用过程测试,确认是否是它拖累 - 注意字符集隐式转换:比如参数声明为
VARCHAR(36),但字段是CHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,而客户端连接用的是utf8mb4_general_ci,会导致索引失效
容易被忽略的三个细节
这些点不显眼,但一踩就卡住半天:
- 事务隔离级别:8.0 默认
REPEATABLE-READ,如果过程里有SELECT ... FOR UPDATE且未显式设SET TRANSACTION ISOLATION LEVEL READ COMMITTED,MVCC 版本链遍历开销会明显上升 - 参数类型不匹配:定义
IN p_id VARCHAR(20),但表字段是BIGINT,传数字CALL proc(123)就会触发字段转字符串,索引白建 - 变量传参导致计划固化:过程里写
WHERE status = p_status,首次调用传'paid'生成了走索引的计划,后续传'pending'(高基数)仍复用旧计划,结果全表扫——改用WHERE status = ?配预编译,或加FORCE INDEX


















