mysql不单独记录存储过程层级开销,需通过performance_schema监控其内部sql语句;启用events_statements_history_long等消费者后,可查call摘要、线程id、i/o与内存消耗,并结合sys schema和执行计划定位隐式临时表、索引缺失等根因。

查存储过程执行时的线程与耗时(performance_schema)
MySQL 不会单独记录“存储过程”这个层级的资源开销,它只监控到 events_statements_summary_by_digest 中的语句摘要——而调用存储过程的 CALL proc_name() 会被当作一条普通语句统计。所以真正能抓到资源消耗的,是执行过程中实际产生的 SQL 和线程行为。
关键点在于:存储过程本身不占资源,它里面跑的 SELECT/UPDATE/LOOP 才占。必须打开 Performance Schema 并确保采集粒度足够细:
-
performance_schema必须为ON(检查SELECT @@performance_schema) - 启用语句级监控:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_statements_history_long'; - 查最近最耗时的 CALL:
SELECT DIGEST_TEXT, SUM_TIMER_WAIT, COUNT_STAR FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT LIKE 'CALL%' ORDER BY SUM_TIMER_WAIT DESC LIMIT 5; - 进一步定位:拿到 DIGEST 后,查
events_statements_history_long找具体线程 ID,再关联threads看用户、host、是否在 sleep
看存储过程里每条 SQL 的内存与 I/O(sys schema 辅助)
原生 Performance Schema 查内存很麻烦,sys schema 封装了更实用的视图,尤其适合快速判断“是不是某个存储过程在疯狂刷磁盘或吃内存”。但注意:sys 是依赖 Performance Schema 的只读视图,没开 P_S 就全为空。
- 查哪个 CALL 引发最多磁盘 I/O:
SELECT * FROM sys.io_by_thread_by_bytes WHERE thread LIKE '%CALL%' ORDER BY total DESC LIMIT 3; - 查该线程当前分配了多少内存:
SELECT user, current_allocated, current_statement FROM sys.memory_by_thread_by_current_bytes m JOIN sys.session s ON m.thread_id = s.thd_id WHERE s.current_statement LIKE 'CALL%'; - 如果返回空,大概率是线程已结束,或
memory instruments没开(需在setup_instruments中启用memory/%类)
避免误判:CALL 本身不慢,慢的是隐式临时表或循环逻辑
常见错误是看到 CALL my_proc 耗时高,就以为过程代码有问题。实际上可能是它内部反复创建隐式临时表、没走索引的子查询嵌套、或者游标遍历大结果集——这些都不会在 DIGEST_TEXT 中展开,只会合并成一个 CALL 摘要。
- 用
SHOW PROCESSLIST配合Time列,看运行中的 CALL 是否卡在Sending data或Copying to tmp table - 在存储过程开头加
SELECT @@tmp_table_size, @@max_heap_table_size;,确认内存临时表上限是否过小(导致频繁落磁盘) - 对过程内关键 SQL 单独
EXPLAIN,尤其注意type=ALL、Extra=Using temporary; Using filesort - MySQL 8.0.25 及之前版本中,含 UNION ALL 的视图被 CALL 时,过滤条件无法下推——这种场景下,CALL 看似简单,实则扫描全部 80 张表(见 2026 年 2 月线上案例)
长期监控要绕开 CALL,盯住底层对象(表/索引/缓冲池)
把监控重心放在 CALL 上是短视的。真实瓶颈永远在数据访问层:某张表被高频更新导致 buffer pool 压力大、某个索引失效引发全表扫描、InnoDB 日志写满造成 flush 延迟……这些才是存储过程“变慢”的根因。
- 定期查
sys.innodb_buffer_stats_by_table,看过程常操作的表是否长期占满 buffer pool - 用
sys.schema_index_statistics看过程里 UPDATE/DELETE 的字段是否有对应索引 - 监控
Innodb_buffer_pool_wait_free状态变量,值持续 > 0 说明 buffer pool 不够用,不是过程问题,是配置或数据模型问题
真正难的不是查出哪句 CALL 慢,而是判断它为什么慢——这需要把语句执行路径、内存分配、磁盘 I/O、锁等待、甚至 MySQL 版本已知缺陷都串起来看。单靠一个 SHOW PROFILE 或 slow_query_log 容易漏掉上下文。











