使用 TRY/CATCH 语句解决 SQL Server 2005 死锁

偶尔偷点懒

偶尔偷点懒

2016-02-19 21:17

下面是个超简单的使用 TRY/CATCH 语句解决 SQL Server 2005 死锁教程,图老师小编精心挑选推荐,大家行行好,多给几个赞吧,小编吐血跪求~

  对于今天的 RDBMS 体系结构而言,死锁难以避免 — 在高容量的 OLTP 环境中更是极为普遍。正是由于 .NET 的公共语言运行库 (CLR) 的出现,SQL Server 2005 才得以为开发人员提供一种新的错误处理方法。在本月专栏中,Ron Talmage 为您介绍如何使用 TRY/CATCH 语句来解决一个死锁问题。一个示例死锁

  让我们从这样一个示例开始说起,它在 SQL Server 2000 和 2005 中都能引起死锁。在本文中,我使用 SQL Server 2005 的最新 CTP(社区技术预览,Community Technology Preview)版本,SQL Server 2005 Beta 2(7 月发布)也同样适用。如果您没有 Beta 2 或最新的 CTP 版本,请下载 SQL Server 2005 Express 的最新版本,用它来进行试验。

  可能发生的死锁情况有很多,但最有趣、最微妙的是那些关于阅读器和编写器互相阻塞的死锁。以下代码在 pubs 数据库中就产生了这样一个死锁。(您可以在 SQL Server 2000 的两个 Query Analyzer 窗口中或 SQL Server 2005 的两个 Management Studio queries 中并列运行这段代码。)在其中一个窗口中的代码正文前面添加下列语句:

-- Window 1 header
DECLARE @au_id varchar(11), @au_lname varchar(40)
SELECT @au_id = '111-11-1111', @au_lname = 'test1'

  在第二个窗口中添加下列语句,进行第二次连接:

-- Window 2 header
DECLARE @au_id varchar(11), @au_lname varchar(40)
SELECT @au_id = '111-11-1112', @au_lname = 'test2'

  在两个窗口中都使用下列语句作为代码正文:

-- Body for both connections:
BEGIN TRANSACTION
INSERT Authors VALUES
 (@au_id, @au_lname, '', '', '', '', '', '11111', 0)
WAITFOR DELAY '00:00:05'
SELECT *
 FROM authors
 WHERE au_lname LIKE 'Test%'
COMMIT

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

  在第三个窗口中运行下列语句,确保 authors 表格中没有任何包含以下 id 的数据:

DELETE FROM authors WHERE au_id = '111-11-1111'
DELETE FROM authors WHERE au_id = '111-11-1112'

  在 5 秒钟内同时执行窗口 1 和 窗口 2。因为每个窗口都要等待至少 5 秒钟的时间才能发出 SELECT 语句,所有每个连接都将完成 INSERT 操作,这样就保证了两个窗口中的 INSERT 操作在各自的 SELECT 语句发布前就已经完成了。每个窗口中的 SELECT 语句都尝试读取 authors 表格中的所有数据,查找 au_lname 字段值中类似“Test%”格式的数据。因此,两个窗口中的 SELECT 语句都将尝试读取各自连接中的插入数据 — 也读取对方连接中的插入数据。

  READ COMMITTED 隔离级别通过发布共享锁确保 SELECT 语句永远不读取未提交的数据。对于同一个资源,共享锁与排它锁互不兼容,请求者在发布共享锁之前必须等待排它锁释放。每个连接对于插入的数据都设置了排它锁,因此尝试读取对方插入数据的 SELECT 语句将试图解除插入数据的共享锁,但它会被阻塞。两个连接将互相阻塞,从而形成一个死锁。SQL Server 的锁定管理器检测到死锁时,将中止其中的一个批处理,回滚它的事务,释放它的阻塞锁,以便其他事务能够完成。作为死锁牺牲品的事务将回滚,其他事务则将成功完成。如何使用 TRY/CATCH 语句避免死锁

  现在,让我们来使用 TRY/CATCH 语句修改代码正文。(对于本示例,需要以 SQL Server 2005 版本运行代码。)使用 TRY/CATCH 时,操作代码和错误处理代码是分开的。您应该将执行一个操作的代码放在 TRY 语句块中,将错误处理代码放在 CATCH 语句块中。如果 TRY 语句块中的代码执行失败,代码执行将跳到 CATCH 语句块。(除了那些防碍整个批处理运行的错误(如,丢失对象),该方法几乎适用于所有的错误。)

  以下示例使用 TRY/CATCH 语句对前面使用的代码进行了改写。代码标题相同,但是代码正文不同:

BEGIN TRANSACTION
BEGIN TRY
 INSERT Authors VALUES
 (@au_id, @au_lname, '', '', '', '', '', '11111', 0)
 WAITFOR DELAY '00:00:05'
 SELECT COUNT(*) FROM Authors
 COMMIT
END TRY
BEGIN CATCH
 SELECT ERROR_NUMBER() AS ErrorNumber
 ROLLBACK
