18

我们有数据库死锁的一个典型原因是当两个事务以不同的顺序插入和更新表时。

例如,事务 A 在表 A 中插入,然后在表 B 中插入。

事务 B 在表 B 中插入,然后是 A。

这种情况总是存在数据库死锁的风险(假设您没有使用可序列化的隔离级别)。

我的问题是:

  1. 您在设计中遵循什么样的模式来确保所有事务都以相同的顺序插入和更新。我正在阅读的一本书 - 建议您可以按表格名称对语句进行排序。您是否做过类似或不同的事情 - 这会强制所有插入和更新都以相同的顺序进行?

  2. 删除记录怎么办?删除需要从子表开始,更新和插入需要从父表开始。你如何确保这不会陷入僵局?

4

6 回答 6

13
  1. 所有事务都以相同的顺序插入\更新。
  2. 删除;识别要在事务之外删除的记录,然后尝试在尽可能小的事务中删除,例如通过查找阶段确定的主键或类似内容进行查找。
  3. 一般是小额交易。
  4. 索引和其他性能调整既可以加快事务速度,又可以促进表扫描上的索引查找。
  5. 避免使用“热表”,例如一个表,其中一个表具有其他表主键的递增计数器。任何其他“总机”类型的配置都是有风险的。
  6. 特别是如果不使用 Oracle,请详细了解目标 RDBMS 的外观行为(乐观/悲观、隔离级别等)。确保您不会像某些 RDMS 那样让行锁升级为表锁。
于 2009-04-12T04:58:07.793 回答
7

死锁不是什么大问题。只要准备好在失败时重试您的交易。

并保持简短。由涉及很少记录的查询组成的短事务(通过索引的魔力)是减少死锁的理想选择——锁定的行更少,时间更短。

您需要知道现代数据库引擎不会锁定表。他们锁定行;所以死锁的可能性要小一些。

您还可以通过使用 MVCC 和 CONSISTENT READ 事务隔离级别来避免锁定:一些线程不会锁定,而是只会看到陈旧的数据。

于 2009-04-10T16:28:50.567 回答
3
  1. 仔细设计您的数据库流程,以尽可能多地消除涉及多个表的事务。当我拥有数据库设计控制权时,从来没有发生过我无法设计出导致它的条件的死锁情况。这并不是说它们不存在,并且可能在我有限的经验之外的情况下比比皆是。但我不乏改进导致这些问题的设计的机会。一个明显的策略是从一个按时间顺序的只写表开始,用于插入没有相互依赖关系的新的完整原子事务,并将它们的效果应用到有序的异步过程中。

  2. 始终使用数据库默认隔离级别和锁定设置,除非您绝对确定它们会带来什么风险,并且已经通过测试证明了这一点。如果可能的话,首先重新设计您的流程。然后,尽量减少消除风险所需的保护(并进行测试以证明它)。不要“以防万一”增加限制性——这通常会导致意想不到的后果,有时是您打算避免的那种。

  3. 从另一个方向重复这一点,您将在本网站和其他网站上读到的大多数提倡更改数据库设置以处理事务风险和锁定问题的内容是误导和/或错误的,正如它们如何相互冲突所证明的那样经常。可悲的是,特别是对于 SQL Server,我没有发现任何文档来源不是令人绝望的混乱和不充分的。

于 2009-04-12T05:19:25.760 回答
2

我分析所有数据库操作以确定,对于每一个,它是否需要在多语句事务中,然后对于每个这样的情况,需要什么最小隔离级别来防止死锁......正如你所说,可序列化肯定会做所以...

通常,只有极少数数据库操作首先需要多语句事务,其中只有少数需要可序列化隔离以消除死锁。

对于那些这样做的人,请在开始之前为该事务设置隔离级别,并在提交后将其重置为默认值。

于 2009-04-09T21:05:43.827 回答
2

我发现我在避免死锁方面做过的最好的投资之一就是使用可以订购数据库更新的对象关系映射器。确切的顺序并不重要,只要每个事务都以相同的顺序写入(并以完全相反的顺序删除)。

这样可以避免大多数开箱即用的死锁的原因是您的操作总是首先是表 A,然后是表 B,然后是表 C(这可能取决于表 B)。

只要您小心处理存储过程或数据层的访问代码,就可以获得类似的结果。唯一的问题是手工操作需要非常小心,而具有工作单元概念的 ORM 可以自动化大多数情况。

更新:删除应该向前运行以验证所有内容都是您期望的版本(您仍然需要记录版本号或时间戳),然后在所有内容验证后向后删除。由于这一切都应该发生在一笔交易中,因此不应该存在从您下面改变某些东西的可能性。ORM 向后执行的唯一原因是遵守关键要求,但如果您向前执行检查,您将拥有所需的所有锁。

于 2009-04-09T21:18:06.027 回答
0

如果数据库锁定了 ENTIRE 表,您的示例只会成为问题。如果您的数据库正在这样做...运行:)

于 2009-04-09T21:10:25.813 回答