惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
V
V2EX
G
Google Developers Blog
F
Full Disclosure
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
H
Hacker News: Front Page
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
NISL@THU
NISL@THU
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
A
About on SuperTechFans
The Cloudflare Blog
C
Cisco Blogs
D
DataBreaches.Net
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Vercel News
Vercel News
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
Help Net Security
Help Net Security
Recorded Future
Recorded Future
PCI Perspectives
PCI Perspectives
S
Schneier on Security
AI
AI
N
News | PayPal Newsroom
雷峰网
雷峰网
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
L
LINUX DO - 最新话题
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
Schneier on Security
Schneier on Security
S
Securelist
云风的 BLOG
云风的 BLOG
Stack Overflow Blog
Stack Overflow Blog
博客园_首页
AWS News Blog
AWS News Blog
TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 三生石上(FineUI控件)
C
CXSECURITY Database RSS Feed - CXSecurity.com
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
C
Cybersecurity and Infrastructure Security Agency CISA
Project Zero
Project Zero
C
Check Point Blog
S
Security Affairs

博客园 - 似水流年-johnhuo

架构师向左,项目经理向右?? 今天迈出第一步,开始了解iphone开发 学习objective-c的一些特殊的地方 信息发布系统 Jquery+MVC架构开发(8)DAL层的补充 信息发布系统 Jquery+MVC架构开发(7) Controller层 . 信息发布系统 Jquery+MVC架构开发(6)BLL层提供WCF 服务 . 信息发布系统 Jquery+MVC架构开发(5) DAL层 信息发布系统 Jquery+MVC架构开发(4) Model 层 . 信息发布系统 Jquery+MVC架构开发(3) 解决方案创建 信息发布系统 Jquery+MVC架构开发(2) 在EA下进行数据库设计 信息发布系统 Jquery+MVC架构开发(1) 需求分析和架构构思 c# 设计模式(三)行为型 c# 设计模式(二)结构型 c# 设计模式(一)创建型 .NET平台上的Model-View-Presenter模式实践 关于cmmi的一些理解及资料 iphone 开发学习 C语言指针总结 Android 开发网站,记录一下
使用 TRY/CATCH 语句解决 SQL Server 2005 死锁
似水流年-johnhuo · 2011-06-22 · via 博客园 - 似水流年-johnhuo

让我们从这样一个示例开始说起,它在 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 的锁定管理器检测到死锁时,将中止其中的一个批处理,回滚它的事务,释放它的阻塞锁,以便其他事务能够完成。作为死锁牺牲品的事务将回滚,其他事务则将成功完成。

  如何使用 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

  这段代码的功能是通过一个 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,您能够对事务进行编码并捕获死锁错误(并重试它们) — 已经意味着您拥有一个可任意支配、功能更加强大的工具。