先查慢查询日志定位call语句耗时,再对过程内关键sql代入实际参数值执行explain;重点看type是否为all、key是否命中索引、rows是否远超实际匹配数。

存储过程本身不慢,慢的是它里面某条 SELECT、UPDATE 或 INSERT 语句——直接对这些语句做 EXPLAIN,比折腾过程语法有效十倍。
怎么快速定位哪条 SQL 在拖后腿
别猜,用慢查询日志抓真实耗时的 CALL 记录:
- 先开日志:
SET GLOBAL slow_query_log = ON;,SET GLOBAL long_query_time = 0.1; - 查日志里带
CALL proc_name的行,确认执行时间是否真高(注意:日志里只记 CALL 入口,不记内部语句) - 如果日志显示 CALL 耗时 2s,但你怀疑是某条子查询卡住,就进过程体,把那句 SQL 单独拎出来,手动代入实际参数值再跑
EXPLAIN - 避免用变量写法如
WHERE id = in_id直接EXPLAIN——优化器看不到具体值,rows估算会严重失真;换成WHERE id = 123再分析
EXPLAIN 看什么?重点盯这三个字段
EXPLAIN 输出里真正决定快慢的是这三项,其他都是干扰项:
-
type是ALL?基本等于全表扫描,立刻检查 WHERE 条件字段有没有索引 -
key显示NULL或不是你预期的索引名?说明没走索引,可能是隐式转换(比如参数定义为VARCHAR,但字段是BIGINT)或函数包裹(WHERE DATE(created_at) = '2026-05-01') -
rows值远超实际匹配行数(比如查 10 行却扫了 10 万行)?说明索引选择错误,可加USE INDEX强制,或检查统计信息是否陈旧(ANALYZE TABLE t_name;)
为什么加了索引,存储过程还是慢
索引建了≠能用上,常见断点在参数传递和执行上下文:
- 存储过程参数类型和表字段不一致,例如
IN p_id VARCHAR(32)对应id BIGINT,调用CALL proc(123)会触发字段转字符串,索引失效 - 用
SELECT ... INTO @var赋值后,再拿@var做WHERE条件,优化器无法预估选择率,常放弃索引;改用WHERE col = ?配合客户端预编译,或把逻辑拆到应用层 - 过程里用了临时表(
CREATE TEMPORARY TABLE),而该表没建索引,后续JOIN或WHERE就只能全表扫——临时表也要像普通表一样加索引
游标、循环、无 INTO 的 SELECT 是隐形性能杀手
这些操作看着轻量,实则放大 N 倍开销:
- 游标遍历万级数据时,每
FETCH一行都维持锁和内存缓存,不如改成INSERT INTO tmp SELECT ...一次性落盘 - 循环里写
INSERT INTO log VALUES (...),每轮都触发解析+优化+日志刷盘;换成拼批量INSERT INTO log VALUES (...), (...), (...),性能差 5–10 倍 - 调试用的
SELECT * FROM t WHERE ...没加INTO,结果集发回客户端,占网络带宽、内存缓冲、上下文切换——上线前必须删干净
最易被忽略的一点:EXPLAIN 看的是计划,不是真实执行。如果某条语句 type 是 ref、rows 是 100,但实际跑起来要 3 秒,大概率是锁等待、磁盘临时表或大字段 JSON_EXTRACT 拖累——这时得切到 Performance Schema 查 events_statements_history_long,看耗时究竟落在哪个阶段。











