Navicat 博客

关系数据库事务中的对象锁定 - 第 3 部分 2021 年 6 月 22 日,由 Robert Gravelle 撰写

避免死锁或将死锁减至最少

在关系数据库系统(RDBMS)中,死锁是两个并发事务无法进行的情况,因为每个事务都在等待另一个释放锁定。在本系列的第 1 部分中,我们知道了什么是关系数据库中的对象锁定、不同类型的锁定和死锁。然后,在第 2 部分中,我们比较了悲观锁定和乐观锁定的优缺点。在本篇文章中,我们将探讨导致死锁的几个原因,以及避免死锁或至少将死锁减至最少的策略。

低效的查询

死锁在某种程度上是不可避免的,但只要两个事务的其中一个及时结束,死锁就会很少发生并且不会导致灾难。事实证明,阻塞问题的最常见来源之一是冗长且低效的 SQL 语句,这些语句会导致数据库在运行时“挂起”。这些可以通过两个步骤来补救:

  • 优化性能不佳的 SQL 语句,以便在最短的时间内释放锁。
  • 在同一会话中执行任何长时间运行的 SQL 语句之前,标识是否可以释放锁。

例如,如果由于执行 DELETE 语句而获取锁,并且紧随其后的是执行完整表扫描的 SELECT 语句,你应该确定是否可以在它们之间执行 COMMIT 语句。这应该有助于更早地释放锁。

嵌套事务

阻塞问题的另一个常见原因是睡眠会话已经失去了对事务嵌套级别的跟踪。例如,如果应用程序取消 SQL 语句或超时但未发出 COMMIT 或 ROLLBACK 语句,则资源可能会无限期地保持锁定状态。处理此问题的一些方法包括:

  • 在出现任何应用程序错误后,在应用程序的错误处理程序中提交 IF@@TRANCOUNT > 0 ROLLBACK TRAN 语句。
  • 在启动事务的任何存储过程中包括 SET XACT_ABORT ON 语句,特别是如果它们在错误后没有清理。通过这样做,如果发生运行时错误,任何打开的事务都将中止并将控制权返回给客户端。
  • 如果连接池由打开连接并在将连接返回到池之前运行一些查询的应用程序使用,那么你可能需要考虑暂时禁用连接池。通过这样做,数据库服务器连接被物理注销,导致服务器回滚任何打开的事务。

取得部分结果

一个鲜为人知的死锁来源是不会一次性取得所有结果行的应用程序。这是一个问题,因为当查询已发送到服务器时,应用程序必须能够取得所有结果行以完成查询。如果没有发生这种情况,表会被锁定,这会导致其他用户阻塞。因此,请尝试对你的应用程序进行编码,以便它们取得所需的全部行,而不是将其分散到多次迭代中。

总结

今天的文章列出了一些导致死锁的原因,以及避免死锁和在死锁发生时处理死锁的策略。下周,我们将进入一个全新的主题。



Rob Gravelle 居住在加拿大渥太华,是一名有 20 多年经验的 IT 专家。过往,Rob 曾为与情报有关的组织(如加拿大边境服务局和各种商业组织)构建系统。在业余时间,Rob 是一名出色的吉他演奏家,他拥有多张 CD 和数字发行版

Navicat 文章
频道条目
分享
文章归档