应使用数据库原生高性能诊断工具:sql server用sp_statement_completed扩展事件(开启cpu_time和statement_text),mysql用now(6)微秒时间戳,oracle用dbms_profiler四表联合分析(需interpreted模式),并避免同步写日志。

SQL Server 存储过程中怎么测每一步的耗时
不能靠 SET STATISTICS TIME ON,它只在会话级输出总耗时,不进存储过程体;也不能在过程里写 GETDATE() 算差值——精度低(毫秒级)、受时钟漂移影响、且无法区分 CPU 时间和等待时间。
真正能落地的方式是:用扩展事件 sp_statement_completed 捕获每条语句执行快照,并确保开启 collect_cpu_time = 1 和 collect_statement_text = 1。
- 该事件能穿透到存储过程内部每一行 SQL(比如
UPDATE、SELECT、嵌套EXEC),不是只记录最外层EXEC proc_name -
duration字段单位始终是微秒,除以 1000 得毫秒;cpu_time同样是微秒,反映真实调度器占用,比sys.dm_exec_requests.cpu_time更准(后者是会话级累计) - 必须加谓词过滤,例如
WHERE [object_name] = N'YourProc' AND [duration] > 500000(即 >500ms),否则日志爆炸 - 别用
sql_batch_completed:它把整个批当一个单元,无法定位到IF @x=1 BEGIN ... END里哪一分支慢
MySQL 存储过程里怎么埋点测单步耗时
别用 BENCHMARK(),它压根不是计时函数,而是重复执行表达式做性能测试;也别用 UNIX_TIMESTAMP(),精度只有秒级,对毫秒级过程毫无意义。
正确做法是用 NOW(6) 或 SYSUTCDATETIME()(MySQL 8.0.28+)获取带微秒的时间戳,前后相减再转换单位:
- 声明局部变量:
DECLARE @start_time DATETIME(6);(必须是局部变量,会话级@var在嵌套调用中会被覆盖) - 开始前赋值:
SET @start_time = NOW(6); - 结束后计算:
SELECT TIMESTAMPDIFF(MICROSECOND, @start_time, NOW(6)) AS us_elapsed; - 结果单位是微秒,除以 1000000 得秒,除以 1000 得毫秒
Oracle PL/SQL 中如何定位哪一行代码拖慢了过程
DBMS_PROFILER 不提供单次 wall-clock 耗时,它只采样统计“时间片”和执行次数,没法告诉你某次 CALL my_proc 到底花了多少毫秒。真要定位到具体行,得靠四张表联合查询,漏一不可。
- 先查
PLSQL_PROFILER_RUNS确认runid已生成且状态为FINISHED - 再查
PLSQL_PROFILER_UNITS找到目标过程对应的unit_number(注意unit_name大小写敏感) - 然后用
runid + unit_number去PLSQL_PROFILER_DATA查每行的total_time(单位是纳秒) - 最后对照
PLSQL_PROFILER_GRANULARITY确认采样粒度是LINE还是BLOCK,否则line#含义不对 - 关键陷阱:
PLSQL_CODE_TYPE = NATIVE时 profiler 完全失效,必须设为INTERPRETED并重编译:ALTER PROCEDURE my_proc COMPILE DEBUG
所有数据库共通的坑:同步写日志会放大耗时
在存储过程末尾直接 INSERT INTO audit_log 记录每一步耗时,看似简单,实则让主逻辑变慢 2–5 倍。尤其当 audit_log 表没索引、或高并发插入时,I/O 和锁竞争会卡住主事务。
- 同步写入可能增加 20–200ms 延迟,原本 50ms 的步骤变成 250ms
-
AUTO_INCREMENT主键 + 高频插入易引发页分裂和锁等待 - 更稳妥的做法是异步落库:SQL Server 可走
Service Broker或先写内存表再由作业批量刷盘;MySQL 应由应用层发 MQ 消息,而非在 SP 内 INSERT - 至少加
TRY...CATCH包裹日志写入,失败不中断主流程;并限制字段长度(如procedure_name截断至 64 字符),避免长文本拖垮性能
sp_statement_completed.duration、MySQL 的 TIMESTAMPDIFF(MICROSECOND, ...)、Oracle 的 PLSQL_PROFILER_DATA.total_time 单位和起点都不一样,混在一起分析前必须统一换算和归因。











