在我的回答中这个问题 https://stackoverflow.com/questions/1994663/setting-version-column-in-append-only-table/1994757#1994757我建议使用单个插入语句,以及一个增加值的选择,如下所示。
Insert Into VersionTable
(Id, VersionNumber, Title, Description, ...)
Select @ObjectId, max(VersionNumber) + 1, @Title, @Description
From VersionTable
Where Id = @ObjectId
我建议这个是因为我believe该语句在并发性方面是安全的,因为如果同时运行同一对象 ID 的另一个插入,则不会有重复的版本号。
我对么?
正如保罗所写:不,这不安全,我想为此添加经验证据:创建一个表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
然后执行
SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
在我的 SQL Server 2008 上,一个 ID (662
)被创建了两次。因此,应用于单个语句的默认隔离级别是not充足的。
编辑:显然,包装INSERT
with BEGIN TRANSACTION
and COMMIT
不会修复它,因为事务的默认隔离级别仍然是READ COMMITTED
,这是不够的。请注意,将事务隔离级别设置为REPEATABLE READ
is also不够。实现上述代码的唯一方法safe是添加
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
在顶部。然而,这在我的测试中时不时地造成僵局。
编辑:我发现的唯一安全的解决方案and不产生死锁(至少在我的测试中)是显式地以独占方式锁定表(这里默认事务隔离级别就足够了)。但要小心;这个解决方案可能kill表现:
...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...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)