我们需要为交易生成序号。当并发用户尝试同时预订事务时,我们遇到sqlcode = -911,sqlstate = 40001,sqlerrmc = 2(死锁)。目前发生死锁是因为它正在读取并更新到同一记录。我们如何设计这个以防止死锁?
答案 0 :(得分:1)
创建"种子"包含单个数据行的表。 这种种子"表格行保存"下一个顺序"值。
如果您希望使用" Next Sequential"插入新的业务数据行?值。执行以下步骤。
1)。在"种子"上打开游标以进行UPDATE。表并获取当前行。这使您可以独家控制种子值。 2)。您将使用此获取的行作为"下一个值" ...但是在此之前 3)。增加获取的"下一个值"并提交更新。此提交将关闭光标并使用新的" Next Value"。
释放种子行您现在可以自由使用您的"下一个价值"。
答案 1 :(得分:0)
围绕这个问题有很多方法,有些方法的性能低于其他方法。
如果所有对象都以相同的层次结构序列锁定,则可以防止死锁。 [https://en.wikipedia.org/wiki/Deadlock#Prevention]
然而,完全防止死锁的餐饮哲学家问题[https://en.wikipedia.org/wiki/Dining_philosophers_problem]的解决方案通常不如简单地回滚交易和重试那么高效。您需要测试您的解决方案。
如果您正在寻找数据端解决方案,那么老式(并且可能性能不足)的方法是通过建立严格的锁定序列来强制获取新的事务ID为原子。
快速解决方案(在发布到生产之前对其进行负载测试!)可能是使用TRANSACTION
边界并使控制行充当网守。这是一个愚蠢的例子,展示了基本技术。
它没有错误检查,并且回收ghost ID的代码超出了此示例的范围:
DECLARE @NewID INTEGER;
BEGIN TRANSACTION;
UPDATE [TABLE] SET [LOCKFLAG] = CURRENT_TIMESTAMP WHERE [ROW_ID] = 0;
SELECT @NewID = MAX([ROW_ID])+1 FROM [TABLE];
INSERT INTO [TABLE] ([ROW_ID]) VALUES (@NewID);
UPDATE [TABLE] SET [LOCKFLAG] = NULL WHERE [ROW_ID] = 0;
COMMIT TRANSACTION;
这个想法是让这个原子的,单线程的,序列化的操作持续时间非常短 - 只需 安全保留ID所需的内容。 / p>
通过更新第0行的第一步,如果所有ID请求都符合此标准,那么竞争用户将在第一步后排队。
保留您的身份证后,请执行您喜欢的操作,然后使用新的交易来更新您已创建的行。
您需要涵盖后面的步骤决定ROLLBACK
的情况,因为现在表中会有一个鬼行。你想要一种方法来收回那些;可以使用各种简单的解决方案。