日志文章

2008年04月12日 11:27:44

sqlserver事务处理全面解析二

1.1.   死锁
1.1.1.   定义

当某组资源的两个或多个线程之间有循环相关性时,将发生死锁。
死锁是一种可能发生在任何多线程系统中的状态,而不仅仅发生在关系数据库管理系统中。多线程系统中的一个线程可能获取一个或多个资源(如锁)。如果正获取的资源当前为另一线程所拥有,则第一个线程可能必须等待拥有线程释放目标资源。这时就说等待线程在那个特定资源上与拥有线程有相关性。
如果拥有线程需要获取另外一个资源,而该资源当前为等待线程所拥有,则这种情形将成为死锁:在事务提交或回滚之前两个线程都不能释放资源,而且它们因为正等待对方拥有的资源而不能提交或回滚事务。例如,运行事务 1 的线程 T1 具有 Supplier 表上的排它锁。运行事务 2 的线程 T2 具有 Part 表上的排它锁,并且之后需要 Supplier 表上的锁。事务 2 无法获得这一锁,因为事务 1 已拥有它。事务 2 被阻塞,等待事务 1。然后,事务 1 需要 Part 表的锁,但无法获得锁,因为事务 2 将它锁定了。事务在提交或回滚之前不能释放持有的锁。因为事务需要对方控制的锁才能继续操作,所以它们不能提交或回滚。


说明 死锁经常与正常阻塞混淆。当一个事务锁定了另一个事务需要的资源,第二个事务等待锁被释放。默认情况下,SQL Server 事务不会超时(除非设置了 LOCK_TIMEOUT)。第二个事务被阻塞,而不是被死锁。



在该插图中,对于 Part 表锁资源,线程 T1 在线程 T2 上具有相关性。同样,对于 Supplier 表锁资源,线程 T2 在线程 T1 上具有相关性。因为这些相关性形成了一个循环,所以在线程 T1 和线程 T2 之间存在死锁。
1.1.2.   检测和结束死锁
Microsoft® SQL Server™ 2000 中,单个用户会话可能有一个或多个代表它运行的线程。每个线程可能获取或等待获取各种资源,如:
    锁。
    与并行查询执行相关的资源(与交换端口相关联的处理协调器、发生器和使用者线程)。
    线程。
    内存。
上述这些资源除内存外都参与 SQL Server 死锁检测方案。对于内存,SQL Server 使用基于超时的机制,该机制由 sp_configure 中的 query wait 选项控制。
SQL Server 2000 中,死锁检测由一个称为锁监视器线程的单独的线程执行。在出现下列任一情况时,锁监视器线程对特定线程启动死锁搜索:
    线程已经为同一资源等待了一段指定的时间。锁监视器线程定期醒来并识别所有等待某个资源的线程。如果锁监视器再次醒来时这些线程仍在等待同一资源,则它将对等待线程启动锁搜索。
    线程等待资源并启动急切的死锁搜索。
