Sql server 序列化插入
如果这个问题的答案取决于DMB,我很想听听Oracle 11g或更高版本和SQL Server 2012的答案 我们有一个表,它有一个引用自身的外键:Sql server 序列化插入,sql-server,oracle,concurrency,isolation-level,table-locking,Sql Server,Oracle,Concurrency,Isolation Level,Table Locking,如果这个问题的答案取决于DMB,我很想听听Oracle 11g或更高版本和SQL Server 2012的答案 我们有一个表,它有一个引用自身的外键: CREATE TABLE Versions ( Id INT IDENTITY(1,1) NOT NULL, [Date] DATETIME NOT NULL, BasedOnVersion INT NULL -- foreign key that references Versions ) 我们有将新记录插入Vers
CREATE TABLE Versions (
Id INT IDENTITY(1,1) NOT NULL,
[Date] DATETIME NOT NULL,
BasedOnVersion INT NULL -- foreign key that references Versions
)
我们有将新记录插入Versions
表的存储过程。如果它们同时运行,我们需要确保没有两个版本引用相同的另一个版本,因此版本层次结构中不能有任何分叉(除非我们有意创建分叉):
在第二种情况下,我们有两个基于相同版本的版本
因此,我们需要一种序列化这两个事务的方法。首先,我们考虑使用隔离级别SERIALIZABLE
,但是隔离级别对插入没有任何影响,因此它们不是解决方案
另一种方法是在事务开始时获取Versions
表上的锁。这可能适用于SQL Server,但不适用于Oracle,因为Oracle的以独占模式锁定表
不会阻止事务2读取表
那么,对于这类问题,最好的解决方案是什么呢?您可以使用版本序列。序列对于并发操作是安全的
所以,
似乎您是按照以下方式确定最新版本的:
select MAX(ID) from Versions
因此,如果它包含在可序列化事务中(作为插入的一部分或单独),那么插入将是可序列化的
set transaction isolation level serializable
begin tran
insert Versions (basedonversion)
select MAX(ID) from Versions
commit tran
这就是所谓的“丢失更新问题”,它有许多解决方案。并不是所有的锁都需要悲观锁
一种方法是修改“write”方法,在WHERE子句中使用一个谓词,如果当前版本与会话读取的版本不同,该谓词将阻止更新更新版本
i、 e
事务1读取当前版本17
事务2读取当前版本17
事务1基于17写入新版本18
事务2尝试写入新版本18,但更新v=18,其中v=17
导致“0条记录已更新”,会话将此作为错误处理,例如“其他用户已修改此版本,请重新查询并重试”
我早在2005年就写过这篇文章,列出了一些方法:
select MAX(ID) from Versions
set transaction isolation level serializable
begin tran
insert Versions (basedonversion)
select MAX(ID) from Versions
commit tran