------------------------------------------------------------------------
-- Author : HappyFlyStone 
-- Date   : 2009-10-03 15:30:00
-- Version: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86) 
--      Apr 14 2006 01:12:25 
--           Copyright (c) 1988-2005 Microsoft Corporation
--           Enterprise Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
--       转载请注明出处,更多请关注:http://blog.csdn.net/happyflystone
--       关键字:隔离等级 锁定 意外数据行为 Lock Hint
------------------------------------------------------------------------

在前面的两篇里我从纯理论上把事务相关的知识作了一个梳理,有人看了一定觉得无味了吧,好这一篇我们加入一点T-SQL语句把前面所说有东东关联起来,我们人为产生锁定来理解不同的意外数据行为在不同隔离等级下的表现,顺便再重温一下意外数据行及隔离等级,让大家对交易事务有一个直观的认识。

在进行实例前不得不先介绍一点锁的知识,注意这儿只是简单的说一下,不作深入讨论。我们根据用户访问资源的行为先归纳出几种锁,这几种锁在下面的实例里会出现,它们为:共享锁定、排它锁定、更新锁定及用意向这个限定词限定的三种锁(意向共享、意向排它、意向更新),当当然还有其它的模式,我们在下一篇再说。意向锁的存在是解决死锁的发生,保证进程在申请锁定前确定当前数据是否存在不兼容性的锁定。

先对上面提到的锁作一个简单的描述,更详细的下面再说。

共享锁定发生在查询记录时,直观就是我们select啦,但是并不是只有select才有共享锁定。一个查询记录的语句必须在没有与共享锁定互斥锁定存在或等待互斥锁定结束后,才能设置共享锁定并提取数据(互斥不互斥就是锁的兼容性,这在以后再说明)。
    排它锁定发生在对数据增加、删除、修改时,事务开始以后语句申请并设置排它锁定(前提是没有其它互斥锁定存在),以明确告知其它进程对当前数据不可以查询或修改,等待事务结束后其它进程才可以查询或修改。
    更新锁定是一个介于共享与排它之间的中继锁定,比如我们带where条件的update语句,在查询要更新的记录时是设置共享锁定,当要更新数据时这时锁定必须由共享锁定升级成更新锁定继而升级为排它锁定,当排它锁定设置成功才可以进行数据修改操作。显然也是要要求在锁升级的过程中没有互斥锁定的存在。简单的理解更新锁定是一个中继闸一样,把升级成排它锁定进程“序列化”,以解决死锁。最后重点说明一下,数据更新阶段是要对数据排它锁定不是更新锁定,不要被字面意思训导哦。
    最后说一下在上述锁定模式下的互斥,共享锁定只与排它锁定互斥,更新锁定只与共享锁定不互斥。
    在进行具体实例前我们一定要有一个工具来对我们实例过程进行监控,好,下面我写了一个过程,在需要时直接调用就行,过程如下:

