SQL Server并发更新访问日志时忽略冲突的实现方案咨询
问题解答
你的try-catch方案存在的问题
首先明确:普通并发更新同一行时,SQL Server默认会让后续会话等待锁释放,不会直接触发异常进入catch块。只有发生死锁(其中一个会话被选为死锁牺牲品)或者你主动设置了SET LOCK_TIMEOUT导致等待超时,才会抛出异常进入catch。所以这个方案没法实现你“冲突就忽略”的核心需求——大部分高并发场景下是阻塞等待,拖慢请求处理,而不是直接跳过。
不过有一点是对的:只要catch块里不额外抛出错误,日志更新的异常不会影响后续业务请求,因为日志操作是独立事务,和业务事务完全分开。
更适合的实现方式
既然你只需要知道服务器最近X分钟是否被访问,不需要每条请求都更新时间,推荐两种高效且无阻塞的方案:
1. 带锁提示和条件判断的UPDATE
UPDATE log WITH (UPDLOCK, READPAST) SET last_access = GETDATE() WHERE last_access < DATEADD(MINUTE, -X, GETDATE())
READPAST:让SQL Server直接跳过被其他会话锁定的行,不会等待,相当于冲突时自动忽略本次更新;UPDLOCK:获取更新锁,降低死锁概率;WHERE条件:如果最近X分钟已经更新过时间,就不再执行更新,进一步减少不必要的数据库操作,完全匹配你的业务需求。
2. 用MERGE处理空表场景(可选)
如果你的log表可能一开始没有记录,可以用MERGE同时处理插入和更新,同样搭配锁提示:
MERGE log WITH (HOLDLOCK, READPAST) AS target USING (SELECT GETDATE() AS new_time) AS source ON 1=1 -- 假设表中只有一行记录 WHEN MATCHED AND target.last_access < DATEADD(MINUTE, -X, source.new_time) THEN UPDATE SET last_access = source.new_time WHEN NOT MATCHED THEN INSERT (last_access) VALUES (source.new_time);
这个方案会自动在表为空时插入初始记录,并发场景下同样会跳过锁定的行,不会阻塞请求。
总结
你的try-catch方案解决不了高并发下的阻塞问题,最优选择是用锁提示+时间条件的更新语句,既能避免阻塞,又能减少不必要的更新,完美实现“冲突时忽略”的需求。
内容的提问来源于stack exchange,提问作者Marc Guillot
相关产品推荐
相关产品推荐

