------------------------------------------------------------------------数据库
-- Author : happyflystone并发
-- Date : 2009-10-26app
-- Version: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86)性能
-- Apr 14 2006 01:12:25学习
-- Copyright (c) 1988-2005 Microsoft Corporationui
-- Enterprise Edition on Windows NT 5.2 (Build 3790: Service Pack 2)spa
-- 转载请注明出处,更多请关注:http://blog.csdn.net/happyflystone.net
-- 关键字:行版本控制器 RCSI SI 锁定提示 锁超时设定线程
------------------------------------------------------------------------版本控制
在前面一篇我说了锁的行锁与页锁之间的是与非,锁升级、动态锁、死锁,加上第六篇的一些理论加实例,基本上锁的相关知识这一个阶段结束了,这一篇咱们来学习一下2005的新特性行版本控制器,顺便说说锁定提示及锁超时设定,其实这个锁定提示在前面已经常常说起了,只是咱们没有细说。
10、行版本控制综述
行版本控制是SQLSERVER2005保证数据完整及一致的新机制。咱们前面提到并发模型有两种:悲观与乐观并发,而行版本控制是乐观并发下的一种保障数据完整及一致的新技术。行版本控制和前面说起的锁定机制不大同样,它保障了写的进程与读的进程间不会阻塞,而且在保证不读到未提交的数据下又提高了数据库的并发能力,然而咱们要注意的是乐观并发下写的进程仍是会获取排它锁定,上面说起的一些锁定模式、锁定时间及管理死锁的方式都适用于它。
SQLSERVER2005使用行版本控制的隔离有两个:RCSI 和 SI。RCSI是相对无阻塞的已提交读模式,所谓相对无阻塞是相对于传统的已提交读模式,在这种模式下写进程不会阻塞读进程,读进程不会设置共享锁定,而是使用行版本控制读取语句级的一致性的数据,简单的说就是任何读均可以获得语句开始那一时刻最近的已经提交数据。快照隔离(SI)可使任何读进程读取到交易级的一致性数据,简单的说就是任何读进程均可以读取到交易开始时已提交数据。
SQLSERVER2005如何作到写不阻塞读的呢?一旦启用RCSI或SI后数据库开始在tempdb中存储全部已经修改过的记录副本(记录版本,之后咱们直接称行版本),同时保证在只要有进程须要这些数据就会一直维持这些行版本,因此tempdb又被咱们称为版本存储区。很显然的是启用行版本控制后tempdb得须要更多的空间来管理行版本,因此若是你的数据库使用了行版本管理,必定要管理好tempdb。行版本如何存储在版本存储区我下面再说。
好,总体上来讲咱们应该有这样一个概念:已经提交的数据存储在当前数据库,而当数据修改前的数据被复制到tempdb中,那么它们之间如何联系呢?这儿咱们引入另外一个术语:XSN,注意哦,区别于LSN哦。XSN称为事务序列号,新行经过这个XSN和tempdb里的行版本之间保持联系,是否是有点指针味道呀,哈哈,同时咱们要注意了,新行XSN指向行版本区的某一旧行,同时这一旧行可能包含指向列旧的数据行XSN,SQLSERVE经过这个链表能够访问到正确的版本。
说了这么多,感情这个行版本管理后好处多多呀,至少加强了并发能力吧,但是在更改当前数据库使用行版本管理前仍是要三思而行:首先,增长了tempdb的负担,这种负担不只仅是空间上的。接着,维护旧版的数据行必然会下降更新操做的能力,无论有没有读进程存在,只要有更新存在数据库就得为此付出代价。第三,增长的并发能力使得每个读进程都得付出额外的开销来访问刚才咱们提到的XSN链表找到合适行版本。最后咱们说阻塞是不能彻底避免的,就是在这种乐观模式下写写仍是阻塞的。在后续咱们模拟在SI下的更新冲突。
有人要问了RCSI和SI之间有什么差异呢?其实RCSI和SI行为上基本相似的,都是能够在当前数据锁定的前提下读取到当前数据已经提交的早期版本,它们的主要差异有二:一,行版本记录在行版本区保存的时效。此话怎么理解呢,我在前面说到RCSI是语句级的而SI是事务级的,这就是直接致使数据行版有多久的关键。二,RCSI是已经提交读的无阻塞的变种,而SI是存在阻塞的。下面咱们会说道说道这两种行为。
11、行版本区
SQLSERVER2005只要开启快照,全部更新和删除就会生成已经提交的行版本,而这些行版本是保存在行版本区,即tempdb数据库的数据分页上,随时保障快照的查询须要,换句话说只要有查询须要,行版本区数据就存在。SQLSERVER2005有一个清理线程,常规好像是一分钟就进行一次回收,对于SI隔离下的查询行版本保存到事务结束(这就是为何在SI隔离下不会出现 不可重复读,由于在SI模式下整个事务都取的是事务开始那一刻的行版本数据),对于RCSI隔离下的查询行版本一直保存到当前查询语句结束(这就是为何RCSI隔离下会出现 不可重复读,由于在RCSI模式下整个事务都取的是表里面最近一次提交的数据,例如事务A有两次查询,但在两次查询间数据被另外一个事务B修改并提交了,那么事务A的两次查询的结果就会不同,形成不可重复读)。
这儿提到tempdb,得稍微提提这个tempdb,tempdb也是记录日志的,并非好多人认为的不记录,它的日志是为了临时对象上的事务回滚,记住只能回滚,不能恢复或重作,固然是是题外话,一带而过。
Tempdb中有三种类型的对象:用户对象、内部对象、版本库。这个版本库的数据来源有三:一,重建索引或有快照级别的数据库上执行了DML(咱们一会说这两个快照级别);二,触发器,这有别于2000哦,2005的伪表(deleted 、inserted)是由行版本产生的;三,活动结果集。
12、已经提交读快照隔离下读写行为
RCSI咱们必定要记住它是一个语句级的快照隔离级别,任何查询均可以查询到语句开始时最近的已经提交的数据。
如何开启这个级别隔离咱们前面已经写过了,对,用alter database dbname set read_comm.itted_snapshot on 就行,在运行这一命令要注意时不能用户在链接数据库,若是有人在使用数据库这个命令就会阻塞。这个命令有两个开关项:with nowait 和rollback来避免阻塞和终止任何数据库链接,你们能够查查联机从书。
我在前面写隔离级别的事例时提到这个隔离级别和用锁定的已提交读具备同样的行为,下面咱们用一个实例来看看:
先修改当前当前库的READ_COMMITTED_SNAPSHOT为ON
ALTER DATABASE TESTCSDN SET READ_COMMITTED_SNAPSHOT ON GO Exec sp_us_lockinfo Go
--test data and table create table ta(id int,col varchar(10)) insert ta select 1 ,'a' union all select 2,'b' union all select 3,'c' go
查询一:
begin tran update ta set col = 'd' where id = 1 waitfor delay '00:00:05' –故意加延时看看这个锁定是否影响查询二 exec sP_us_lockinfo –查看当前锁定状况,由图咱们知道在表上有排它锁定 commit
查询二:
begin tran waitfor delay '00:00:01'—确保表上已经有排它锁定 select * from ta where id = 1 -–行版本读到最近提交的数据 /* id col ----------- ---------- 1 a (1 行受影响) */ waitfor delay '00:00:05'—-保证查询一已经提交数据 select * from ta where id = 1 – 查询到最新行版本数据 /* id col ----------- ---------- 1 d (1 行受影响) */ commit
回顾一下以上过程,咱们发现这个已提交快照和已提交锁定方式同样的行为,可是它比锁定模式有更强的并发能力 ,由于读写进程间再也不阻塞。另外咱们注意到没有,不须要在每个会话中使用SET来设置选项就可使用RCSI,也就是咱们无需对应用程序做任何修改就能够从缺省的锁定方式的已提交读切换到快照方式的已提交读,从而下降阻塞带来的并发冲突。
13、快照隔离下的读写行为
SI是SQLSERVER2005引入的一个新的隔离,要启用必须在两个地方同时启用:一、启用Allow_SNAPSHOW_ISOLATION;二、在会话中使用SET TRANSACTION ISOLATION LEVEL命令为每个会话设置隔离。咱们在前面说过它是一个乐观模式的隔离,相似于已提交读快照隔离,可是又有些差异。
启用命令:
ALTER DATABASE DB_NAME SET ALLOW_SNAPSHOT_ISOLATION ON;
当咱们使用这个命令时,若是有活动链接时它不会像RCSI阻塞,可是若是有活动事务时仍是会被阻塞。这个命令运行后数据的状态不会当即成ON状态 ,而是经历一个IN_TRANSITION_TO_ON的状态,这时数据库处于等待数据库中全部事务结束并开始为更新和删除产生版本数据,一旦在alter命令开始时已经进行的事务一结束,数据库就会进入ON状态。同理我修改成OFF时数据的库状态也会经历一个中间状态IN_TRANSITION_TO_OFF,等待活动的事务结束。一旦全部的活动事务结束数据库就会变为OFF状态。好下面咱们来模拟启动过程,关闭的过程你们本身模拟吧。
咱们模拟打开的过程:
查询一:开始一个事务,记住不要提交可回滚
BEGIN TRAN UPDATE TA SET COL = 'B' WHERE ID = 1
查询二:开启快照
ALTER DATABASE DBlock SET ALLOW_SNAPSHOT_ISOLATION on;
查询三:
exec sp_us_lockinfo --你们能够看到当前数据处于中间态:IN_TRANSITION_TO_ON
--咱们模拟这时开启SI进行数据访问,看看会是什么结果 SET TRANSACTION ISOLATION LEVEL SNAPSHOT BEGIN TRAN SELECT * FROM TA WHERE ID = 1 /* id col ----------- ---------- 消息3956,级别16,状态1,第4 行 快照隔离事务未能在数据库'dblock' 中启动,由于用于启用此数据库的快照隔离的 ALTER DATABASE 命令还没有完成。数据库正在转换到挂起ON 状态。您必须等待, 直到ALTER DATABASE 命令成功完成。 */
接着,咱们在查询一里 增长一句:COMMIT;而后咱们再运行查询三:
那好,咱们经过上面的命令已经学会使用这个隔离级别。SI保证事务级的数据一致性,任何读操做均可以获得事务开始时最近已经提交的数据版本。下面咱们再模拟一下查询快照数据:
查询一:
SELECT * FROM TA /* id col ----------- ---------- 1 B (1 行受影响) */ BEGIN TRAN UPDATE TA SET COL = 'C' WHERE ID = 1 WAITFOR DELAY '00:00:05' exec sp_us_lockinfo /*
*/ COMMIT
查询二:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT BEGIN TRAN SELECT * FROM TA WHERE ID = 1 /* id col ----------- ---------- 1 B (1 行受影响) */ waitfor delay '00:00:05' SELECT * FROM TA WHERE ID = 1 /* id col ----------- ---------- 1 B (1 行受影响) */ commit tran SELECT * FROM TA WHERE ID = 1 /* id col ----------- ---------- 1 C (1 行受影响) */
还记得我在前面说过SI是有冲突阻塞(错误:3960)的哦, 下面咱们模拟一下,这也是在提醒你们使用SI模式时必定要潜在的阻塞,好看下面的实例:
查询一:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT BEGIN TRAN SELECT * FROM TA where id = 1 WAITFOR DELAY '00:00:05' UPDATE TA SET COL = 'c' WHERE ID = 1 /* 消息3960,级别16,状态2,第9 行 快照隔离事务因为更新冲突而停止。您没法在数据库'dblock'中使用快照隔离来直接 或间接访问表'dbo.TA',以便更新、删除或插入已由其余事务修改或删除的行。请重 试该事务或更改update/delete 语句的隔离级别。 */
查询二:
WAITFOR DELAY '00:00:02' BEGIN TRAN UPDATE TA SET COL = 'd' WHERE ID = 1 commit tran SELECT * FROM TA WHERE ID = 1 /* id col ----------- ---------- 1 d (1 行受影响) */
13、锁定提示(LOCK HINTS)
隔离级别是会话级别的,在会话内内对持有锁、阻塞 、锁的生命周期产生影响。然而,必要时咱们使用表级锁定提示来改变这种默认锁定行为,可是咱们必定要注意这种操做不当会影响并发性能。
咱们必定要记住使用锁定提示是表级提示,由于咱们通常是在From子句中使用wth指定。另外SQLSERVER2005推荐使用With(Locktype),不带with语法尽可能不要使用。
下面枚举一下锁定提示的关键字(申明太晚了,我直接复制了之前收藏的):
锁提示使用示例:
SELECT au_lname FROM authors WITH (NOLOCK) 锁定提示 描述 HOLDLOCK 将共享锁保留到事务完成,而不是在相应的表、行或数据页再也不须要 时就当即释放锁。它等同于SERIALIZABLE,只不过仅做用于表级。 NOLOCK 不发出共享锁,而且不要提供排它锁。当此选项生效时,可能会读取未提交的事务或一组在读取中间回滚的页面。有可能发生脏读。仅应用于 SELECT语句,显然至关于未提交读级别。 PAGLOCK 在一般使用单个表锁的地方采用页锁。 READCOMMITTED 用与运行在提交读隔离级别的事务相同的锁语义执行扫描。默认状况 下,SQL Server 2000 在此隔离级别上操做。 READPAST 跳过锁定行。此选项致使事务跳过由其它事务锁定的行(这些行日常 会显示在结果集内),而不是阻塞该事务,使其等待其它事务释放在这 些行上的锁。READPAST锁提示仅适用于运行在提交读隔离级别的事 务,而且只在行级锁以后读取。仅适用于SELECT语句。 READUNCOMMITTED 等同于NOLOCK。 REPEATABLEREAD 用与运行在可重复读隔离级别的事务相同的锁语义执行扫描。 ROWLOCK 使用行级锁,而不使用粒度更粗的页级锁和表级锁。 SERIALIZABLE 用与运行在可串行读隔离级别的事务相同的锁语义执行扫描。 等同于HOLDLOCK。 TABLOCK 使用表锁代替粒度更细的行级锁或页级锁。在语句结束前,SQL Server 一直持有该锁。可是,若是同时指定 HOLDLOCK,那么在事务结束以前,锁将被一直持有。 TABLOCKX 使用表的排它锁。该锁能够防止其它事务读取或更新表,并在语句或事务结束前一直持有。它和TABLOCKG与XLOCK连用的效果同样。 UPDLOCK 读取表时使用更新锁,而不使用共享锁,并将锁一直保留到语句或事务的结束。UPDLOCK的优势是容许您读取数据(不阻塞其它事务)并在之后更新数据,同时确保自从上次读取数据后数据没有被更改。消除转换类型的死锁重要技术。 XLOCK 提示SQLSERVER在语句使用的所有数据上使用排它锁,并一直保持到 事务结束时。可使用 PAGLOCK或TABLOCK指定该锁,这种状况下 排它锁适用于适当级别的粒度
14、锁定超时
SET LOCK_TIMEOUT n;
晕,这个看联机从书就好了,不能再写了。