1

如果这个问题的答案取决于 DMBS,我很想听听 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
)

我们有将新记录插入Versions表中的存储过程。如果它们同时运行,我们需要确保没有两个版本引用相同的另一个版本,因此版本层次结构中不能有任何分叉(除非我们故意创建一个分叉):

How it should run                          

Transaction 1 reads current version 17
Transaction 1 writes new version 18 based on 17
Transaction 2 reads current version 18
Transaction 2 writes new version 19 based on 18

How it should NOT run

Transaction 1 reads current version 17
Transaction 2 reads current version 17
Transaction 1 writes new version 18 based on 17
Transaction 2 writes new version 19 based on 17

在第二种情况下,我们有两个基于相同版本的版本。

所以我们需要一种方法来序列化这两个事务。首先,我们想到了使用ISOLATION LEVEL SERIALIZABLE,但隔离级别对 没有任何影响INSERTs,因此它们不是解决方案。

Versions另一种方法是在事务开始时获取表上的锁。这可能适用于 SQL Server,但不适用于 Oracle,因为 OracleLOCK TABLE IN EXCLUSIVE MODE不会阻止事务 2 读取表。

那么这种问题的最佳解决方案是什么?

4

3 回答 3

1

看来您确定最新版本是

 select MAX(ID) from Versions

因此,如果它包含在可序列化事务中,作为插入的一部分或单独包含,那么插入将是可序列化的

set transaction isolation level serializable

begin tran
    insert Versions (basedonversion)
    select MAX(ID) from Versions
commit tran 
于 2013-08-13T08:47:51.423 回答
1

您可以对版本使用序列。序列对于并发操作是安全的

所以;

Transaction 1 reads nextvalue version 18 ( So current is 18 - 1 = 17 )
Transaction 2 reads nextvalue version 19 ( So current is 19 - 1 = 18)
Transaction 1 writes new version 18 based on 17
Transaction 2 writes new version 19 base on 18
于 2013-08-13T08:42:11.097 回答
1

这被称为“丢失更新问题”,它有多种解决方案。并非所有这些都需要悲观锁。

一种方法是修改“写入”方法以在 WHERE 子句中使用谓词,如果当前版本与会话读取的版本不同,则该谓词将停止更新版本。

IE

  1. 事务 1 读取当前版本 17
  2. 事务 2 读取当前版本 17
  3. 事务1基于17写新版本18
  4. 事务 2 尝试写入新版本 18,但UPDATE v=18 WHERE v=17结果为“0 条记录已更新”,会话将此作为错误处理,例如“另一个用户已修改此版本,请重新查询并重试”。

我在 2005 年写过这篇文章,并列出了一些方法:

http://jeffkemponoracle.com/2005/10/21/avoiding-lost-updates-protecting-data-in-a-multi-user-environment/

于 2013-08-14T05:39:43.450 回答