MySQL不记录存储过程调用历史,需通过general_log日志、应用代码扫描及运行时埋点三者交叉验证;其中日志仅适用于短期低流量环境,代码扫描需覆盖拼接调用与ORM配置,埋点则适合关键过程以获取真实调用时间。

MySQL 本身不记录存储过程调用历史,information_schema.ROUTINES 只存定义,不存调用次数;SHOW STATUS LIKE 'Com_call_procedure' 是全局累计值,无法拆到单个过程。所谓“长期未调用”,必须靠外部日志+代码扫描+运行时埋点三者交叉验证。
查 general_log(仅限短期、低流量环境)
如果 general_log 已开启且保留足够久(比如最近 90 天),可直接 grep 调用痕迹:
-
SELECT * FROM mysql.general_log WHERE argument LIKE '%CALL%sp_name%' AND event_time > DATE_SUB(NOW(), INTERVAL 90 DAY);—— 注意:该表默认关闭,且写入频繁时 I/O 压力极大,生产库慎开 - 若用文件日志(
general_log_file),用grep -i "CALL sp_name" /var/lib/mysql/general.log | tail -20快速确认近期有无调用 - 别信
long_query_time设置——慢日志只记执行时间长的语句,而空跑或快返回的CALL不会进慢日志
扫应用代码和配置(最可靠、必须做)
真实调用点永远在应用侧,不是数据库里。漏掉任意一类调用源,就可能误判“未使用”:
- 搜
CALL字符串:但注意拼接场景,如"CALL " + procName或String.format("CALL %s", name),需用正则CALL\s+\w+ - 搜 ORM 配置:
MyBatis的 XML 中常见{call sp_xxx(?, ?)};@Procedure(name = "sp_xxx")注解也要覆盖 - 搜配置文件:
application.yml里可能有procedure.name: sp_inventory_sync,代码读取后动态拼接 - 用
rg "CALL|@Procedure|statementType.*CALLABLE|{call" --type=java --type=xml --type=yml,比grep -r更准更快 - 别忽略定时任务脚本(
.sh、.py)和运维手动执行记录——它们往往不在主代码树里
建标记表 + 运行时埋点(适合关键过程)
对核心存储过程,主动加轻量级运行日志,才能拿到真实“最后一次调用时间”:
- 在过程开头插入:
INSERT INTO proc_usage_log (proc_name, start_time, conn_id) VALUES ('sp_billing_close', NOW(), CONNECTION_ID()); - 结尾或
EXIT HANDLER中更新:UPDATE proc_usage_log SET end_time = NOW() WHERE proc_name = 'sp_billing_close' AND conn_id = CONNECTION_ID(); - 查“长期未调用”:
SELECT proc_name, MAX(end_time) last_run FROM proc_usage_log GROUP BY proc_name HAVING last_run - 表结构必须加索引:
CREATE INDEX idx_proc_time ON proc_usage_log (proc_name, end_time);,否则查询变慢 - 避免高频写入:只对业务关键过程埋点,非核心过程靠代码扫描+日志回溯更稳妥
真正容易被忽略的是“调用来源多样性”——同一个 sp_user_sync 可能被 Java 应用、Python 脚本、DBA 手动 CALL、甚至某个已下线但未清理的 Cron 任务调用。只查数据库侧,等于只看了半张地图。


















