服务器 频道

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

  【IT168 服务器学院】对于今天的 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 的最新版本,用它来进行试验。

  可能发生的死锁情况有很多,[参阅http://msdn.microsoft.com/library/default.asp?url=/library/en-us/acdata/ac_8_con_7a_3xrf.asp以及死锁文章树中的后续文章。— 编者 ],但最有趣、最微妙的是那些关于阅读器和编写器互相阻塞的死锁。以下代码在 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

  在第三个窗口中运行下列语句,确保 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 的锁定管理器检测到死锁时,将中止其中的一个批处理,回滚它的事务,释放它的阻塞锁,以便其他事务能够完成。作为死锁牺牲品的事务将回滚,其他事务则将成功完成。

0
相关文章