SETIMPLICIT_TRANSACTIONS ON --式事模式

INSERTINTO orders

VALUES('p07','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

SELECTtimes=5,* FROM orders --'p07'被插入

ROLLBACK

go

SELECTtimes=6,* FROM orders --为执行了回,插入的'S060107'被撤消

go

DELETEFROM orders

WHEREdocno='p05' --除第个插入

SELECTtimes=7,* FROM orders --'p05'不存在

ROLLBACK

GO

SELECTtimes=8,* FROM orders --,使除作,所以'p05'又重新示存在。

GO

SETIMPLICIT_TRANSACTIONS OFF --式事模式束,又入自模式

--4关闭隐式事模式后,入自模式

DELETEFROM orders

WHEREdocno='p05' --除第个插入

SELECTtimes=9,* FROM orders

--模式行成功被自提交,'p05'除不存在。

DEMO2:,使事到指定的保存点,分批行,行的

use mydb

go

SELECTtimes=0, * FROM orders --检查当前表中的

go

begintransaction demo

INSERTINTO orders

VALUES('p05','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

go

SAVETRANSACTION save_demo

INSERTINTO orders

VALUES('p06','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

SELECTtimes=1, * FROM orders --'p05''p06'都被插入。

GO

ROLLBACKTRANSACTION save_demo --部分事

SELECTtimes=2, * FROM orders --'p06'被撤消不存在。

GO

ROLLBACKTRANSACTION demo --整个事

SELECTtimes=3, * FROM orders --'p05'被撤消不存在。

DEMO3更新数据表orders,生成三个级别的嵌套时务,并提交嵌套事@@TRANCOUNT

use mydb --选择数据须单独在一个批中

go

select@@TRANCOUNT --@@TRANCOUNT值为

begintransaction inside1

select@@TRANCOUNT --@@TRANCOUNT值为

INSERTINTO orders

VALUES('p05','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

go

begintransaction inside2

select@@TRANCOUNT --@@TRANCOUNT值为

INSERTINTO orders

VALUES('p06','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

go

begintransaction inside3

select@@TRANCOUNT --@@TRANCOUNT值为

INSERTINTO orders

VALUES('p07','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

go

COMMITTRANSACTION inside3

SELECT@@TRANCOUNT --@@TRANCOUNT

GO

COMMITTRANSACTION inside2

SELECT@@TRANCOUNT --@@TRANCOUNT

GO

COMMITTRANSACTION inside1

SELECT@@TRANCOUNT --@@TRANCOUNT

GO

DEMO4:orders表中先除一条记录,然后再插入一条记录,通过测试错误值确定提交是回

use mydb

go

DECLARE@del_error int,@ins_error int

--始一个事

BEGINTRAN del_or

--除一个学生

DELETEorders WHERE docno = 'p07'

--为删置一个接受错误

SELECT@del_error = @@ERROR

--@@ERRORint型返回最后行的SQL句的错误

--,表示行成功

--行插入

INSERTINTO orders

VALUES('p08','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

--插入置一个接受错误

SELECT@ins_error = @@ERROR

--测试错误变量中的

IF @del_error = 0

AND@ins_error = 0

BEGIN

--成功,提交事

COMMITTRAN del_or

END

ELSE

BEGIN

--错误发生,回

IF @del_error <> 0

PRINT'错误发生在'

IF @ins_error <> 0

PRINT'错误发生在插入'

ROLLBACKTRAN del_or

END

GO

 

分布式事务

在大型应用领域,经常需要事务跨服务器进行数据操作,这样的事务被称作分布式事务。所以分布式事务要能够在多个服务器上执行。

按照关于分布式事务处理的X/Open XA规范,分布式事务的处理过程规定为两个阶段,就是通常说的两阶段提交。为了简化应用程序对分布式事务的处理工作,系统提供了一个事务管理器来协调各个不同服务器对事务的处理操作,它就是MS DTC( Distributed Transaction

Coordinator ),既事务管理协调器。

分布式事务的两阶段提交

(1)准备阶段:当分布式事务管理器接受到提交请求后,它向所有参与该事务的SQL Server服务器发出准备命令。每个服务器接受到准备命令后,做好接受处理事务的准备工作,并将准备工作状态返回给事务管理器。

(2)提交阶段:当事务管理器接受到所有服务器成功准备好的信息后,它向这些服务器发出提交命令。之后所有服务器进行提交。如果所有服务器均能成功提交事务,管理器向应用程序报告分布式事务成功提交,如若有任一个服务器未能提交,事务管理器将向所有服务器发出回滚事务命令,并向应用程序报告事务提交失败。

 

分布式事务的处理过程

(1)T-SQL 程序或应用程序执行BEGIN DISTRIBUTED TRANSACTION语句启动一个分布式事务。此后,该服务器就成为分布式服务器的管理服务器。

(2)应用程序对链接服务器执行分布式查询或执行远程服务器上的存储过程。

(3)分布式事务管理服务器自动调用MS DTC,使链接服务器或远程服务器参加分布式事务处理。

(4)T-SQL 应用程序执行COMMIT 或ROLLBACK语句时,分布式事务管理服务器通过调用MS DTC 来管理两阶段提交,使链接或远程服务器提交或回滚事务。

 

分布式事务处理DEMO 分析

(1)、语法:

BEGINDISTRIBUTED TRANSANCTION [transanctin_name|@ transanctin_variable]

参数说明:

transanctin_name|@transanctin_variable 事务名称或事务名变量。

(2)、分布式事务DEMO:

有两个服务器LinkServer1 和LinkServer2 。在LinkServer2 服务器上建立存储过程student_insert_new,其功能是向LinkServer1 上的教学管理数据库的student 表插入一个新行。

--1、先接(程)服

SQL Server Management Studio → 服器 → 服象 → 接服器 → 右 →新建接服

配置程服器的服器名称及登/

--在第一台运行SQL Server的服器上运行下列代

EXECsp_addlinkedserver'LinkServer1', '','SQLOLEDB','本地服器名或ip地址'

--例如'zufe-mxh'

EXECsp_addlinkedserver'LinkServer2', '','SQLOLEDB','程服器名或ip地址'

--例如'172.19.2.156'

EXECsp_configure 'remote access', 1

--是,一般不需要

RECONFIGURE

--'LinkServer1'rpc出属性,使得允许调接服器上的存储过程。

EXECsp_serveroption'LinkServer1','rpc out','true'

GO

--停止并重新启第一台SQL Server

--确保使用SQL Server 身份验证

--在第二台SQL Server 上运行下列代

EXECsp_addlinkedserver'LinkServer2', '','SQLOLEDB','本地服器名或ip地址'

--例如'172.19.2.156'

EXECsp_addlinkedserver'LinkServer1', '','SQLOLEDB','程服器名或ip地址'

--例如'zufe-mxh'

EXECsp_configure 'remote access', 1

--是,一般不需要

RECONFIGURE

--'LinkServer2'rpc出属性,使得允许调接服器上的存储过程。

EXECsp_serveroption'LinkServer2','rpc out','true'

GO

--在第二个服器上添加新的程登IDLinkServer1),以便允许远程服LinkServer1接并

用。

--LinkServer2LinkServer1的用都是'sa',并且有相同的口令。

EXECsp_addremoteloginLinkServer1, sa, sa

GO

--停止并重新启第二台SQL Server

--2、在linkserver2 建存储过

if exists

(select *

fromsys.sysobjects

whereid=object_id(N'pr_up_order')

andxtype='P')

dropproc pr_up_order

go

createproc pr_up_order --带输入参数的存储过程,定义变量,在行存储过入数值则

插入一条记录

@docnochar(20),

@docdatedatetime,

@custnvarchar(200),

@carrencychar(3),

@ratenumeric(5),

@userpasswordvarbinary(256)

as

insertinto LinkServer1.mydb.dbo.orders

VALUES(

@docno,

@docdate,

@cust,

@carrency,

@rate,

@userpassword)

Go

--3、启用分布式事

--在第一台服器上启DTC始分布式事

--使用sa 在就可以在第一台SQL Server 行第二台SQL Server 上的存储过程。

USE mydb

GO

BEGINDISTRIBUTED TRANSACTION insert_tran

--始分布式事

--LinkServer1器上orders的插入

INSERTINTO orders

VALUES('p10','2011-09-10 00:00:00.000','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

Go

--LinkServer1器自动调MS DTC使得LinkServer2行存储过student_insert_new

student的插入。

EXECUTELinkServer2.mydb.dbo. pr_up_order

COMMITTRANSACTION --提交事

 

并发控制

在大型分布式数据库应用程序中,对数据库的并发访问操作是一个普遍存在的问题。SQL  Server 使用资源锁定的方法管理用户的并发操作。

SQL Server 提供了两种并发控制方法:

乐观并发控制:该方法假想用户之间不太可能发生资源冲突(事实上不是不可能),所以允许用户在不锁定任何资源的情况下执行事务。只有当用户试图修改数据时才

检查资源是否冲突。该方法需要使用游标。

悲观并发控制:该方法根据需要在事务的持续时间内锁定资源,从而确保事务的完整性和数据库的一致性。这是SQL Server 默认的并发控制方法。下面予以介绍。

 

SQL Server 锁的粒度及模式

(1)、SQL Server 锁的粒度

○1RID:行标识符,锁定表中单行数据。

○2 键值:具有索引的行数据。

○3 页面:一个数据页面或索引页面。

○4 区域:一组连续的8 个数据页面或索引页面。

○5 表:整个表,包括其所有的数据和索引。

○6 数据库:一个完整的数据库。

可以根据事务所执行的任务来灵活选择所锁定的资源粒度。

(2)、资源锁定模式—基本锁

○1 共享锁(S 锁):用于只读数据操作,它允许多个并发事务对资源锁定进行读取,但禁止其他事务对锁定资源的修改操作。

○2 排它锁(X 锁) :它锁定的资源不能被其它并发事务再进行任何锁定,所以其它事务不能读取和修改。锁定的资源用于自己的数据修改。

一般更新模式是由一个事务组成,该事务先读取记录,要获取资源的共享锁,然后修改记录,此操作要求锁转换为排它锁。如果两个事务都获得了资源上的共享锁,然后试图同时更新数据,这样肯定有一个事务要将共享锁转化为排它锁,因为一个事务的排它锁与其它事务的共享锁不兼容,发生锁等待。另一个事务也会出现这个问题,由于两个事务都要转化为排它锁,并且都等待另一个事务释放共享锁,因此发生死锁。

(3)、资源锁定模式—专用锁

○1 更新锁(U 锁)

在修改操作的初始化阶段用于锁定可能被修改的资源。一个数据修改事务在开始时直接申请更新锁,每次只有一个事务可以获得资源的更新锁。

使用更新锁可以避免上述死锁,因为一次只有一个事务可以获得更新锁,之后当需要继续修改数据时,将更新锁转换为排它锁,否则将更新锁转换为共享锁。

○2 意向锁:

意向锁表示如果获得一个对象的锁,说明该结点的下层对象正在被加锁。例如放置在表上的共享意向锁表示事务打算在表中的页或行上加共享锁。

意向锁可以提高性能,因为系统仅在表级上检查意向锁而无须检查下层。

意向共享锁(IS 锁):对一个对象加意向共享锁,表示将要对它的下层对象加共享锁。

意向排它锁(IX 锁) :对一个对象加意向排它锁,表示将要对它的下层对象加排它锁。

意向排它共享锁:对一个对象加意向排它共享锁,表示对它加共享锁,再在它的下层对象加排它锁。

○3 架构锁

架构修改锁(Sch-M 锁):执行表的数据定义语言(DDL)操作时使用。

架构稳定锁(Sch-S 锁):编译查询时使用。它不阻塞任何事务锁,包括排它锁。

○4 大容量更新锁(BU 锁)

当数据大容量复制到表的时候使用。

查看锁:

sp_lock

 

封装协议

在运用X锁和S 锁对数据对象加锁时,需要约定一些规则:封锁协议(LockingProtocol)何时申请X 锁或S 锁持锁时间、何时释放

1 级封锁协议+事务T 在读取数据R 前必须先加S 锁,读完后即可释放S 锁

2 级封锁协议可以防止丢失修改和读脏‖数据。

3 级封锁协议可防止丢失修改、读脏数据和不可重复读。容易造成比较多的死锁

封装协议小结:

 

事务隔离

为了避免产生并发访问问题,SQL Server 使用不同类型的锁对资源进行锁定,从而可重复读

限制在一个事务读取数据期间其他事务锁执行的操作类型,即对事务进行隔离。不同的并发访问问题可以通过设置不同的事务隔离级别加以解决。事务的隔离级别控制一个事务与其他事务的隔离程度,它决定该事务在读取数据时对资源所使用的锁类型。

(1)、事务隔离级别

未提交读:这是4 种隔离级别中限制最低的级别,它仅能保证SQL Server 不读取物理损坏的数据。在这种隔离级别下,不发出共享锁,也不接受排它锁,事务可以对数据执行未提交读或脏读;在事务结束前可以更改数据集内的数值,行也可以出现在数据集中或从数据集消失。

提交读:它要求在读取数据时控制共享锁以避免发生脏读,但数据可在事务结束前更改,这可能产生不能重复读或幻影读问题。

可重复读:锁定查询中使用的所有数据以防止其他用户更新,但是其他用户可以将新的幻影行插入到数据集中,新插入的幻影行将出现在当前事物的后续读取结果集中。可重复读

能够避免产生脏读和非重复读问题,但仍可能导致幻影读问题。

可串行读:这是事务隔离的最高级别,它使事务之间完全隔离,所以将导致并发级别较低。

在这种隔离级别下,SQL Server 在数据集上放置一个范围锁,以防止其他用户在事务完成之前更新数据集或向数据集内插入数据行,从而避免出现脏读、非重复读或幻影读等并发问题。

(2)、事务隔离级别对不同类型的行为

(3)、设置事务隔离级别

调用T-SQL中的SET TRANSACTION INOLATION LEVEL 语句可以调整事务的隔离

级别,以控制由该连接所发出的所有SELECT 语句的默认事务锁定行为。该语句的语法格式为:

SET TRANSACTIONISOLATION LEVEL{

READ UNCOMMITTED

|READ COMMITTED

|REPEATABLE READ

|SERIALIZABLE

}

例如:将事务隔离级别设置为REPEATABLE READ

SET TRANSACTIONISOLATION LEVEL REPEATABLE READ

注意:一旦设定,系统就会按这种隔离级别自动进行并发处理。

(4)、设置表级锁

表级锁是由程序员进行的,可以对SELECT、INSERT、UPDATE、DELETE 语句进行精确控制。表级锁定不是系统自动完成,而是需要程序员自己根据事务的要求进行锁定,然后,系统按程序员在程序中的锁定予以执行。

一般来说,读操作需要共享锁,写操作需要排它锁。如果需要更精确,还需要一些其它专用锁。具体设置有:

设置共享锁

设置排它锁

设置专用锁

○1 、设置共享锁

共享锁用于读操作。一个资源可获得多个共享锁。

使用HOLDLOCK设置共享锁。

DEMO:

USE mydb

GO

BEGINtransaction T1

SELECT * FROMOrders

WITH (HOLDLOCK)

SELECTCOUNT(docno) FROM Orders

COMMITtransaction t1

○2 、设置排它锁

对于INSERT、UPDATE 和DELETE 语句使用排它锁。在并发事务中,只有一个事务能够获得资源的排它锁。

使用TABLOCKX 置排它

DEMO:

USE mydb

GO

BEGINtransaction T1

INSERTINTO Orders

WITH(TABLOCKX)

VALUES('p09','2011-09-10','ded','EN','4000',convert(varbinary(256),

pwdencrypt('passw0rd')))

COMMIT

3 置其它

法同上

NOLOCK:不要出共享和排它适用SELECT 句。

READPAST:跳经锁定的行。适用SELECT 句。

TABLOCK:使用大容量更新

PAGLOCK:使用页级锁

ROWLOCK:使用行级锁,不使用页级和表级锁

UPDLOCK取表使用更新

XLOCK:适用于不同粒度的排它

5)、死锁处

SQL Server 定期搜索和理死锁问题。当检测到有死锁时SQL Server 被中断的事,并向用程序返回1205 错误信息,未被中断的事务则继续执行。在数据库应用程序捕捉到1205 错误,可以锁现象做后续处理。减少死次数,在设计应用程序

u 尽量使一个事在一个批内,并且要短;

u 不要在事务处理期和用交互;

u 灵活使用级别的事隔离;

u 在事务处理期要使访问数据量最小。