不能靠show status或innodb_trx直接识别存储过程执行时间长,因mysql不将存储过程视为独立执行单元;必须通过performance_schema按线程关联events_statements_current、innodb_trx、events_statements_history_long等表,从语句、事务、锁、内存四维度交叉定位。

不能靠 SHOW STATUS 或 INNODB_TRX 直接识别“存储过程执行时间长”,因为 MySQL 不把存储过程本身当作独立可计量的执行单元;它只记录线程状态和事务行为,而一个 CALL 可能包含多个事务、空闲等待、甚至跨事务的游标保持——必须拆解到线程 + 语句 + 锁 + 内存分配四个维度交叉定位。
查哪些线程在跑存储过程(不是查“过程名”,而是查 CALL 语句)
MySQL 不会把 CALL proc_name() 持久标记为“正在执行存储过程”,它只在 events_statements_current 中短暂保留该语句文本。所以得立刻抓取:
- 执行
SELECT THREAD_ID, SQL_TEXT, TIMER_WAIT/1000000000000 AS sec FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE 'CALL %' AND TIMER_WAIT > 0; -
SQL_TEXT必须用LIKE 'CALL %',不能只查INFORMATION_SCHEMA.PROCESSLIST,因为后者只显示当前命令类型(如Query),不保留原始 CALL 文本 -
TIMER_WAIT单位是皮秒,除以1000000000000才是秒级,小于 1 秒的调用基本可忽略 - 如果返回为空,不代表没在跑——可能是刚进入、还没解析完,或已执行完第一层语句正卡在子查询/锁/IO 上
判断它是否真“卡住”:看线程状态和事务状态是否错位
一个看似在跑存储过程的线程,实际可能处于假死状态:比如事务已提交但连接未关闭、游标未释放、或被锁阻塞却没反映在 TRX_STATE 里。要交叉验证:
- 用上一步拿到的
THREAD_ID关联performance_schema.threads查PROCESSLIST_COMMAND和PROCESSLIST_STATE:若为Sleep但SQL_TEXT显示CALL,说明过程已退出但连接没断 - 再用
THREAD_ID关联INFORMATION_SCHEMA.INNODB_TRX:若TRX_STATE = 'RUNNING'但TRX_QUERY是NULL,大概率是游标打开后没 fetch 完、或卡在隐式排序/临时表生成中 - 特别注意
TRX_STARTED时间:如果比当前时间早几分钟以上,且TRX_QUERY为空,基本可断定是空闲事务持有锁或缓存资源
定位内存和锁的真实消耗点(不是过程,而是它内部的 SQL)
存储过程本身不占内存,真正吃资源的是它内部执行的语句。想确认是不是某个 CALL 导致 OOM 或锁堆积,得顺藤摸瓜:
- 从
events_statements_current拿到THREAD_ID后,查performance_schema.events_statements_history_long中最近 20 条语句:SELECT SQL_TEXT, DIGEST_TEXT, WORK_COMPLETED, TIMER_WAIT/1000000000000 AS sec FROM performance_schema.events_statements_history_long WHERE THREAD_ID = ? ORDER BY EVENT_ID DESC LIMIT 20; - 重点关注
WORK_COMPLETED > 0的SELECT或INSERT—— 这类语句常触发大临时表或排序缓冲区(sort_buffer_size),WORK_COMPLETED值越大,说明扫描/排序行数越多 - 若发现某条
SELECT ... ORDER BY的TIMER_WAIT高但WORK_COMPLETED低,大概率是等锁(查data_lock_waits确认);若WORK_COMPLETED高但TIMER_WAIT低,说明 CPU/IO 效率尚可,但内存开销已在后台持续增长
为什么你查不到“这个存储过程跑了多久”
因为 MySQL 没有“存储过程生命周期计时器”。events_transactions_current 只跟踪事务,events_statements_current 只跟踪单条语句,而一个存储过程可以:BEGIN 开事务 → 执行多条 SELECT(不进事务)→ COMMIT → 再 OPEN cursor(不进新事务)→ 一直 SLEEP(30)。这种混合模式下,唯一可靠的时间锚点是线程的 PROCESSLIST_TIME(自连接开始的秒数),但它无法区分“执行中”和“空闲中”。
真正要监控,得在存储过程开头写 INSERT INTO debug_log VALUES (NOW(), CONNECTION_ID(), 'start');,结尾写 INSERT 记结束——否则所有系统视图给的都是碎片化快照,拼不出完整过程耗时。











