事务
构成单一逻辑工作单元的操作集合称作事务。
事务概念
事务是访问并可能更新各种数据项的一个程序执行单元。 事务通常由高级数据操作语言或编程语言通过JDBC或ODBC嵌入式数据库访问书写的用户程序的执行所引起。 事务形如 begin transaction 和 end transaction 语句(或函数调用)来界定。 事务由 begin transaction 与(end transaction 之间执行的全体操作组成。
数据库系统维护事务的以下性质:
- 原子性
事务的所有操作在数据库中要么全部正确反映出来,要么完全不反映。 - 隔离性
并发执行,不会导致出错。 - 持久性
一个事务成功完成后,它对数据库的改变必须是永久的,即使出现系统故障。 - 一致性
隔离执行事务时(在没有其他事务并发执行的情况下)保持数据库的一致性。
一个简单的事务模型
事务运用以下两个操作访问数据。
- read(X):从数据库把数据项X传送到指向read操作的事务的主存缓冲区的一个也称为X的变量中。
- write(X):从执行write的事务的主存缓冲区的变量X中把数据项X传回数据库中。
事务举例: 设T是从账户A过户¥50到账户B的事务。这个事务可以定义为:
T: read(A);
A: = A - 50;
write(A);
read(B);
B+=50;
write(B).
- 一致性
执行前执行后A+B之和不变。 - 原子性
对事务要执行写的数据项,在磁盘日志文件记录其旧值 如果事务完成执行,则得到预期结果。 如果事务执行被打断,依据日志文件恢复事务造成的部分改动,让其状态和执行前一致。 - 持久性
事务结束前,可将所做更新写入磁盘。 有关事务已执行的更新信息已写到磁盘上,并且此类信息必须充分,能让数据库在系统出现故障后重新启动时重新构造更新。 - 隔离性
保证并发执行下,可以得到和串行执行下一致的结果。
存储结构
- 易失性存储器
易失性存储器中的信息通常在系统崩溃后不会幸存。 易失性存储器的访问非常快,一方面是因为内存访问本身的速度,另一方面是因为可以直接访问易失性存储器中的任何数据项。 如主存储器、高速缓冲存储器。 - 非易失性存储器
非易失性存储器中的信息会在系统崩溃后幸存。 非易失性存储器比易失性存储器慢,特别是对于随机访问。 如磁盘和闪存,以及光介质、磁带。 - 稳定性存储器
稳定性存储器中的信息永远不会丢失。 如磁盘+备份。
事务原子性和持久性
事务并非总能成功地执行完成。这种事务称为中止了。 一旦中止事务造成的变更被撤销,说事务已回滚。
恢复机制负责管理事务中止。 典型的方法是维护一个日志。 每个事务对数据库的修改会先记录到日志中。 记录执行修改的事务标识符,修改的数据项标识符,数据项旧值,数据项新值。之后再修改。 维护日志提供了重做修改以及撤销修改以保证原子性,持久性可能。
成功完成执行的事务称为已提交。 撤销已提交事务所造成影响的唯一方法是执行一补偿事务。
事务状态:
- 活动的:初始状态,事务执行时处于这个状态。
- 部分提交的:最后一条语句执行后,未写入到磁盘。
- 失败的:发现正常的执行不能继续后。
- 中止的:事务回滚并且数据库已恢复到事务开始执行前的状态后。
- 提交的:成功完成后。
事务隔离性
- 提高吞吐量和资源利用率。
吞吐量——即给定时间内执行的事务数增加。相应地,处理器与磁盘利用率也提高。 即处理器与磁盘空闲或者没有做有用的工作的时间较少。 - 减少等待时间。
并发执行可以减少执行事务时不可预测的延迟。 也可减少平均响应时间:即一个事务从提交到完成所需的平均时间。
调度:指令在系统中执行的时间顺序。 保证所执行的任何调度都能使数据库处于一致状态,是数据库系统的任务。(并发控制) 调度结果等价于串行调度的调度称为可串行化调度。
可串行化
在调度中通常只显示 read 与 write 指令。
对指令A,B 1.冲突指令
- A读取区间K,B写区间K
则指令A,B的顺序不同,结果也不同.A,B是冲突指令 - B读取区间K,A写区间K
类似,A,B是冲突指令 - A写区间K,B写区间K
类似,A,B是冲突指令
2.非冲突指令 设K1区间和K2区间不重叠
- A读区间K,B读区间K
则指令A,B的交换执行顺序,结果相同.A,B是非冲突指令 - A针对区间K1操作,B针对区间K2操作
则指令A,B的交换执行顺序,结果相同.A,B是非冲突指令
3.冲突等价 并行调度是一个指令执行序列,但序列中指令由多个独立事务指令序列构成。 任意事务内各个指令相对顺序在并行调度和事务中一致。 不同事务之间指令交叉组合,构成多种调度方案。
如果调度S经过一系列非冲突指令交换为调度S’, 称为S与S’是冲突等价调度。
调度方案中,属于同一事务的各个指令集中在一起,顺序集成得到的方案称为串行调度方案。 串行调度方案数量有多个,串行调度方案间不能保证是冲突等价调度.但其执行结果一定是一致的。
4.冲突可串行化 若一个调度S和一个串行调度冲突等价,称S是冲突可串行化的。 我们知道串行调度一定可保证一致性,故,冲突可串行化调度一定是并行的可保证一致性的方案。 需要注意的是,有的调度,虽然不是冲突可串行化的,但其仍然可以得到可串行调度一样的结果。 所以,冲突可串行化调度是并行调度是一个可保证一致性调度的充分非必要条件。
5.检测冲突可串行化 依据调度方案构造优先图
调度方案包含的每个事务用一个节点表示。 对事物I,J。 若调度方案重,存在I,J的冲突指令,则,依据两条冲突指令前后顺序,在前指令所在事务的节点,发出一条到在后指令所在事务的边,这样构建出的优先图。 若是无环,则,调度属于一个冲突可串行化的调度(调度可以保证和串行调度一样的一致性) 若有环,则,调度不属于一个冲突可串行化的调度(不能断定调度是否可保证与串行调度一样的一致性)
串行化顺序可通过拓扑排序得到,拓扑排序用于计算与优先图的偏序相一致的线性顺序。
事务隔离性和原子性
事务T失败时,依赖于其的事务也应中止。
可恢复调度
下图显示了部分调度9,其中事务T7只执行了一条指令:read(A)。 称之为部分调度,因为在T6中没有包括 commit 或 abort 操作。 调度9是不可恢复调度。 一个可恢复调度应满足:对每对事务A、B,若A读取了由B所写的数据项,则要保证B先提交,A后提交。
无级联调度
单个事务故障导致一系列事务回滚的现象称为级联回滚。
无级联调度应满足:对每对事务A、B,若A读取了由B所写的数据,要保证A读取时,B已经提交。
事务隔离性级别
隔离性级别越高越能保证一致性,但响应、性能上也要相应牺牲。
SQL标准规定的隔离性级别如下:
- 可串行化
即为让事务满足串行化调度,或可等价转化到串行化调度的冲突可串行化调度。 - 可重复读
读取的数据必须是已经提交的。 一个事务两次读取同一数据项期间,其他事务不可更新该数据。 - 已提交读
读取的数据必须是已经提交的,但不要求可重复读。 一个事务两次读取同一数据项期间,其他事务可更新该数据。 - 未提交读
允许读取尚未提交的数据。(事务B修改了数据X,还未提交,事务A此时读取X,B中止时,A也得级联回滚)
上述所有隔离性级别都不允许脏写,即如果一个数据项已经被另外一个尚未提交或中止的事务写入,则不允许对该数据项执行写操作。
隔离性级别的实现
可以使用多种并发控制机制来保证,即使在有多个事务并发执行时,不管操作系统在事务之间如何分时共享资源(如CPU时间),都只产生可接受的调度。
并发控制机制的性能低下。 并发控制机制的目的是获得高度的并发性,同时保证所产生的调度是冲突可串行化或视图可串行化的、可恢复的,并且是无级联的。
锁
一个事务可以封锁其访问的数据项,而不用封锁整个数据库。
两阶段封锁协议: 阶段一只获得锁,阶段二只释放锁。
共享锁:用于事务读的数据项。 排他锁:用于事务写的数据项。
许多事务可以同时持有一个数据项上的共享锁,但是只有当其他事务在一个数据项上不持有任何锁(无论共享锁或排他锁)时,一个事务才允许持有该数据项上的排他锁。
这两种锁模式以及两阶段封锁协议在保证可串行化的前提下允许数据的并发读。
时间戳
事务开始时,为它分配一个时间戳。 对于每个数据项,系统维护两个时间戳。
数据项读时间戳,记录该数据项的事务的最大(即最近的)时间戳。 数据项写时间戳,记录写入数据项当前值的事务的时间戳。
访问冲突时,事务按时间戳顺序来访问。
多版本和快照隔离
每个事务开始时产生一个快照。 后续读写基于初始快照。 提交时,更新到实际数据库。 提交时,若发现有其他事物也修改了本事务所修改的数据,事务中止。
快照隔离可能的问题是, 事务A读取数据X,采用其快照版本。若此时,事务B对数据X有更新,对A来说,更新不可见。 某些情形下,这样会导致数据库不一致。
事务的SQL语句表示
在SQL中, insert 语句用来创建新数据, delete 语句用来删除数据。 这两条语句是 write 写操作,因为它们改变了数据库,但是它们与其他事务操作的交互与我们在简单模型中看到的不同。
例如:
select ID, name
from instructor
where slary > 90000;
假设砸执行上述查询的同一时间,另一个用户插入一条新的数据。
insert into instructor values ('11111','James','Marketing','100000');
查询结果会取决于该插入是先于还是后于执行的查询而有所不同。 在这两个事务的并发执行中,从直观上看他们是冲突的,然而这种冲突在简单模型中无法发现。这种情况称为幻象现象,因为冲突存在于一个“幻象”数据上。
再次考虑查询:
select ID, name
from instructor
where slary > 90000;
和以下SQL更新:
update instructor
set salary = salary * 0.9
where name = 'Wu';
在上述查询中,谓词是“salary>90000”,则一个将“Wu”的工资从90000更新为比90000更高的更新,或者将‘Einstein’的工资从高于90000更新到低于或小于90000的更新都会和谓词发生冲突。 基于这种思想的封锁称为谓词锁。
学习参考资料:
《数据库系统概念》第6版
|