Dav*_*all 6 t-sql sql-server concurrency
在我对这个SO问题的回答中,我建议使用单个insert语句,使用select增加一个值,如下所示.
Insert Into VersionTable
(Id, VersionNumber, Title, Description, ...)
Select @ObjectId, max(VersionNumber) + 1, @Title, @Description
From VersionTable
Where Id = @ObjectId
Run Code Online (Sandbox Code Playgroud)
我建议这是因为我认为这个语句在并发性方面是安全的,因为如果同时运行同一个对象id的另一个插入,则不可能有重复的版本号.
我对么?
Hei*_*nzi 12
正如保罗写道:不,这不安全,我想为此添加经验证据:创建一个Table_1包含一个字段ID和一个有价值记录的表0.然后在两个Management Studio查询窗口中同时执行以下代码:
declare @counter int
set @counter = 0
while @counter < 1000
begin
set @counter = @counter + 1
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
end
Run Code Online (Sandbox Code Playgroud)
然后执行
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
Run Code Online (Sandbox Code Playgroud)
在我的SQL Server 2008上,662创建了一个ID()两次.因此,施加到单个语句的缺省隔离级别是不足够的.
编辑:显然,包装INSERTwith BEGIN TRANSACTION并且COMMIT不会修复它,因为事务的默认隔离级别仍然是READ COMMITTED,这是不够的.请注意,将事务隔离级别设置REPEATABLE READ为也是不够的.使上述代码安全的唯一方法是添加
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
Run Code Online (Sandbox Code Playgroud)
在顶部.然而,这在我的测试中偶尔会导致死锁.
编辑:我发现唯一安全且不会产生死锁的解决方案(至少在我的测试中)是显式锁定表(这里默认的事务隔离级别就足够了).要小心; 此解决方案可能会破坏性能
...loop stuff...
BEGIN TRANSACTION
SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0
INSERT INTO Table_1
SELECT MAX(ID) + 1 FROM Table_1
COMMIT
...loop end...
Run Code Online (Sandbox Code Playgroud)
我认为你的假设是不正确的。当您查询 VersionNumber 表时,您仅对该行放置读锁。这不会阻止其他用户读取同一个表中的同一行。因此,两个进程有可能同时读取VersionNumber表中的同一行并生成相同的VersionNumber值。
| 归档时间: |
|
| 查看次数: |
3428 次 |
| 最近记录: |