END CATCH;
SELECT @@TRANCOUNT AS '@@Trancount'

  现在,在连接到 SQL Server 2005 的并列窗口中运行这些代码,在此之前您需要确认已经删除了 authors 表格中任何可能阻止插入操作的数据;或者,您可以使用前置 DELETE 语句。

  两个窗口返回的 @@TRANCOUNT 级别都为 0,这表明仍然发生了死锁,但 TRY/CATCH 语句捕获了这次发生的死锁。死锁牺牲品的批处理没有再次中止,可在它的输出结果中看到错误:

ErrorNumber
-----------
1205
@@Trancount
-----------
0

  您应该已经发现 TRY/CATCH 语句具有的威力了。因为死锁错误能够为 CATCH 语句块所捕获,所以批处理将不再中止,T-SQL 代码也能继续执行。对于死锁牺牲品而言,死锁错误 1205 将代码放入 CATCH 语句块 — 在这里您可以使用新的错误处理函数浏览死锁错误。前置代码仅使用 ERROR_NUMBER() 函数取代 @@ERROR 变量,您也可以使用 ERROR_MESSAGE()、ERROR_PROCEDURE()、ERROR_SEVERITY() 和 ERROR_STATE()。这些函数的功能一目了然,它们提供的功能比我们以往使用的更多。

  请注意,这个前置 CATCH 语句块包含一个 ROLLBACK。这样做的原因是,即使捕获了死锁错误,事务也不会回滚。事务仍然要失败,但是,现在您有责任在 TRY/CATCH 语句中回滚事务。那么,区别在哪里?尽管您不能使事务继续进行,但是您能够 重试事务!在 TRY/CATCH 语句中进行重试

  在 SQL Server 2000 的 T-SQL 中,错误 1205 令人沮丧之处是它提供的建议:“Rerun the transaction.”问题是,至少在 SQL Server 2000 的 T-SQL 中,您不能做到这一点。但是,由于 SQL Server 2005 的 TRY/CATCH 为我们提供了捕获死锁错误的方法,现在,重试事务是可能实现的。

  以下代码正文说明了一种执行重试操作的方法。这段代码仍然使用与前面相同的标题:

DECLARE @Tries tinyint
SET @Tries = 1
WHILE @Tries = 3
BEGIN
 BEGIN TRANSACTION
 BEGIN TRY
  INSERT Authors VALUES
   (@au_id, @au_lname, '', '', '', '', '',
'11111', 0)
  WAITFOR DELAY '00:00:05'
  SELECT * FROM authors WHERE au_lname LIKE 'Test%'
  COMMIT
  BREAK
 END TRY
 BEGIN CATCH
  SELECT ERROR_NUMBER() AS ErrorNumber
  ROLLBACK
  SET @Tries = @Tries + 1
  CONTINUE
 END CATCH;
END

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

  这段代码的功能是通过一个 WHILE 循环添加一个重试操作。我将重试次数设置为 3,重试次数是可以配置的。至少我们现在有了一种在 T-SQL 内重试一个死锁牺牲品代码的方法 — 这是我们过去一直无法做到的。

  但是,需要注意整个事务是在 WHILE 循环内进行的 — 而不是在循环外部。因此执行循环时,事务不仅在每个循环体内部开始,而且也在其中结束 — 不是 TRY 语句块执行完毕,返回一个 COMMIT,就是 CATCH 语句块执行,返回一个 ROLLBACK。如果 TRY 成功,TRY 语句块将以一个 BREAK 语句结束,退出 WHILE 循环。否则,CATCH 语句块将重试计数器加 1,以一个 CONTINUE 语句结束本次循环,重新执行下次 WHILE 循环。事实上,您有实现重试事务的代码 — 就像错误 1205 告诉我们做的那样。但现在,重试操作完全在 T-SQL 内部完成。

  SQL Server 2005 也提供帮助解决死锁问题的其他方法,例如 SNAPSHOT ISOLATION 级别和用于 READ COMMITTED 的新选项(称为 READ COMMITTED SNAPSHOT)。然而,这一事实 — 现在,通过 SQL Server 2005,您能够对事务进行编码并捕获死锁错误(并重试它们) — 已经意味着您拥有一个可任意支配、功能更加强大的工具。

展开更多 50%)
分享

猜你喜欢

使用 TRY/CATCH 语句解决 SQL Server 2005 死锁

编程语言 网络编程
使用 TRY/CATCH 语句解决 SQL Server 2005 死锁

在SQL Server 2005中解决死锁

编程语言 网络编程
在SQL Server 2005中解决死锁

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

SQL Server死锁的分析

SQLServer
SQL Server死锁的分析

SQL Server中处理死锁

SQLServer
SQL Server中处理死锁

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

使用SQL Server 2005 FOR XML嵌套查询

编程语言 网络编程
使用SQL Server 2005 FOR XML嵌套查询

SQL2005 高效分页sql语句

编程语言 网络编程
SQL2005 高效分页sql语句

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

SQL Server 2005安装及新增功能图解

SQL Server 2005安装及新增功能图解

Dreamweaver 入门:DW 8 的功能

Dreamweaver 入门:DW 8 的功能
下拉加载更多内容 ↓