SQL Server中推荐用SYSDATETIME()记录过程执行时间,精度高且轻量;MySQL则用NOW(3)配合TIMESTAMPDIFF计算毫秒级耗时,并需注意事务隔离与异常日志兜底。

SQL Server 中用 GETDATE() 或 SYSDATETIME() 记录过程执行时间
直接在存储过程开头和结尾赋值给两个 datetime 或 datetime2 变量,是最轻量、最通用的做法。注意别用 GETUTCDATE() 除非你明确需要 UTC 时间,否则时区不一致会导致日志时间难对齐。
示例片段:
DECLARE @start_time datetime2 = SYSDATETIME(); -- 你的主逻辑(INSERT/UPDATE/复杂查询等) DECLARE @end_time datetime2 = SYSDATETIME(); SELECT DATEDIFF(ms, @start_time, @end_time) AS [duration_ms];
-
SYSDATETIME()比GETDATE()精度更高(100纳秒 vs 3.3毫秒),推荐用于性能敏感场景 - 如果过程可能运行超 24 小时,避免用
TIME类型计算差值 - 不要在循环内反复调用
SYSDATETIME()来“打点”,开销虽小但累积后会影响基准判断
把执行时间写入日志表而不是只 SELECT 输出
仅靠 SELECT 返回耗时,无法留存历史记录,也难以做趋势分析。必须显式 INSERT 到一张日志表中,且建议包含上下文字段。
日志表最小必要字段示例:
CREATE TABLE proc_execution_log (
log_id INT IDENTITY(1,1) PRIMARY KEY,
proc_name NVARCHAR(128),
start_time datetime2,
end_time datetime2,
duration_ms BIGINT,
status CHAR(1), -- 'S'=success, 'F'=fail
error_msg NVARCHAR(4000)
);-
proc_name建议用OBJECT_NAME(@@PROCID)动态获取,避免硬编码字符串 - 务必把
INSERT放在TRY...CATCH的CATCH块里补一条失败记录,否则异常退出时日志就断了 - 如果日志表写入本身失败(比如磁盘满),不要让主逻辑跟着失败——加
IF @@ERROR = 0判断再插入,或用EXEC sys.sp_executesql异步兜底(需权衡)
MySQL 存储过程中如何实现类似计时
MySQL 没有内置的高精度时间函数,NOW(3)(毫秒级)是实际可用的底线。注意其返回类型是 DATETIME,不能直接减法算毫秒,得用 TIMESTAMPDIFF。
关键写法:
DECLARE v_start_time DATETIME(3) DEFAULT NOW(3); -- 主逻辑 DECLARE v_end_time DATETIME(3) DEFAULT NOW(3); SELECT TIMESTAMPDIFF(MICROSECOND, v_start_time, v_end_time) / 1000 AS duration_ms;
- 必须声明为
DATETIME(3),否则默认无毫秒,NOW()和NOW(3)行为不同 -
TIMESTAMPDIFF(MICROSECOND, ...)是唯一可靠方式;直接v_end_time - v_start_time会丢精度甚至出错 - MySQL 8.0+ 支持
STATEMENT_DIGEST_TEXT等性能视图,但无法在过程内实时捕获自身耗时,仍需手动计时
避免在事务中记录耗时导致锁延长
如果你的日志表和业务表在同一个事务里更新,计时日志的 INSERT 会延长事务持有锁的时间,尤其当过程本身慢时,可能放大阻塞。
- 把日志写入单独事务:用
SAVE TRANSACTION+COMMIT单独提交日志,或更稳妥地用EXEC sp_executesql异步触发(SQL Server) - MySQL 可改用
INSERT DELAYED(5.6已弃用)或干脆走外部代理写日志,避免耦合 - 测试时容易忽略这点:本地单次执行看不出问题,但并发压测时,日志表的
INSERT可能成为热点锁瓶颈
真实线上环境里,计时本身几乎没开销,但日志落盘、事务绑定、错误路径覆盖,这三处最容易漏掉或写错。尤其是异常分支没记日志,查问题时会以为“过程没跑”,其实它崩在第一行就退出了。

















