TransactionScope默认ReadCommitted级别未锁定读取问题咨询
问题根本原因
默认ReadCommitted隔离级别下的读操作加的是共享锁(S锁),共享锁的特点是读完数据后立刻释放,不会持有到事务提交/回滚。所以多个并发事务可以同时拿到同一行的S锁,同时读到pending状态,之后都去执行业务逻辑,最后都去更新状态,就出现了重复执行的问题,这和加不加nolock没有关系。
问题解答
1、在存储过程中使用updlock会不会引发死锁之类的其他问题?
updlock(更新锁)本身就是为了减少死锁设计的:同一时间只允许一个事务拿到某行的更新锁,其他事务要等当前持有更新锁的事务释放后才能拿到,避免了「多个事务先拿S锁,再都要升级为排他锁(X锁)互相等待」的经典死锁场景。
只要满足两个条件基本不会出现死锁:
- 每次查询都是按主键/唯一索引精准定位到单行,不会扫描多余行
- 所有涉及这张表的同类查询都按照相同的顺序访问行,不要出现A先锁行1再锁行2、B先锁行2再锁行1的情况
2、只锁定查询到的指定行而非整张表的更合适实现方案
你现在用的updlock + rowlock组合就是最适配当前场景的方案:
rowlock明确提示数据库引擎使用行级锁,只要你的查询条件走了主键/唯一索引,数据库就不会升级为表锁,只会锁你查到的那一行- 写法直接加在查询语句后面即可:
SELECT transactionid, status FROM 表名 WITH (UPDLOCK, ROWLOCK) WHERE transactionid = @id
3、调整IsolationLevel的方案是否可行?
不管是RepeatableRead还是Serializable都不能精准匹配你的需求:
RepeatableRead虽然会把S锁持有到事务结束,但还是允许多个事务同时拿S锁,还是会出现多个事务同时读到pending状态的问题,仅解决了不可重复读问题Serializable隔离级别粒度太粗,会加范围锁,性能损耗大,还容易引发死锁
因此调整隔离级别的方案远不如用更新锁的方案精准高效。
4、只锁定指定transactionid的读操作、不影响事务内其他读的实现
你当前的UPDLOCK + ROWLOCK方案刚好满足这个要求:
- 这个锁只加在你当前查询到的那一行transactionid对应的行上,事务内的其他读操作只要不加锁提示,还是走正常的ReadCommitted逻辑,不会加额外的锁
- 其他并发事务查询同一个transactionid的时候,拿不到更新锁就会进入等待,等你当前事务提交/回滚后,其他事务再读就会读到已完成的状态,直接返回不会执行业务逻辑。
内容的提问来源于stack exchange,提问作者Dexters
相关产品推荐
相关产品推荐

