sql server存储过程中不能在主事务内同步写日志表,因其绑定主事务导致回滚时日志丢失、事务延长、锁粒度升级(如页锁变表锁),高并发下响应从20ms升至300ms+;须将日志表置于独立schema、禁用触发器/外键/非必要索引、insert时加with(tablock),且日志写入必须放在catch末尾并容错处理。

不能在主事务里同步写日志表——一写就拖慢 10 倍以上,高并发下直接锁等待雪崩。
SQL Server 存储过程中 INSERT INTO 日志表为什么导致性能断崖?
因为日志写入会绑定主事务:主逻辑回滚时,日志也消失;更关键的是它延长事务时间、升级锁粒度(比如页锁变表锁)。尤其在 sp_executesql 或 UPDATE 密集调用时,INSERT INTO audit.LogEntry 可能把响应从 20ms 拉到 300ms+。
- 必须把日志表放在独立 schema(如
audit),且禁用触发器、外键、非必要索引 -
INSERT时显式加WITH (TABLOCK)减少锁争用,但要确认没开READ_COMMITTED_SNAPSHOT(二者冲突) - 绝对不要在
TRY块里写日志——得挪到CATCH末尾,并用IF @@ERROR = 0判断是否写成功,失败也不该中断主逻辑
MySQL 存储过程里怎么获取可靠毫秒级时间戳?
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 小时;建议用TIMESTAMP类型字段(自动转 server 时区),别用DATETIME
哪些日志字段真有用,哪些纯属自找麻烦?
见过太多人往日志表里塞 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 快两个数量级——但很多人建完表就忘了配分区函数和方案。










