事务管理
一、基本概念
1. 概述
事务(Transaction)是一系列的数据库操作,是数据库应用程序的基本逻辑单位,应用程序对事务的操作都应该以事务的方式进行
事务是一个操作序列 ,这些操作 “要么都做,要么都不做” ,是数据库环境中不可分割的逻辑工作单位。
事务定义语句如下:
- BEGIN TRANSACTION : 事务开始
- END TRANSACTION : 事务结束
- COMMIT : 事务提交 该操作表示事务成功的结束,他将通知事务管理器该事务的所有更新操作现在可以被提交或永久保留
- ROLLBACK: 事务回滚 该操作表示事务非成功的结束,将通知事务管理器出故障了,事务的所有更新操作必须回滚或撤销
2. 事务的特性
事务具有四个特性:原子性,一致性,隔离性,持久性 这四个特性通常被称为事务的ACID特性
- 原子性: 所有操作 要么都做 要么都不做 不能只执行部分
- 一致性:一个事务独立执行的结果,将保持数据的一致性,数据不会因为事务的在执行而遭到破坏。数据一致性是对现实世界的真实状态的描述
- 隔离性:一个事务的执行不能被其他事务干扰。并发事务在执行过程中可能会对同一数据进行操作,这些事务的操作不能相互干扰,是相互隔离的,如事务执行过程中出现数据不一致状态时,不能让其他事务读取到不一致的数据
- 持久性:一个事务一旦提交,对数据库的改变必须是永久的,即使系统出现故障也是如此
3. 事务的状态
如果不出现故障,那么所有的事务都能执行完成。一旦在执行过程中发生故障,不能执行完成的事务称为中止事务 ;将中止事务对数据库的更新撤销称为事务回滚;成功执行完成的事务称为已提交事务
中止的事务时可以回滚的,通过回滚恢复数据库,保持数据的一致性,这是DBMS的责任,已提交的事务不能回滚,必须由程序员或DBA手工执行一个补偿事务,才能撤销提交的事务对数据库的影响
为了更明确的描述事物的执行过程,一般将事务的执行状态分为5种:
- 活动状态:初始状态,事务执行时处于这个状态
- 部分已提交状态:当操作序列的最后一条语句自动执行后,事务处于部分提交状态
- 失败状态:由于硬件或逻辑等错误,使得事务不能正常执行,事务就进入了失败状态,处于失败状态的事务必须回滚,这样事务就进入中止状态
- 中止状态:事务回滚并且数据库恢复的事务开始执行前得状态
- 提交状态:事务成功完成后,称事务处于提交状态。只有事务处于提交状态后,才能说事务已经提交
事务的状态转换如下图所示:
事务状态转换操作命令如下表所示:
说明:事务进入中止状态后,系统的选择有两种
- 重启事务:当事务中止的原因是由软硬件错误引起而不是由事务内部逻辑错误所产生时,一般采用重启事务的方法。重启事务可以被看做一个新事务
- 杀死事务:这样做通常是因为事务中止的原因是由于事务内部的逻辑错误或者输入错误,或者所需数据在数据库中没有找到等原因
二、数据库的并发控制
并发操作是指在多用户共享的系统中, 许多用户可能同时对同一数据进行操作。并发操作带来的问题是数据的不一致性,主要有:丢失修改、不可重复读和读脏数据。其主要原因是并发操作破坏了事务的隔离性。DBMS的并发控制子系统负责协调并发事务的执行,保证数据库的完整性不受破坏,避免用户得到不正确的数据。
1. 事务调度
串行调度
串行调度是指事务依次串行执行,且只有当一个事务的所有操作都执行完后才能执行另一个事务的所有操作。
并发调度
利用分时的方法同时处理多个事务
可恢复调度
若事务T提交失败,则应当撤销T的影响以保证其原子性。在允许并发执行的系统中,还必须确保依赖于T的任何事务M也中止。
可恢复调度应满足:当事务M要读事务T写的事务时,事务T必须要先于事务M提交
2. 并发调度的可串行性
数据库系统必须控制事务的并发执行以保证数据的一致性状态
可串行化的调度
多个事务的并发执行是正确的,当且仅当其结果与某一次序串行的执行它们时的结果相同,称这种调度是可串行化的调度
可串行化是并发事务正确性的准则,按这个准则规定,一个给定的并发调度,当且仅当它是可串行化的才认为是正确调度
冲突可串行化
冲突:当$I_i$和$I_j$是不同事务上在相同数据项上操作的命令,且至少有一个是write命令时,则称$I_i$ 和 $I_j$是冲突的。
等价调度:设$I_i$和$I_j$是不同事务的命令且不冲突,则可以交换$I_i$和$I_j$的顺序得到一个新的调度$S^$。我们称S和$S^$是等价的
冲突等价:如果调度S经过一系列非冲突命令S交换成$S^$则称S与$S^$是冲突等价的
冲突可串行化:若调度S与一个串行调度$S^*$冲突等价,则称S是冲突可串行化的
冲突可串行话判定
3. 并发控制技术
为了保持事物的隔离性,系统必须对事务之间的相互作用加以控制,最典型的方式就是对数据对象以互斥的方式访问,即当一个事务访问某个数据库对象时,其他事务都不能更新该数据对象。最常用的控制手段就是加锁,该方法只允许事务访问当前持有锁的数据项,给数据加锁的方式有多种,此处只介绍共享锁和排他锁
排他锁(Exclusive Locks 简称X锁)也称为写锁,用于对数据进行写操作时进行锁定。如果事务T对数据A加上X锁后,就只允许事务T读取和修改数据A,其他事务对数据A不能再加任何锁,也不能读取和修改数据A,知道事务T释放A上的锁
共享锁(Share Locks 简称S锁) 也称为读锁,用于对数据进行读操作时进行锁定。如果事务T对数据A加上S锁后,事务T就只能读取数据A但不可以修改,其他事务可以对数据A再加S锁来读取,只要数据A上有S锁,任何事务都只能再对其加S锁读取而不能加X锁修改
4. 两段锁协议 (2PL协议)
通过对数据加锁,可以限制其他事务对数据的访问,但这会降低事务的并发性。封锁协议用于解决如何在保证事务的一致性的前提下尽可能的提高并发性这一问题。封锁协议是对数据加锁类型,加锁时间,和释放锁时间的一些规则的描述。封锁协议主要有三级封锁协议,以及两段封锁协议。
封锁协议
封锁协议有三个级别:一级封锁协议、二级封锁协议和三级封锁协议
- 一级封锁协议:事务T在修改数据A之前必须对其加X锁,直到事务结束才释放X锁。
- 二级封锁协议:是一级封锁协议加上事务T在读取数据A之前必须对其加上S锁,读完即可释放S锁。二级封锁协议使得一个事务不能读取被其他事务修改中的数据,解决了读脏数据的问题。但是,如果事务T在读取数据A之后,其他事务再对A做完修改,事务T再读取A,还会产生不可重复读的错误
- 三级封锁协议:是 一级封锁协议加上事务T在读取数据A之前必须对其加上S锁,直到事务结束后才释放S锁。三级封锁协议使得一个事务读取数据期间,其他事务读取该数据而不能修改,解决了不可重复读的问题。
两段锁协议
两段锁协议是指对任何数据进行读写之前必须对该数据加锁,释放一个封锁后,事务不再申请和获得任何其他封锁。
“两段”的含义在于,事务分为两个阶段,第一阶段是获得封锁,也称扩展阶段,第二阶段是释放封锁,也称收缩阶段。
为确保事务并行执行的正确性,许多系统采用两段锁协议,同时系统设有死锁检测机制,发现锁后按一定的算法解锁
活锁与死锁
活锁,是指事务$T_1$封锁了数据R,事务$T_2$请求封锁数据R,于是$T_2$等待,当$T_1$释放了R的封锁后,系统首先批准了$T_3$请求,于是$T_2$继续等待,当$T_3$释放了R的封锁后,系统又批准了$T_4$的请求,$T_2$仍然继续等待,依次类推,导致$T_2$可能永远等待的情况
死锁,是指两个以上的事务分别请求封锁对方已经封锁的数据,导致长期等待而无法继续运行的现象
5. 多粒度封锁协议
封锁的粒度
封锁对象的大小,称为封锁的粒度。封锁的对象可以是逻辑单元(属性,元组,关系,索引项,整个索引,数据库),也可以是物理单元(数据页、索引页)
封锁的粒度和系统的并发度和并发控制的开销密切相关,封锁的粒度越大,并发度越小,相应的系统开销也就越小;封锁的粒度越小,并发度越高,相应的系统开销也就越大
多粒度机制是指通过允许各种大小的数据项并定义数据粒度的层次结构,其中小粒度数据项嵌套大粒度数据项中,对此,可以构造一个粒度层次图,粒度层次图像一棵倒置的树,故也称多粒度树
粒度树的每个节点可以单独加锁,当一个事务对节点加锁时,该事务也将用同类型的锁隐含的封锁该节点的全部后代节点
意向锁
与共享锁和排他锁像关联的另一种锁是意向锁
若在一个节点加了共享型意向锁(IS),那么将在数据较低层次进行显式封锁,只能加共享锁
若在一个节点加了排他型意向锁(IX),那么将在树的较低层次进行显示封锁,可以加排他锁或共享锁
若在一个节点加了共享排他型意向锁(SIX),那么以该节点为根的子树将被显式的加共享锁。这些锁类型的相容函数矩阵如下表
锁相容函数矩阵表 IS IX S SIX X IS TRUE TRUE TRUE TRUE FALSE IX TRUE TRUE FALSE FALSE FALSE S TRUE FALSE TRUE FALSE FALSE SIX TRUE FALSE FALSE FALSE FALSE X FALSE FALSE FALSE FALSE FALSE
多粒度封锁协议
多粒度封锁协议允许多粒度树中的每个节点被独立的加锁,对某个节点加锁意味着该节点的所有后代节点也被加了同类型的锁
多粒度封锁协议采用这些锁(S , X , IS , IX , SIX)可以保证调度的可串行性。对每个事务T,要求按如下规则对节点Q加锁:
- 事务T必须遵循锁相容函数矩阵表
- 事务T必须首先封锁根节点,且可以加任意类型的锁。
- 仅当T当前对节点Q的父节点持有IX和IS锁时,T对节点Q可加S锁或IS锁
- 仅当T当前对节点Q的父节点持有IX和SIX锁时,T对节点Q可加X锁、SIX锁或IX锁
- 仅当T未曾对任何节点解锁时,T可对节点加锁,即T是两阶段的(2PL)
- 仅当T不持有Q的子节点的锁时,T可对节点Q加锁
注意:多粒度封锁协议要求加锁按自顶向下(从根到叶)的顺序进行,而锁的释放则按自底向上进行
并发控制的方式还有很多,如基于时间戳协议,基于有效性检查协议,快照隔离等