SQLSERVER语句交错引发的死锁问题怎么解决

56次阅读
没有评论

共计 5739 个字符,预计需要花费 15 分钟才能阅读完成。

这篇文章主要讲解了“SQLSERVER 语句交错引发的死锁问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着丸趣 TV 小编的思路慢慢深入,一起来研究和学习“SQLSERVER 语句交错引发的死锁问题怎么解决”吧!

一:背景 1. 讲故事

相信大家在使用 SQLSERVER 的过程中经常会遇到 阻塞 和 死锁,尤其是 死锁,比如下面的输出:

(1 row affected) Msg 1205, Level 13, State 51, Line 5 Transaction (Process ID 62) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

二:死锁简析 1. 一个测试案例

开启两个会话 65 和 66,分别使用如下查询。

--  会话  65 --
BEGIN TRAN
UPDATE dbo.Employees SET Title= Dr.  WHERE EmployeeID=1;
WAITFOR DELAY  00:00:10 
SELECT * FROM dbo.Orders WHERE OrderID=10258
ROLLBACK
--  会话  66 --
BEGIN TRAN
UPDATE dbo.Orders SET ShipAddress= 上海  WHERE OrderID=10258
WAITFOR DELAY  00:00:10 
SELECT * FROM dbo.Employees WHERE EmployeeID=1;
ROLLBACK

两个会话非常简单,交错的对 Employees 和 Orders 进行 SELECT 和 UPDATE 操作,稍等几秒后就会出现死锁。

2. 寻找死锁源头

当我们的应用程序拿到了这样的输出其实作用是不大的,要想溯源最好就是通过不断的对 SQLSERVER 进行监视来捕获死锁时的上下文信息,手段也有很多:

SQL Server Profile

DBCC TRACEON(1222)

DMV VIEW

这里我们就用第一种方式,一定要勾选 TextData 项,因为这里面会有死锁上下文信息的 xml 表示,截图如下:

将 profile 开启后,重新执行刚才的两个查询,一旦出现死锁,profile 就会成功捕获,然后 copy 出 TextData 项,截图如下:

deadlock-list 
  deadlock victim= process2d69c9748c8 
  process-list 
  process id= process2d69c9748c8  taskpriority= 0  logused= 324  waitresource= KEY: 7:72057594043170816 (8194443284a0)  waittime= 1304  ownerId= 70740  transactionname= user_transaction  lasttranstarted= 2023-02-19T22:11:26.413  XDES= 0x2d6a0200428  lockMode= S  schedulerid= 5  kpid= 13816  status= suspended  spid= 66  sbid= 0  ecid= 0  priority= 0  trancount= 1  lastbatchstarted= 2023-02-19T22:11:26.413  lastbatchcompleted= 2023-02-19T22:11:26.410  lastattention= 1900-01-01T00:00:00.410  clientapp= Microsoft SQL Server Management Studio - Query  hostname= DESKTOP-STS8TPB  hostpid= 1696  loginname= DESKTOP-STS8TPB\Administrator  isolationlevel= read committed (2)  xactid= 70740  currentdb= 7  currentdbname= Northwind  lockTimeout= 4294967295  clientoption1= 671090784  clientoption2= 390200 
  executionStack 
  frame procname= adhoc  line= 5  stmtstart= 24  stmtend= 128  sqlhandle= 0x020000007383d935b349bc173c0f104de14945e9a526322b0000000000000000000000000000000000000000 
unknown  /frame 
  frame procname= adhoc  line= 5  stmtstart= 204  stmtend= 294  sqlhandle= 0x020000002c3b203105961d63d10b17e54ed6ac081105f9450000000000000000000000000000000000000000 
unknown  /frame 
  /executionStack 
  inputbuf 
BEGIN TRAN
UPDATE dbo.Orders SET ShipAddress= apos; 上海 apos; WHERE OrderID=10258
WAITFOR DELAY  apos;00:00:10 apos;
SELECT * FROM dbo.Employees WHERE EmployeeID=1;
ROLLBACK
  /inputbuf 
  /process 
  process id= process2d6ae694ca8  taskpriority= 0  logused= 368  waitresource= KEY: 7:72057594044088320 (59ce0997f9b8)  waittime= 3468  ownerId= 70716  transactionname= user_transaction  lasttranstarted= 2023-02-19T22:11:24.247  XDES= 0x2d6a7284428  lockMode= S  schedulerid= 9  kpid= 7124  status= suspended  spid= 65  sbid= 0  ecid= 0  priority= 0  trancount= 1  lastbatchstarted= 2023-02-19T22:11:24.247  lastbatchcompleted= 2023-02-19T22:11:24.247  lastattention= 1900-01-01T00:00:00.247  clientapp= Microsoft SQL Server Management Studio - Query  hostname= DESKTOP-STS8TPB  hostpid= 1696  loginname= DESKTOP-STS8TPB\Administrator  isolationlevel= read committed (2)  xactid= 70716  currentdb= 7  currentdbname= Northwind  lockTimeout= 4294967295  clientoption1= 671090784  clientoption2= 390200 
  executionStack 
  frame procname= adhoc  line= 5  stmtstart= 26  stmtend= 118  sqlhandle= 0x02000000dd7720067e0519b8a368501716c04b4b50cfe6be0000000000000000000000000000000000000000 
