--维护数据库--
--事务(transaction)和锁--
--事务(transaction)--
--概述:
事务是指封装了一组T-SQL语句的单个逻辑单元。单元中的所有语句作为一个整体,在满足一定条件时全部执行(提交事务),或因不满足条件而全部不执行(回滚事务)。
在同一数据库中的数据在操作时可能是相互影响的,如果数据再互相影响的操作中发生,可以使用事务解决。
--事务的属性:事务的定义(声明)、执行(提交)、撤销(回滚)操作一旦发生,必须满足四个属性,即ACID属性。
1)原子性:事务作为一个独立的逻辑单元,被称为原子单元。它的执行是一次性的,要么事务中的所有事件都完成,要么不执行事务中的任何事件。
2)一致性:事务提交或回滚操作成功执行后,所有数据必须保持从一种状态到另一种状态的一致性。
3)隔离性:并发事务的修改必须与其他并发事务所做的修改隔离。一个事务执行时不可以看到另一个事务执行的中间阶段。
4)持久性:事务提交或回滚操作成功执行后,该结果对于系统的响应是永久的,无论之后发生任何事件。
--分类:根据数据库中发生事件的逻辑功能的不同,可以使用不同类型的事务完成对数据库中数据的维护。事务可以分显式事务、隐式事务和自动提交事务三种类型。
1)自动提交事务。是数据库的默认事务管理模式。每一个T-SQL语句都作为一个独立的事务发生。如果语句成功执行,则提交该语句;如果执行遇到错误,则回滚该语句。只要没有声明显式事务或隐式事务,那么数据库的实际连接就此默认模式操作。
2)隐式事务。指不需要描述事务的开始,只需要指明何时提交或回滚事务即可。若想使用隐式事务,可以使用set implicit_transaction on语句开启隐式事务模式,在隐式事务使用完毕后,再用set implicit_transaction off语句关闭隐式事务模式即可。当隐式事务模式开启后,首次执行以下任何语句时,都会自动启动一个事务:create、alter、drop、select、insert、update、delete、truncate、open、fetch、grant、revoke。在发生COMMIT或ROLLBACK语句之前,该事务将一直保持有效。在第一个事务被提交或回滚之后,下次当连接执行以上任何语句时,数据库引擎实例都将自动启动一个新事务。该模式将不断的生成隐性事务链,直到隐式事务模式关闭为止。
3)显式事务。指表明了事物的开始和结束的事务。事务以beigin transaction语句开始,以commit transaction或rollback transaction语句结束。
--事务的操作流程
显式事务的主要操作流程:
1)开始事务:begin transaction [事务名称]
--使用该语句标识一个事务的开始,全局变量@@transaction的值递增1,可以用来判断事务执行的结果状态。允许开始事务是不指定事务名称。transaction可简写为tran。
2)提交事务:commit transaction [事务名称]
--如果事务中的语句执行时没有遇到错误,可以使用该语句成功提交事务。事务中对数据库的操作有效执行,事务占有的资源被释放。可以不指定事务名称,以提交与之配对的begin transaction开始的事务。transaction可简写为tran。
3)回滚事务:rollback transaction [事务名称]
--如果事务中的语句执行时遇到错误,可以使用该语句清除遇到的事务,所有数据返回到事务开始时的状态。事务占用的资源被释放。可以不指定事务名称,以回滚与之配对的begin transaction开始的事务。transaction可简写为tran。
例1:(***下面是一个帮助理解的案例***) 在“账户表”中完成从张三账户中转出100元到道李四账户的转账操作,要求账户不能透支。如果产生透支的情况,打印“转账失败!”的错误提示,同时撤销转账事件;如果转账操作没有错误,打印出“转账成功!”提示,同时显示转账后信息。 "账户表"转账前 "账户表"转账后 ────┬──── ────┬──── 姓名 余额 姓名 余额 ────┼──── ────┼──── 张三 1100 张三 100 ────┼──── ────┼──── 李四 200 李四 1200 ────┴──── ────┴──── 分析:为避免账户余额透支,为“余额”字段设置check约束“余额>=0”,执行从张三账户转出金额给李四的操作实质是修改:账户表:,给张三金额减1000元,给李四金额加1000元。 可以把修改操作封装到事务中,再通过判断修改操作是否存在错误,决定事务提交或回滚。 set nocount on print '=====转账前金额=====' select*from账户表 go begin tran --开始事务 declare @serror int set @serror=0 update 账户表 set 余额=余额-1000 where 姓名='张三' set @serror=@serror+@@ERROR --全局变量用来统计最后一次T-SQL语句操作是否有误,如有返回错误代码,如无返回0。 update 账户表 set 余额=余额+1000 where 姓名='李四' set @serror=@serror+@@ERROR if @serror<>0 begin rollback tran --回滚事务 print '转账失败!' end else begin commit tran --提交事务 print '转账成功!' end go print '=====转账后金额=====' select*from 账户表 go set nocount off 附:("账户表"数据库 T-SQl语句) --建立"账户表"数据库-- create database 账户表 on(name='账户表_m', filename='D:\账户表_m.mdf', size=3mb,filegrowth=1mb,maxsize=unlimited) log on(name='账户表_l', filename='D:\账户表_l.ldf', size=1,filegrowth=10%,maxsize=unlimited) go --为数据库"账户表"建立数据表"账户表" use 账户表 go create table 账户表 (姓名 nvarchar(5)not null primary key, 余额 decimal(18, 0)not null ) --为数据表"账户表"添加数据 insert 账户表 values('张三',1100) insert 账户表 values('李四',200) --为数据表“账户表”设置检查约束 alter table 账户表 add constraint CK_账户表_余额 --添加约束 check (余额>=0) 例2:(从“商品管理数据库”的“客户信息表”中删除客户编号为20130001的客户记录。删除后给出提示信息,并显示删除后的“客户信息表”中的记录) use 商品管理数据库 go set nocount on print '=====原客户信息表=====' select*from 客户信息表 go begin tran declare @serror int set @serror=0 delete 客户信息表 where 客户编号='' set @serror=@serror+@@ERROR if @serror<>0 begin rollback tran print '删除客户失败!' end else begin commit tran print '删除客户成功!' end go print '=====原客户信息表=====' select*from 客户信息表 go set nocount off
理解例子
--锁--
SQL Server中的锁用来控制一个事务与另一个事务并发性。系统会自动为被访问的资源设置或释放锁。
如果某个事务已锁定一个资源,而另一个事务要访问该资源,那么SQL Server会根据第一个事务所使用的锁模式的兼容性来确定是否授予第二个锁。
资源的锁定模式可分为: 意向共享(IS)、共享(S)、更新(U)、意向排他(IX)、意向排他共享(SIX)和排他(X)六种模式。
常用锁模式矩阵:(YES不冲突,NO冲突)
请求的锁模式 | IS | S | U | IX | SIX | X |
IS | YES | YES | YES | YES | YES | NO |
S | YES | YES | YES | NO | NO | NO |
U | YES | YES | NO | NO | NO | NO |
IX | YES | NO | NO | YES | NO | NO |
SIX | YES | NO | NO | NO | NO | NO |
X | NO | NO | NO | NO | NO | NO |
Microsoft SQL Server 中完整的锁兼容性矩阵:(参考)
死锁现象:在多个任务中,如果一个任务锁定了其他任务试图锁定的资源,此时会造成任务的永久阻塞,从而出现死锁现象。
除非某个外部进程断开死锁,否则死锁中的两个事务都将无限期等待下去。
注:"--"可看成说明或者注释文本