Create Proc sp_us_lockinfo
---------------------------------------------------------------------
-- Author : HappyFlyStone
-- Date : 2009-10-03 15:30:00
-- BLOG : http://blog.csdn.net/happyflystone
-- 申明 :请保留作者信息,转载注明出处
--------------------------------------------------------------------
AS
BEGIN
SELECT
DB_NAME(t1.resource_database_id) AS [数据库名],
t1.resource_type AS [资源类型],
-- t1.request_type AS [请求类型],
t1.request_status AS [请求状态],
-- t1.resource_description AS [资源说明],
CASE t1.request_owner_type WHEN 'TRANSACTION' THEN '事务所有'
WHEN 'CURSOR' THEN '游标所有'
WHEN 'SESSION' THEN '用户会话所有'
WHEN 'SHARED_TRANSACTION_WORKSPACE' THEN '事务工作区的共享所有'
WHEN 'EXCLUSIVE_TRANSACTION_WORKSPACE' THEN '事务工作区的独占所有'
ELSE ''
END AS [拥有请求的实体类型],
CASE WHEN T1.resource_type = 'OBJECT'
THEN OBJECT_NAME(T1.resource_ASsociated_entity_id)
ELSE T1.resource_type+':'+ISNULL(LTRIM(T1.resource_ASsociated_entity_id),'')
END AS [锁定的对象],
t4.[name] AS [索引],
t1.request_mode AS [锁定类型],
t1.request_session_id AS [当前spid],
t2.blocking_session_id AS [锁定spid],
-- t3.snapshot_isolation_state AS [快照隔离状态],
t3.snapshot_isolation_state_desc AS [快照隔离状态描述],
t3.is_read_committed_snapshot_on AS [已提交读快照隔离]
FROM
sys.dm_tran_locks AS t1
left join
sys.dm_os_waiting_tasks AS t2
ON
t1.lock_owner_address = t2.resource_address
left join
sys.databases AS t3
ON t1.resource_database_id = t3.database_id
left join
(
SELECT rsc_text,rsc_indid,rsc_objid,b.[name]
FROM
sys.syslockinfo a
JOIN
sys.indexes b
ON a.rsc_indid = b.index_id and b.object_id = a.rsc_objid) t4
ON t1.resource_description = t4.rsc_text END
GO
/*
调用示例:exec sp_us_lockinfo
*/
exec sp_us_lockinfo
/*

最后介绍一个隔离等级设置命令:

SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED

                                 | READ COMMITTED

                                 | REPEATABLE READ

                                 | SNAPSHOT

                                 | SERIALIZABLE}[;]

该设置的选项将一直对当前连接(数据库连接)始终有效,直到显式更改该选项为止。详情参见

好,下面开始实例“快乐”之旅了。

五、隔离等级实例

测试数据准备:

CREATE DATABASE testcsdn;

GO

CREATE TABLE TA(TCID INT PRIMARY KEY,TCNAME VARCHAR(20))

INSERT TA SELECT 1,'AA'

INSERT TA SELECT 2,'AA'

INSERT TA SELECT 3,'AA'

INSERT TA SELECT 4,'BB'

INSERT TA SELECT 5,'CC'

INSERT TA SELECT 6,'DD'

INSERT TA SELECT 7,'DD'

GO

1、 未提交读(uncommitted Read)

概念回顾:未提交读是最低等级的隔离,允许其它进程读取本进程未提交的数据行,也就是读取数据时不设置共享锁定直接读取,忽略已经存在的互斥锁定。很显然未提交读这种隔离级别不会造成丢失更新,但是其它意外行为还是可以发生的。它和select 加锁定提示NOLOCK效果相当。

测试实例:

查询一:

SELECT * FROM TA WHERE TCID = 1

BEGIN TRAN

UPDATE TA

SET TCNAME = 'TA'

WHERE TCID = 1

--COMMIT TRAN --Don't commit

SELECT * FROM TA WHERE TCID = 1

SELECT @@SPID

/*

tcid        Tcname

----------- --------------------

1           AA

(1 行受影响)

(1 行受影响)

tcid        Tcname

----------- --------------------

1           TA

(1 行受影响)

SPID

------

54

(1 行受影响)

*/

查询二:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

SELECT * FROM TA WHERE TCID = 1

/*

tcid        Tcname

----------- --------------------

1           TA

(1 行受影响)

*/

--显然未提交读模式我们读到SPID=54未提交的数据。

查询三:

SELECT * FROM TA WHERE TCID = 1

--查询一直进行中…… 无结果

--因为缺省下已提交读级别,所以修改数据设置了排它锁定必须等到SPID=54的事务结束

查询四:

exec sp_us_lockinfo

这个时候如果我们回头到查询一里执行commit tran ,你会发现查询三会得到结果,并且是查询一修改后的结果,如果你改用rollback ,那么结果就是原来的值不变,这个你们自己再测试。

2、已提交读(Read Committed)

概念回顾:已提交读是SQL SERVER的缺省隔离级别,悲观模型下是用锁定,乐观模型下使用行版本控制器。这个设置可以通过SET READ_CIMMITTED_SNAPSHOT来修改。在悲观模型下对于读取来说设置共享锁定仅阻止排它锁定,并在数据读取结束自动释放,其它进程方可进行修改操作。也就是说读不会阻止其它进程设置共享及更新锁定,仅阻止排它锁定。在悲观模型下对于修改数据来说设置排锁定阻止所有锁定请示,必须等到排它锁定释放。这个级别的隔离解决了脏读的意外行为。

A、       READ_COMMITTED_SNAPSHOT为OFF的情况(缺省)

I、读数据测试

查询一:

BEGIN TRAN

--用锁定提示模拟共享锁定,并强制共享锁定持续到事务结束

SELECT * FROM TA with(holdlock) WHERE TCID = 1

--COMMIT TRAN --Don't commit

SELECT @@SPID

/*

tcid        Tcname

----------- --------------------

1           CA

(1 行受影响)

------

54

(1 行受影响)

*/

查询二:悲观模型下已提交读级别

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

UPDATE TA

SET TCNAME = 'TA'

WHERE TCID = 1

--查询一直没有结果,显然我们验证了共享锁定阻止了排它锁定。

查询三:

exec sp_us_lockinfo

--结果大家自己运行看结果。

II、修改数据测试

查询一:

SELECT * FROM TA WHERE TCID = 1

