我们遇到数据库死锁的一个经典原因是两个事务以不同的顺序插入和更新表。
例如,事务A在表A中插入,然后在表B中插入。
事务B在表B中插入后跟A。
这种情况总是存在数据库死锁的风险(假设您没有使用可序列化的隔离级别)。
我的问题是:
您在设计中遵循哪种模式,以确保所有交易都以相同的顺序插入和更新。 我正在阅读的一本书 - 建议您可以按表格的名称对语句进行排序。你有没有做过这样或不同的事情 - 这会强制所有插入和更新的顺序相同?
删除记录怎么样?删除需要从子表开始,更新和插入需要从父表开始。你如何确保这不会陷入僵局?
答案 0 :(得分:13)
答案 1 :(得分:7)
死锁并不重要。只是准备好在失败时重试你的交易。
并保持简短。由触摸极少数记录的查询组成的短事务(通过索引的魔力)是最小化死锁的理想选择 - 锁定的行数更少,并且时间更短。
您需要知道现代数据库引擎不会锁定表格;他们锁定行;所以死锁的可能性不大。
您还可以通过使用MVCC和CONSISTENT READ事务隔离级别来避免锁定:某些线程只会看到陈旧数据,而不是锁定。
答案 2 :(得分:3)
仔细设计数据库进程,尽可能消除涉及多个表的事务。当我进行数据库设计控制时,从来没有出现死锁的情况,我无法设计导致它的条件。这并不是说它们不存在,而且在我有限的经验之外的情况下可能比比皆是;但我不乏改进设计导致这些问题的机会。一个明显的策略是从一个按时间顺序排列的只写表开始,用于插入没有相互依赖关系的新的完整原子事务,并在有序的异步过程中应用它们的效果。
始终使用数据库默认隔离级别和锁定设置,除非您完全确定它们带来的风险,并通过测试证明了这一点。如果可能的话,首先重新设计您的过程。然后,消除风险所需的保护增加最少(并进行测试以证明它。)不要增加限制性“以防万一” - 这通常会导致意想不到的后果,有时是你想要避免的类型。
要从另一个方向重复这一点,您将在本网站和其他网站上阅读的大部分内容都主张更改数据库设置以处理交易风险和锁定问题,这是误导性和/或错误的,如他们经常互相冲突。可悲的是,特别是对于SQL Server,我发现没有任何文档来源不会让人感到困惑和不足。
答案 3 :(得分:2)
我分析所有数据库操作,以确定每个数据库操作是否需要在多语句事务中,然后对于每个这样的情况,需要什么样的最小隔离级别来防止死锁...正如您所说的可序列化肯定会这样做......
通常,只有极少数数据库操作首先需要多语句事务,其中只有少数需要可序列化隔离以消除死锁。
对于那些做的人,在开始之前设置该事务的隔离级别,并在提交后将其重置为默认值。
答案 4 :(得分:2)
我发现我在避免死锁方面做过的最好的投资之一就是使用可以命令数据库更新的对象关系映射器。确切的顺序并不重要,只要每个事务以相同的顺序写入(并以完全相反的顺序删除)。
这避免了大多数开箱即用的死锁的原因是你的操作总是表A首先是表A,然后是表B,然后是表C(可能取决于表B)。
只要您在存储过程或数据层的访问代码中小心谨慎,就可以获得类似的结果。唯一的问题是它需要很好的关心手工完成它,而具有工作单元概念的ORM可以自动化大多数情况。
更新:删除应该向前运行以验证所有内容都是您期望的版本(您仍需要记录版本号或时间戳),然后在所有内容验证后向后删除。因为这一切都应该在一次交易中发生,所以不应该存在从你身下发生变化的可能性。 ORM向后执行此操作的唯一原因是遵守关键要求,但如果您执行检查,您将获得所需的所有锁定。
答案 5 :(得分:0)
如果数据库锁定了ENTIRE表,那么您的示例只会出现问题。如果你的数据库正在这样做...运行:)