SQL Server 通常只执行定期死锁检测,而不使用急切模式。因为系统中遇到的死锁数通常很少,定期死锁检测有助于减少系统中死锁检测的开销。
当锁监视器对特定线程启动死锁检测时,它识别线程正在等待的资源。然后,锁监视器查找特定资源的拥有者,并递归地继续执行对那些线程的死锁搜索,直到找到一个循环。用这种方式识别的循环形成一个死锁。
在识别死锁后,SQL Server 通过自动选择可以打破死锁的线程(死锁牺牲品)来结束死锁。SQL Server 回滚作为死锁牺牲品的事务,通知线程的应用程序(通过返回 1205 号错误信息),取消线程的当前请求,然后允许不间断线程的事务继续进行。
SQL Server 通常选择运行撤消时花费最少的事务的线程作为死锁牺牲品。另外,用户可以使用 SET 语句将会话的 DEADLOCK_PRIORITY 设置为 LOWDEADLOCK_PRIORITY 选项控制在死锁情况下如何衡量会话的重要性。如果会话的设置为 LOW ,则当会话陷入死锁情况时将成为首选牺牲品。
1.1.2.1.   识别死锁
识别死锁后,SQL Server 选择特定的线程作为死锁牺牲品,并返回一条列出死锁中涉及的资源的错误信息。该死锁信息采用下列形式:
Your transaction (process ID #52) was deadlocked on {lock | communication buffer | thread} resources with another process and has been chosen as the deadlock victim. Rerun your transaction.
死锁中涉及的线程和资源位于错误日志中。有关如何识别死锁中涉及的死锁线程和资源的更多信息。
1.1.2.2.   处理死锁
当一个应用程序提交的事务被选作死锁牺牲品时,该事务将自动终止并回滚,然后系统将 1205 号错误信息返回给应用程序。因为任何提交 SQL 查询的应用程序都可以被选定作为死锁牺牲品,应用程序应该有错误处理程序来捕获错误信息 1205。如果应用程序没有捕获到错误,它可能继续处理而未意识到事务已经回滚,这样应用程序就会出错。
通过实现捕获 1205 号错误信息的错误处理程序,使应用程序得以处理该死锁情况并采取补救措施(例如,可以自动重新提交陷入死锁中的查询)。自动重新提交查询可能意味着用户不必知道发生了死锁。
在自动重新提交查询前,客户端程序应暂停,以便为控制所需锁的事务提供完成并释放这些锁的机会。这样,在事务试图获得那些锁时,可以最大限度地降低事务被死锁的可能性。
说明 死锁不总是取消返回错误的批处理操作。对于客户端程序来说进行错误检查是非常重要的,因为死锁并不总是返回失败的返回代码。在大多数情况下,如果发生死锁且批处理未自动取消,则应用程序应取消当前查询。如果没有进行此操作,则 SQL Server 在连接上可能仍有未决结果等待客户端处理。如果没有处理未决结果,则在该应用程序下一次试图向 SQL Server 发送命令时将发生错误。
1.1.3.   将死锁减至最少
虽然不能完全避免死锁,但可以使死锁的数量减至最少。将死锁减至最少可以增加事务的吞吐量并减少系统开销,因为只有很少的事务:
    回滚,而回滚会取消事务执行的所有工作。
    由于死锁时回滚而由应用程序重新提交。
下列方法有助于最大限度地降低死锁:
    按同一顺序访问对象。
    避免事务中的用户交互。
    保持事务简短并在一个批处理中。
    使用低隔离级别。
    使用绑定连接。
1.1.3.1.   按同一顺序访问对象
如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。






1.1.3.2.   避免事务中的用户交互
避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。
1.1.3.3.   保持事务简短并在一个批处理中
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。
保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。
1.1.3.4.   使用低隔离级别
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。
1.1.3.5.   使用绑定连接
使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。
1.1.3.6.   附录:使用绑定连接
绑定连接允许两个或多个连接共享同一个事务和锁定。绑定连接可以对同一个数据进行操作,而不会有锁定冲突。绑定连接可以从同一个应用程序内的多个连接中创建,也可以从使用不同连接的多个应用程序中创建。绑定连接使得协调多个连接上的操作更加容易。
若要加入到绑定连接中,连接必须调用 sp_getbindtoken srv_getbindtoken(开放数据服务),以获得一个绑定令牌。绑定令牌是一个字符串,它唯一地标识每个绑定事务。然后绑定令牌将发送给加入到绑定连接的其它连接。其它连接通过调用 sp_bindsession,并使用从第一个连接中接收到的绑定令牌绑定到事务上。
必须从创建第一个连接的应用程序代码将绑定令牌传送到创建随后每个绑定连接的应用程序代码中。应用程序不能使用 Transact-SQL 语句或 API 函数获取由另一个进程启动的事务绑定令牌。可以用来传送绑定令牌的方法有:
    如果所有连接都是从同一个应用程序进程创建而来,那么绑定令牌可以存储在全局内存中,也可以作为参数传递到函数中。
    如果连接是从不同的应用程序进程创建而来,那么绑定令牌可以使用进程间通讯 (IPC),如远程过程调用 (RPC) 或动态数据交换 (DDE)来传输。
    Microsoft® SQL Server™ 中可以将绑定令牌存储在某个表中,该表应能够被要绑定到第一个连接的进程读取。
在一组绑定连接中,任意时刻只能有一个连接是活动的。如果一个连接正在服务器上执行一个语句,或者包含由服务器挂起的结果,那么共享同一事务空间的其它连接都不能访问该服务器,直到当前的连接完成处理或取消了当前的语句为止。如果服务器很忙,那么就会出现错误,表明事务空间正在使用,该连接应该稍后再试。
1.1.3.7.   绑定连接的类型
有两种绑定连接类型:本地和分布式。
    本地绑定连接
允许绑定连接共享单个服务器上的单个事务的事务空间。
    分布式绑定连接
允许绑定连接在用 Microsoft 分布式事务处理协调器 (MS DTC) 提交或回滚整个事务之前,共享跨越两个或多个服务器的同一事务。
分布式绑定连接的标识不是用字符串绑定令牌,而是用分布式事务标识号。如果本地事务中涉及到绑定连接,而且该连接使用 SET REMOTE_PROC_TRANSACTIONS ON 执行远程服务器上的 RPC,则 MS DTC 将该本地绑定事务自动升级到分布式绑定事务,并且 MS DTC 会话也会启动。
1.1.3.8.   何时使用绑定连接
绑定连接在开发必须执行 Transact-SQL 语句的扩展存储过程方面很有用,这些 Transact-SQL 语句代表调用这些绑定连接的进程。让调用进程在绑定令牌中作为扩展存储过程的一个参数进行传递,可使该过程加入到调用进程的事务空间中,从而将扩展存储过程与该调用进程结合在一起。
绑定连接可以用来开发三层应用程序,其中,商业逻辑用单独的程序表示,而这些程序在单个商业事务上协同工作。
下面的绑定连接示例阐明了两个连接如何能够访问同一个事务。某个顾客决定在本地的一个百货商店购买产品。售货员访问销售事务系统,在销售事务表中插入一行,其中包括信用卡授权号码。对同一服务器建立两个连接,连接 C1 和连接 C2C1 开始一个事务,将产品销售行添加到销售表中。必须将信用卡授权号码添加到新的销售事务行中。在信用卡授权进程中,扩展存储过程创建连接 C2,通过电话线拨出到信用卡公司并使用信用卡授权号码修改销售事务行。只有使用绑定连接,才能实现两个连接访问同一行而不出现锁定冲突。

Tags: sqlserver   事务处理  

类别: SQL语法 |  评论(0) |  浏览(1607) |  收藏
发表评论