Hyt*_*oth 7 sql sql-server transactions
(注意:这适用于MS SQL Server)
假设您有一个带有主键标识列和CODE列的表ABC.我们希望这里的每一行都有一个独特的,顺序生成的代码(基于一些典型的校验位公式).
假设您有另一个表DEF只有一行,它存储下一个可用的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.
Run Code Online (Sandbox Code Playgroud)
我知道,两个用户可能会陷入第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
Run Code Online (Sandbox Code Playgroud)
有人可以对此有所了解吗?我认为该事务将阻止其他用户在第一个事务完成之前能够访问我的NextCodeTable,但我想我对事务的理解是有缺陷的.
编辑:我尝试将等待移到"更新"声明后,我有两个不同的代码...但我怀疑.我在那里有waitfor声明来模拟延迟,因此可以很容易地看到竞争条件.我认为关键问题是我对交易如何运作的错误认识.
将事务隔离级别设置为Serializable.
在较低的隔离级别,其他事务可以读取此事务中读取(但尚未修改)的行中的数据.因此,两个事务确实可以读取相同的值.在非常低的隔离(Read Uncommitted)下,其他事务甚至可以在修改后(但在提交之前)读取数据...
在此处查看有关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
Run Code Online (Sandbox Code Playgroud)
正如其他响应者所提到的,您可以设置事务隔离级别,以确保使用SELECT语句"读取"的任何内容都不能在事务中更改.
或者,您可以通过WITH HOLDLOCK在表名后面添加语法来专门取出DEF表上的锁,例如,
SELECT nextcode FROM DEF WITH HOLDLOCK
Run Code Online (Sandbox Code Playgroud)
这里没有太大区别,因为您的事务很小,但是为某些SELECT而不是事务中的其他SELECT取出锁定会很有用.这是"可重复性与并发性"的问题.
一些相关的MS-SQL文档.
| 归档时间: |
|
| 查看次数: |
11829 次 |
| 最近记录: |