不能在主事务中同步写日志表,因其会参与主事务导致回滚时日志丢失、延长事务时间、升级锁粒度(如页锁变表锁),高并发下引发锁等待雪崩,响应时间可从20ms拖至300ms+。

别在主事务里同步写日志表——这是最常踩的坑,一写就拖慢 10 倍以上,高并发下直接引发锁等待雪崩。
SQL Server 存储过程里写日志,为什么不能用 INSERT INTO log_table 直接塞?
因为日志写入会参与主事务:一旦主逻辑回滚,日志也消失;更糟的是,它延长事务时间、升级锁粒度(比如页锁变表锁),尤其在 UPDATE 或 sp_executesql 调用密集时,INSERT INTO audit.LogEntry 可能把响应从 20ms 拉到 300ms+。
- 必须把日志表放在独立 schema(如
audit),且禁用触发器、外键、非必要索引 -
INSERT时显式加WITH (TABLOCK),减少锁争用,但要确认没开READ_COMMITTED_SNAPSHOT(二者冲突) - 绝对不要在
TRY块里写日志——得挪到CATCH末尾,并用IF @@ERROR = 0判断是否写成功,失败也不该中断主逻辑
MySQL 存储过程计时 + 日志,NOW(3) 和 TIMESTAMPDIFF 怎么配?
MySQL 没 GETDATE(),NOW(3) 是唯一靠谱的毫秒级起点,但直接相减会丢精度——必须用 TIMESTAMPDIFF 算微秒再转毫秒。
- 声明变量必须是
DATETIME(3),写成DATETIME默认无毫秒,NOW()和NOW(3)行为不同 - 计算写法固定:
TIMESTAMPDIFF(MICROSECOND, v_start_time, v_end_time) / 1000 - 时区陷阱:检查
SELECT @@time_zone,如果返回+00:00但应用在东八区,所有日志时间晚 8 小时
日志表字段设计,哪些字段真有用,哪些纯属自找麻烦?
见过太多人往日志表里塞 HostName、ClientIP、WindowsLogin,结果发现全是无效数据:SQL Server 的 HOST_NAME() 返回的是应用服务器名,SUSER_SNAME() 在连接池下永远是同一个账号,CLIENT_NET_ADDRESS 在代理后全是 127.0.0.1。
- 真正稳定字段只有五个:
LogId(BIGINT IDENTITY,别用UNIQUEIDENTIFIER)、ProcedureName(用OBJECT_NAME(@@PROCID)动态取)、ExecutionTime(DATETIME2(3))、UserId(必须由应用传入@CurrentUserId)、OperationType(CHAR(3),如'INS') - 别存完整 SQL 或参数 JSON——那是 ELK 或 Grafana 干的事,数据库表只记关键上下文
-
CreatedOn字段加非聚集索引就够了,其他字段一律不建索引
最易被忽略的一点:日志表必须按月分区(如 log_proc_202607),否则单表超千万行后,哪怕只是 SELECT TOP 100 都可能触发全表扫描——而分区切换(SWITCH PARTITION)归档旧数据,比 DELETE 快两个数量级。