unknown  /frame 
  frame procname= adhoc  line= 5  stmtstart= 196  stmtend= 282  sqlhandle= 0x0200000093f01512208755a056f5f28930fbd3dedf58a2850000000000000000000000000000000000000000 
unknown  /frame 
  /executionStack 
  inputbuf 
BEGIN TRAN
UPDATE dbo.Employees SET Title= apos;Dr. apos; WHERE EmployeeID=1;
WAITFOR DELAY  apos;00:00:10 apos;
SELECT * FROM dbo.Orders WHERE OrderID=10258
ROLLBACK
  /inputbuf 
  /process 
  /process-list 
  resource-list 
  keylock hobtid= 72057594043170816  dbid= 7  objectname= Northwind.dbo.Employees  indexname= PK_Employees  id= lock2d69ccbbb80  mode= X  associatedObjectId= 72057594043170816 
  owner-list 
  owner id= process2d6ae694ca8  mode= X / 
  /owner-list 
  waiter-list 
  waiter id= process2d69c9748c8  mode= S  requestType= wait / 
  /waiter-list 
  /keylock 
  keylock hobtid= 72057594044088320  dbid= 7  objectname= Northwind.dbo.Orders  indexname= PK_Orders  id= lock2d69ccbbf80  mode= X  associatedObjectId= 72057594044088320 
  owner-list 
  owner id= process2d69c9748c8  mode= X / 
  /owner-list 
  waiter-list 
  waiter id= process2d6ae694ca8  mode= S  requestType= wait / 
  /waiter-list 
  /keylock 
  /resource-list 
  /deadlock 
 /deadlock-list

虽然上面有图形化表示,但在生产环境下参考价值并不多,因为这张图蕴含的信息比较少,熟读和整理 xml 的内容就非常必要了,截图如下:

仔细观察上面的这张图可以清晰的看到,spid=66 持有了 Orders.PK_Orders 索引上哈希码为 59ce0997f9b8 键值的 X 锁,之后需要再次获取 Employees.PK_Employees 索引上哈希码为 8194443284a0 键值上的 S 锁,很不巧的是,此时的 Employees.PK_Employees 索引上哈希码为 8194443284a0 的键值已经被 spid=65 的会话附加了 X 锁,这是一种典型的相互等待造成的死锁。

同时也可以观察到,我们的语句是一个 adhoc 即时查询,其外层也没有 存储过程 之类的包围语句。

3. 寻找解决方案

知道了是什么语句和什么语句之间的冲突之后,后面的问题就比较简单了,常见措施如下:

使用 nolock 脏读

由于冲突中涉及到了 S 锁,其实绝大多数系统对脏读不是特别敏感,所以使用 nolock 无锁提示是一个好办法。

BEGIN TRAN
UPDATE dbo.Orders SET ShipAddress= 上海  WHERE OrderID=10258
WAITFOR DELAY  00:00:10 
SELECT * FROM dbo.Employees WITH(NOLOCK) WHERE EmployeeID=1;
ROLLBACK

BEGIN TRAN UPDATE dbo.Employees SET Title= Dr.  WHERE EmployeeID=1; WAITFOR DELAY  00:00:10 SELECT * FROM dbo.Orders WITH(NOLOCK) WHERE OrderID=10258 ROLLBACK

使用 MVCC 多版本控制

现代化的关系型数据库都支持 快照读 来解决 并发读写 的冲突,同时又能保证不脏读,简而言之就是在事务修改时将修改前的数据存到 tempdb 中来形成字段的版本化。

首先需要从 数据库 级别开启它。

ALTER DATABASE Northwind SET ALLOW_SNAPSHOT_ISOLATION ON

然后在各自事务中显式使用 SNAPSHOT 隔离级别查询,参考 sql 如下:

--  会话  65 --
SET TRAN ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
UPDATE dbo.Employees SET Title= Dr.  WHERE EmployeeID=1;
WAITFOR DELAY  00:00:10 
SELECT * FROM dbo.Orders WHERE OrderID=10258
ROLLBACK
--  会话  66 --
SET TRAN ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
UPDATE dbo.Orders SET ShipAddress= 上海  WHERE OrderID=10258
WAITFOR DELAY  00:00:10 
SELECT * FROM dbo.Employees WHERE EmployeeID=1;
ROLLBACK

感谢各位的阅读,以上就是“SQLSERVER 语句交错引发的死锁问题怎么解决”的内容了,经过本文的学习后,相信大家对 SQLSERVER 语句交错引发的死锁问题怎么解决这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是丸趣 TV,丸趣 TV 小编将为大家推送更多相关知识点的文章,欢迎关注!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-13发表,共计5739字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)