SQL Server 2014单语句/存储过程实现查询并更新同一条记录
原子查询并更新记录的SQL实现
这是一个典型的并发场景需求,核心要解决的是竞态条件——如果分开执行查询和更新,高并发下很可能出现多个进程同时拿到同一条未锁定的记录,导致重复处理。下面给你两种可靠的实现方式,都能保证操作的原子性:
方法一:使用CTE + UPDATE + OUTPUT子句(单语句实现)
这种方式直接在数据库层面完成查询和更新的原子操作,同时返回被更新的记录。推荐加上UPDLOCK和READPAST锁提示,避免死锁和线程阻塞:
WITH TargetRow AS ( SELECT TOP 1 ID, TimeStamp, Locked, Deleted FROM TableName WITH (UPDLOCK, READPAST) WHERE Locked = 'False' AND Deleted = 'False' ORDER BY TimeStamp ASC ) UPDATE TargetRow SET Locked = 'True' OUTPUT inserted.ID, inserted.TimeStamp, inserted.Locked, inserted.Deleted;
关键细节说明:
CTE(公共表表达式)先筛选出符合条件的第一条记录,保证排序逻辑生效;UPDLOCK:给目标行加更新锁,防止其他线程同时修改;READPAST:让查询跳过已经被锁定的行,避免线程阻塞;OUTPUT子句:直接返回更新后的记录(inserted代表更新后的行数据)。
方法二:封装为存储过程(更易复用)
如果这个操作需要在多个地方调用,把逻辑封装成存储过程会更方便维护:
CREATE PROCEDURE GetAndLockNextPendingRecord AS BEGIN SET NOCOUNT ON; -- 避免返回额外的影响行数信息 WITH TargetRow AS ( SELECT TOP 1 ID, TimeStamp, Locked, Deleted FROM TableName WITH (UPDLOCK, READPAST) WHERE Locked = 'False' AND Deleted = 'False' ORDER BY TimeStamp ASC ) UPDATE TargetRow SET Locked = 'True' OUTPUT inserted.ID, inserted.TimeStamp, inserted.Locked, inserted.Deleted; END
调用方式很简单:
EXEC GetAndLockNextPendingRecord;
为什么不推荐先查询再更新?
如果先执行SELECT拿到记录,再单独执行UPDATE,中间会有时间窗口——在高并发场景下,极有可能出现多个线程同时查到同一条未锁定的记录,导致重复标记和处理。而上面的两种方式都是原子操作,数据库会保证整个逻辑的一致性,不会出现竞态问题。
内容的提问来源于stack exchange,提问作者Matthew
相关产品推荐
相关产品推荐

