sql server中必须为每步逻辑独立声明datetime2变量(如declare @t_load datetime2 = sysdatetime())才能准确捕获单步耗时,因相邻日志行的endtime/starttime相减受锁等待、事务延迟等干扰而失真;sysdatetime()精度达100纳秒,远高于getdate()的3.33毫秒,且须配合datediff_big防止溢出。

SQL Server 中用 SYSDATETIME() 打点记录各步骤时间
直接在每步逻辑前后声明 datetime2 变量,是唯一能准确捕获单步耗时的方式。别信日志表里相邻两行的 EndTime/StartTime 相减——锁等待、事务延迟、日志写入顺序错乱都会让这个差值毫无意义。
-
SYSDATETIME()精度 100 纳秒,比GETDATE()(仅 3.33ms)可靠得多,尤其对毫秒级短操作 - 每个步骤必须独立声明起点变量,例如:
DECLARE @t_load DATETIME2 = SYSDATETIME();,不能复用同一个变量 - 避免在单条
INSERT里多次调用SYSDATETIME():它在语句开始时求值一次,不是“实时取” - 计算差值优先用
DATEDIFF_BIG(MILLISECOND, @t_start, SYSDATETIME()),防止老版本DATEDIFF在超 24 天时溢出
MySQL 存储过程中怎么给每步计时
MySQL 没有 SYSDATETIME() 这种高精度函数,NOW(6) 是唯一可用的微秒级时间源,但要注意它的行为受事务隔离级别影响——在 REPEATABLE READ 下,同一事务内多次调用 NOW(6) 可能返回相同值。
- 必须声明为
DATETIME(6)类型变量,NOW()默认无小数位,NOW(3)只到毫秒,NOW(6)才到微秒 - 用
TIMESTAMPDIFF(MICROSECOND, @start, NOW(6))算差值,再除以 1000 得毫秒;别用UNIX_TIMESTAMP(),它只返回整秒 - 局部变量(
DECLARE)比会话变量(@start)安全,嵌套调用时不会被覆盖 - 如果过程含远程调用或
WAITFOR类等待,计时点必须严格卡在EXEC或CALL前后,否则测的是发起时间而非执行时间
日志表字段设计和写入时机的关键坑
只存两个时间戳没用,必须带上下文才能还原执行流。更麻烦的是,把 INSERT INTO log 写在主事务里,一卡全卡——日志表写慢,整个存储过程就拖慢。
- 日志表至少要有:
BatchId(传入参数或NEWID())、StepName(如'ValidateInput')、StartTime、EndTime、DurationMs(插入时算好,别用计算列) -
BatchId必须唯一且跨步骤一致,否则查不出完整链路;别用@@SPID,并发时多个过程共享同一 SPID - 日志写入建议脱离主事务:SQL Server 可用
TRY...CATCH+XACT_ABORT OFF单独提交;MySQL 可加INSERT IGNORE防重复,失败也不中断主逻辑 - 索引要建在
BatchId和StartTime上,否则按批次查执行流时扫描全表,几万条就明显变慢
PostgreSQL 函数里埋点的真实耗时怎么测
EXPLAIN ANALYZE 看似方便,但它会重跑查询、开启统计开关,结果比真实业务调用慢 10%–30%,不能当优化依据。
- 用
CLOCK_TIMESTAMP(),不是NOW():前者不受事务快照影响,始终返回当前物理时间 - 开头写
DECLARE start_ts TIMESTAMPTZ := CLOCK_TIMESTAMP();,结尾用EXTRACT(EPOCH FROM (CLOCK_TIMESTAMP() - start_ts)) * 1000000得微秒值 - 别把日志
RAISE NOTICE输出到客户端——某些驱动会缓冲或丢弃,应写入专用日志表 - 若函数内含
PERFORM调用其他函数,要在每一层都打点,否则只能看到外层总耗时,看不出瓶颈在哪一层
真正难的不是记时间,是让每条日志能对得上人眼可读的“步骤”。StepName 不能写成 'step1',得明确是 'FetchFromCache' 还是 'WriteToStaging';BatchId 不能漏传;DATEDIFF 函数选错类型会导致整数截断——这些细节一错,时间数据就变成噪音。











