我有以下表格(这里以实体框架为模型,但我的问题与EF无关):
如您所见,这是一个版本化的Product
表。 Id
列是主键,但组合(EntityId, VersionId)
也可以是主键。 EntityId
表示实体的Id,它在实体的不同版本之间保持不变。通过使用IsDeleted = 1
写一行来删除实体。
负责数据操作的存储过程首先检查数据操作是否正常。例如,UPDATE SP检查实体是否已被删除。如果这些检查成功,则SP在Version表中生成一个新行,然后在Product
表中生成一个新行:
(pseudo-code):
sp_Product_Update:
(1) IF EXISTS (SELECT Id FROM Product WHERE IsDeleted = 1 AND EntityId = @ProductId)
RAISERROR "Entity has already been deleted"
RETURN
(2) INSERT INTO Versions ...
(3) INSERT INTO Product ... (IsDeleted = 0)
sp_Product_Delete:
(1) IF EXISTS (SELECT Id FROM Product WHERE IsDeleted = 1 AND EntityId = @ProductId)
RAISERROR "Entity has already been deleted"
RETURN
(2) INSERT INTO Versions ...
(3) INSERT INTO Product ... (IsDeleted = 1)
这很有效。
目前,我正在分析这个并发问题。想象一下以下并发场景,其中同一个实体同时调用两个SP:
Transaction 1 Transaction 2
sp_Product_Update sp_Product_Delete
(1) Check succeeds, entity has not yet been deleted.
(1) Same check.
(2) INSERT INTO Versions...
(3) INSERT INTO Product.. (IsDeleted = 1)
(2) INSERT INTO Versions...
(3) INSERT INTO Product ... (IsDeleted = 0)
正如您所看到的,此竞争条件会导致数据不一致,即IsDeleted = 0
行 IsDeleted = 1
条目之后
因此,我们必须确定避免这种竞争条件所需的隔离级别。
所以我有两个问题:
答案 0 :(得分:1)
您的解决方案需要可序列化的隔离级别,因为所有命令都需要作为一个原子操作一起执行。
如果您不使用存储过程,我建议使用专为高吞吐量的此类情况设计的optimistic locking。