SQL Server中的5种死锁类型
1)书签查找死锁(Bookmark lookup deadlock)
书签查找是SQL Server中常见的死锁。它是由于select语句和DML(插入、更新和删除)语句之间的冲突而发生的。通常,SQLServer选择select语句作为死锁牺牲品,因为它不会导致数据更改并且回滚很快。为避免书签查找,您可以使用覆盖索引。您也可以在select语句中使用NOLOCK查询提示,但它会读取未提交的数据。
2) 范围扫描死锁(Range scan deadlock)
有时,我们在服务器级别或会话级别使用SERIALIZABLE隔离级别。它是用于并发控制的限制性隔离级别,可以创建范围扫描锁而不是页级或行级锁。在SERIALIZABLE隔离级别中,如果数据被修改但等待在事务中提交,用户将无法读取数据。同样,如果一个事务读取数据,另一个事务不能修改它。它提供了最低的并发性,因此我们应该在特定的应用程序需求中使用此隔离级别。
3)级联约束死锁(Cascading constraint deadlock)
SQL Server通过外键约束使用表之间的父子关系。在这种情况下,如果从父表中更新或删除记录,则需要对子表进行必要的锁定以防止出现孤立记录。要消除这些死锁,应该始终先修改子表中的数据,然后再修改父数据。还可以使用DELETE CASCADE或UPDATECASCADE选项直接处理父表。还应该在外键列上创建适当的索引。
4)查询内并行死锁(Intra-query parallelism deadlock)
一旦用户向SQL查询引擎提交查询,查询优化器就会构建优化的执行计划。它可以根据查询成本、最大并行度(MAXDOP)和并行成本阈值以串行或并行顺序执行查询。
在并行模式下,SQLServer分配多个线程。有时对于并行模式下的大型查询,这些线程开始相互阻塞。最终,它转化为死锁。在这种情况下,您需要查看执行计划以及并行配置的MAXDOP和成本阈值。您还可以在会话级别指定MAXDOP来解决死锁情况。
5)反向对象顺序死锁 (Reverse object order deadlock)
在这种类型的死锁中,多个事务在T-SQL中以不同的顺序访问对象。这会导致每个会话的资源阻塞并将其转换为死锁。所以按逻辑顺序访问对象,以免导致死锁情况。
避免死锁的最佳方法
1. 尽量缩短交易时间; 这将避免在事务中长时间持有锁。
2. 在多个事务中以类似的逻辑方式访问对象。
3. 创建覆盖索引以减少死锁的可能性。
4. 创建索引以匹配外键列。 这样,您可以消除由于级联引用完整性而导致的死锁。
5. 使用SET DEADLOCK_PRIORITY会话变量设置死锁优先级。 如果设置死锁优先级,SQLServer会终止死锁优先级最低的会话。
6. 使用try-catch块利用错误处理。 您可以捕获死锁错误并在死锁牺牲品的情况下重新运行事务。
7. 将隔离级别更改为READ COMMITTED SNAPSHOT ISOLATION或SNAPSHOT ISOLATION。 这会更改SQLServer锁定机制。