尽管死锁不能完全避免,但遵守特定的编码惯例可以将发生死锁的机会降至最低。将死锁减至最少可以增加事务的吞吐量并减少系统开销,因为只有很少的事务:

  • 回滚,撤消事务执行的所有工作。
     
  • 由于死锁时回滚而由应用程序重新提交。
     

下列方法有助于将死锁减至最少:

  • 按同一顺序访问对象。
     
  • 避免事务中的用户交互。
     
  • 保持事务简短并处于一个批处理中。
     
  • 使用较低的隔离级别。
     
  • 使用基于行版本控制的隔离级别。
    • 将READ_COMMITTED_SNAPSHOT 数据库选项设为 ON,使得已提交读事务使用行版本控制。
       
    • 使用快照隔离。
       
  • 使用绑定连接。

    按同一顺序访问对象

    如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务先获取 Supplier 表上的锁,然后获取 Part 表上的锁,则在其中一个事务完成之前,另一个事务将在 Supplier 表上被阻塞。当第一个事务提交或回滚之后,第二个事务将继续执行,这样就不会发生死锁。将存储过程用于所有数据修改可以使对象的访问顺序标准化。

  • 将死锁减至最少_第1张图片

避免事务中的用户交互


 

避免编写包含用户交互的事务,因为没有用户干预的批处理的运行速度远快于用户必须手动响应查询时的速度(例如回复输入应用程序请求的参数的提示)。例如,如果事务正在等待用户输入,而用户去吃午餐或甚至回家过周末了,则用户就耽误了事务的完成。这将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚后才会释放。即使不出现死锁的情况,在占用资源的事务完成之前,访问同一资源的其他事务也会被阻塞。

保持事务简短并处于一个批处理中

在同一数据库中并发执行多个需要长时间运行的事务时通常会发生死锁。事务的运行时间越长,它持有排他锁或更新锁的时间也就越长,从而会阻塞其他活动并可能导致死锁。

保持事务处于一个批处理中可以最小化事务中的网络通信往返量,减少完成事务和释放锁可能遭遇的延迟。

使用较低的隔离级别

确定事务是否能在较低的隔离级别上运行。实现已提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如已提交读)比使用较高的隔离级别(例如可序列化)持有共享锁的时间更短。这样就减少了锁争用。

使用基于行版本控制的隔离级别

如果将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,则在已提交读隔离级别下运行的事务在读操作期间将使用行版本控制而不是共享锁。

注意:
某些应用程序依赖于已提交读隔离的锁定和阻塞行为。对于这些应用程序,要启用此选项必须进行一些更改。

 

 

快照隔离也使用行版本控制,该级别在读操作期间不使用共享锁。必须将 ALLOW_SNAPSHOT_ISOLATION 数据库选项设置为 ON,事务才能在快照隔离下运行。

实现这些隔离级别可使得在读写操作之间发生死锁的可能性降至最低。

使用绑定连接

使用绑定连接,同一应用程序打开的两个或多个连接可以相互合作。可以像主连接获取的锁那样持有次级连接获取的任何锁,反之亦然。这样它们就不会互相阻塞。