BEGIN TRAN

UPDATE TA

SET TCNAME = 'READ COMMITTED LOCK'

WHERE TCID = 1

--COMMIT TRAN --Don't commit

SELECT @@SPID

/*

tcid        Tcname

----------- --------------------

1           TA

(1 行受影响)

------

54

(1 行受影响)

*/

查询二:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

SELECT * FROM TA WHERE TCID = 1

/*

--查询一直进行中……被锁定无结果

--修改数据设置了排它锁定必须等到SPID=54的事务结束

*/

查询三:

exec sp_us_lockinfo

B、      READ_COMMITTED_SNAPSHOT为ON的情况

先修改当前当前库的READ_COMMITTED_SNAPSHOT为ON

ALTER DATABASE TESTCSDN

SET READ_COMMITTED_SNAPSHOT ON

GO

查询一:

SELECT * FROM TA WHERE TCID = 1

BEGIN TRAN

UPDATE TA

SET TCNAME = 'READ COMMITTED SNAP'

WHERE TCID = 1

--COMMIT TRAN --Don't commit

SELECT @@SPID

/*

TCID        TCNAME

----------- --------------------

1           AA

(1 行受影响)

(1 行受影响)

------

56

(1 行受影响)

*/

查询二:因为启用行版本控制器来锁定数据,保证其它进程读取到虽然被排它锁定但在事务开始前已经提交的保证一致性的数据。

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

SELECT * FROM TA WHERE TCID = 1

/*

TCID        TCNAME

----------- --------------------

1           AA

(1 行受影响)

*/

查询三:

exec sp_us_lockinfo

SQL2005中的事务与锁定(三)- 转载的更多相关文章

  1. SQL2005中的事务与锁定(七) - 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone - ...

  2. SQL2005中的事务与锁定(一) - 转载

    ----------------------------------------------------------------------- -- Author : HappyFlyStone -- ...

  3. SQL2005中的事务与锁定(八)- 转载

    ------------------------------------------------------------------------ -- Author : happyflystone - ...

  4. SQL2005中的事务与锁定(四)- 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone - ...

  5. SQL2005中的事务与锁定(二)- 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone  ...

  6. SQL2005中的事务与锁定(九)-(2)- 转载

    -------------------------------------------------------------------------- Author : HappyFlyStone -- ...

  7. SQL2005中的事务与锁定(六) - 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone - ...

  8. SQL2005中的事务与锁定(五)- 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone - ...

  9. SQL2005中的事务与锁定(九)- 转载

    ------------------------------------------------------------------------ -- Author : HappyFlyStone - ...

随机推荐

  1. js写当鼠标悬浮及移开出现背景变化

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. cat 显示指定行

    [一]从第3000行开始,显示1000行.即显示3000~3999行 cat filename | tail -n +3000 | head -n 1000 [二]显示1000行到3000行 cat ...

  3. sublime text 3 package control

    使用Ctrl+`快捷键或者通过View->Show Console菜单打开命令行,粘贴如下代码: import urllib.request,os; pf = 'Package Control. ...

  4. 几何不能具有Z值

    ArcEngine 复制要素Geometry时,产生 几何不能具有Z值 的异常 解决方法:http://forums.esri.com/Thread.asp?c=159&f=1707& ...

  5. linux重新设定分区大小

    一.目的 在使用CentOS6.3版本Linux系统的时候,发现根目录(/)的空间不是很充足,而其他目录空间有很大的空闲,所以本文主要是针对现在已有的空间进行调整.首先,先来查看一下系统的空间分配情况 ...

  6. IOS第12天(2,UINavigationController导航控制器)

    ****HMAppDelegate.m @implementation HMAppDelegate - (BOOL)application:(UIApplication *)application d ...

  7. node js 模块分类

    核心模块 require('fs'); 核心模块是被编译成二进制代码 文件模块 require('../fs.js'); 对于加载模块时既没指出./ ../ /.../时,加载模块的搜索路径.如果'/ ...

  8. 使用LVM对硬盘在线扩容

    初始状态: root@control:/dev/nova-volumes# vgdisplay --- Volume group --- VG Name nova-volumes System ID ...

  9. 在winform中添加普通右键菜单

    显示水平滚动条:点击GridControl的Run Designer在弹出的对话框中选择Views,将右侧属性窗口中OptionsView下的ColumnAutoWidth设置成false: 可以选择 ...

  10. CentOS7配置日志(VirtualBox)

    版本为CentOS-Minimal 1.VirtualBox下安装CentOS. 新建虚拟机 下载CentOS,放入盘片,启动虚拟机,按提示开始安装(建议内存1G,硬盘10G以上)   2. 设置网络 ...