2017-08-03 75 views
0

我们需要为我们的事务生成序列号。当并发用户试图同时预订事务时,我们遇到了sqlcode = -911,sqlstate = 40001,sqlerrmc = 2(死锁)。当前发生死锁是因为它正在读取并更新到相同的记录。我们如何设计这样才能防止死锁?生成序列号遇到死锁

+2

请提供更多信息,究竟是什么造成死锁,调用代码是什么,db设计是什么?目前没有什么可继续。 – GurV

+1

在这种情况下通常要做的事情就是赶上例外情况并再次尝试。另一种方法是在数据库表中创建事务并让数据库本身为其生成主键。 –

+0

您可以使用同步关键字来防止死锁。同步块确保只有在当前线程成功输入对象的监视器后才会调用作为对象成员的方法。一旦一个线程在一个实例上输入任何同步方法,其他线程就不能输入任何其他同步方法。 – Tehmina

回答

0

围绕这个问题有很多解决方法,其中一些问题比其他问题少。

如果所有对象都被锁定在相同的层次序列中,则可以防止死锁。 [https://en.wikipedia.org/wiki/Deadlock#Prevention]

然而,完全防止死锁的餐饮哲学家问题的解决方案往往不如简单地回滚事务和重试。你需要测试你的解决方案。

如果您正在寻找数据端解决方案,那么老式(可能性能不足)方法就是通过建立严格的锁定序列来强制要求获取新的事务ID。

一个快速的解决方案(在发布之前在负载下测试这个解决方案!)可以使用TRANSACTION边界并且具有控制行充当关守。这是一个愚蠢的例子,它演示了基本的技术。 它没有错误检查,并回收鬼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; 

我们的想法是,使这个原子,单线程,系列化操作非常,在持续时间很短的 - - 做只有需要什么来安全地保留身份证并避开。

通过第一步更新第0行,如果所有ID请求都符合此标准,则竞争用户将在第一步之后简单排队。

一旦您保留您的ID,请关闭并执行您喜欢的操作,并且您可以使用新的事务来更新您创建的行。

您需要覆盖后面的步骤决定为ROLLBACK的情况,因为表中现在会有一个虚影行。你会想要一个方法来回收这些;可以使用各种简单的解决方案。

1

创建一个包含单个数据行的“种子”表。 此“种子”表格行保存“下一个顺序”值。

当您希望使用“Next Sequential”值插入新的业务数据行时。执行以下步骤。

1)。在“seed”表上打开UPDATE游标并获取当前行。这使您可以独占控制种子值。 2)。你将使用这个提取的行作为“下一个值”...但是在这之前,你可以使用3)。增加提取的“下一个值”并提交更新。这个提交关闭你的光标,并用新的“下一个值”释放种子行。

你现在可以自由使用你的“下一个价值”。

+1

您可能遇到的其他名称描述了这种方法:高水位标记,序列ID表和主交易表。 ++ Hector可以更有效地使用专用的表格。不确定CURSOR是否比简单的TRANSACTION边界更有效率,但是您可以同时使用这两者来查看您的环境中的工作情况。 –

+0

我在许多企业生产系统中使用Cursor进行更新。这种方法以有序的方式排队请求“下一个值”,我已经看到它在主机z/OS DB2上支持非常高的TPS 100 - > 1000。 – Hector

+0

非常好。我想我会加入我的个人“额外时间”来做清单,试图在两种技术之间进行性能比较。因为,知道很好,玩得很开心。 :-) –