一尘不染

SQL Server竞争条件问题

sql

(注意:这是针对MS SQL Server的)

假设您有一个具有主键标识列和CODE列的表ABC。我们希望此处的每一行都有一个唯一的,顺序生成的代码(基于一些典型的校验位公式)。

假设您有另一个仅具有一行的表DEF,该表存储下一个可用的CODE(想象一个简单的自动编号)。

我知道像下面这样的逻辑将呈现一种竞争状态,其中两个用户可能最终得到相同的CODE:

1) Run a select query to grab next available code from DEF
2) Insert said code into table ABC
3) Increment the value in DEF so it's not re-used.

我知道,两个用户可能会卡在步骤1)中,并最终在ABC表中得到相同的CODE。

处理这种情况的最佳方法是什么?我以为可以将“ begin tran” /“ commit
tran”包装在这个逻辑上,但是我认为那没有用。我有一个像这样的存储过程来测试,但是当我从MS中的两个不同窗口运行时,我并没有避免竞争条件:

begin tran

declare @x int

select   @x= nextcode FROM  def

waitfor delay '00:00:15'

update def set nextcode = nextcode + 1

select @x

commit tran

有人可以阐明这一点吗?我认为该事务将阻止另一个用户在第一个事务完成之前访问我的NextCodeTable,但是我想我对事务的理解是有缺陷的。

编辑:我试图将等待移动到“更新”语句后,并且我得到了两个不同的代码…但是我怀疑。我在那儿有一个waitfor语句来模拟一个延迟,因此可以很容易地看到竞争状况。我认为关键问题是我对交易方式的错误认识。


阅读 206

收藏
2021-03-10

共1个答案

一尘不染

将事务隔离级别设置为可序列化。
在较低的隔离级别,其他事务可以读取该事务中已读取(但尚未修改)的行中的数据。因此,两个事务确实可以读取相同的值。在非常低的隔离度(读取未提交)下,其他事务甚至可以在修改数据之后(但在提交之前)读取数据…

在此处查看有关SQL
Server隔离级别的详细信息

因此,最重要的是隔离级别在这里至关重要,可以控制其他事务进入该级别的访问级别。

笔记。在链接中,关于 “可 序列化
语句”无法读取已被其他事务修改但尚未提交的数据
这是因为锁是在修改行时而不是在Begin Trans发生行时放置的,因此您所做的可能仍然允许另一个事务读取旧值,直到修改该值为止。因此,我将更改逻辑以在阅读时在同一条语句中对其进行修改,从而同时锁定它。

begin tran
declare @x int
update def set @x= nextcode, nextcode += 1
waitfor delay '00:00:15'
select @x
commit tran
2021-03-10