不是因为“没重新编译”,mysql存储过程无传统编译缓存;变慢根因是优化器行为突变、统计信息失真或默认配置漂移,须立即执行analyze table更新统计信息、对比explain计划、检查optimizer_switch与直方图,并警惕变量传参导致的计划缓存污染。

不是因为“没重新编译”,MySQL 存储过程本身没有传统编译缓存;变慢的根因是优化器行为突变、统计信息失真或默认配置漂移——升级后必须立刻干预,否则性能不会自行恢复。
升级后必须手动执行 ANALYZE TABLE,别碰 OPTIMIZE TABLE
MySQL 优化器严重依赖 ANALYZE TABLE 生成的统计信息做索引选择、JOIN 顺序和临时表策略判断。升级(尤其是 5.7 → 8.0)后,旧统计信息与新优化器成本模型不匹配,极易导致计划退化:同一语句从 type=ref 退化为 type=ALL,key 字段显示 NULL。
-
ANALYZE TABLE会重采样数据分布,直接影响执行计划;OPTIMIZE TABLE只整理碎片,对查询性能无实质帮助,且在低版本中会锁表 - 优先对存储过程中高频访问的表执行,例如:
ANALYZE TABLE orders, customers, order_items;,避免全库扫描式mysqlcheck --analyze - InnoDB 表确认
innodb_stats_auto_recalc=ON(8.0 默认开启),但升级后首次仍需手动触发 - 5.7 中
ANALYZE TABLE是阻塞操作,建议在低峰执行;8.0+ 支持并发采样,但仍需留意Created_tmp_disk_tables上升趋势
检查 optimizer_switch 和直方图是否被意外启用
MySQL 5.7 → 8.0 升级会默认开启 hash_join=on、启用直方图支持,并调整 use_stat_tables 行为。这些变化会让原本走 Nested-Loop 的 JOIN 突然切到 Hash Join,而若内存不足或数据倾斜,反而更慢。
- 用
EXPLAIN FORMAT=TREE(8.0)或EXPLAIN FORMAT=JSON(5.7+)对比升级前后实际执行路径,重点关注join_execution和used_columns - 临时验证影响:在会话级关闭可疑特性,例如
SET SESSION optimizer_switch='hash_join=off';,再调用存储过程测试 - 检查是否启用了直方图:
SELECT * FROM information_schema.COLUMN_STATISTICS WHERE schema_name='db' AND table_name='t';;若有,确认histogram_type是否合理(如SINGLE_PREC_HB对高基数字段可能失真) - 避免全局修改
optimizer_switch,应在存储过程开头用SET SESSION局部控制,防止污染其他会话
警惕变量传参导致的执行计划缓存污染
MySQL 不具备参数嗅探机制。存储过程中使用 WHERE status = p_status 这类写法时,优化器在首次调用时基于该值生成并缓存计划;后续传入不同选择率的值(如高频用户 vs 冷门用户),仍复用旧计划,造成严重误判。
- 现象:同一存储过程多次调用,
SHOW PROFILES显示耗时剧烈波动;EXPLAIN中rows预估远高于实际匹配数 - 8.0.22+ 可在关键查询前加
/*+ RECOMPILE */注释强制重生成计划 - 兼容性更强的做法:改用动态 SQL,
PREPARE stmt FROM 'SELECT ... WHERE status = ?'; EXECUTE stmt USING p_status;,让每次编译都基于真实值 - 避免在 WHERE 中对字段使用函数或隐式转换(如
WHERE id = p_id但p_id是VARCHAR而字段是BIGINT),这会让索引彻底失效
高并发下过程缓存未命中才是隐形瓶颈
如果你还在用 MySQL 5.7.21 之前版本(比如 5.6 或早期 5.7),CALL 每次都会重新解析、优化、生成执行计划——这不是“慢”,是“每次都重来”。版本限制问题,调优毫无意义。
- 先查版本:
SELECT VERSION();;低于 5.7.21 就别折腾索引和参数,升级才是唯一解 - 即使版本达标,也要确认是否真命中缓存:查
performance_schema.events_statements_history_long,看同名CALL my_proc()多次调用时PLAN_EXPLAIN字段是否重复出现 - 缓存失效常见原因是参数类型不一致(如声明
IN p_id INT,却传字符串'123'),触发隐式转换,导致缓存键不匹配 - 调试时可用:
SELECT p_id, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='t' AND COLUMN_NAME='id';直接比对字段与参数类型
真正容易被忽略的是:升级后没人去动 ANALYZE TABLE,也没人去查 EXPLAIN FORMAT=TREE 输出里的 join_execution 节点——所有“变慢”的线索其实都明明白白写在那几行文本里,而不是藏在存储过程的语法结构中。











