(注意:这是针对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语句来模拟一个延迟,因此可以很容易地看到竞争状况。我认为关键问题是我对交易方式的错误认识。
将事务隔离级别设置为可序列化。 在较低的隔离级别,其他事务可以读取该事务中已读取(但尚未修改)的行中的数据。因此,两个事务确实可以读取相同的值。在非常低的隔离度(读取未提交)下,其他事务甚至可以在修改数据之后(但在提交之前)读取数据…
在此处查看有关SQL Server隔离级别的详细信息
因此,最重要的是隔离级别在这里至关重要,可以控制其他事务进入该级别的访问级别。
笔记。在链接中,关于 “可 序列化 语句”无法读取已被其他事务修改但尚未提交的数据 。 这是因为锁是在修改行时而不是在Begin Trans发生行时放置的,因此您所做的可能仍然允许另一个事务读取旧值,直到修改该值为止。因此,我将更改逻辑以在阅读时在同一条语句中对其进行修改,从而同时锁定它。
Begin Trans
begin tran declare @x int update def set @x= nextcode, nextcode += 1 waitfor delay '00:00:15' select @x commit tran