1. Overview
本小节主要介绍DBMS中并发控制和故障恢复,以及事务(Transaction)的相关内容。DBMS的存储和执行都可能涉及到并发控制,在DBMS中正确的将I/O和执行操作并行化将极大的提升系统性能。
原本串行化执行的操作并行化执行将带来许多新的问题,例如更新丢失。同时,DBMS为了保证所有操作最终持久化到磁盘,需要具备故障恢复机制,当从故障中恢复后能够快速恢复到正确的状态。
DBMS通过事务(Transaction)达到并发控制和故障恢复的目的。具体而言,事务共需要满足ACID四个特性。
DBMS事务是由一系列按序组织的操作集合,事务中的操作要么全部提交成功,要么全部失败。若一个DBMS支持事务机制,一个事务将是它的最小操作单元。
下图是一个事务的例子,包含三个子操作,Andy向一个promoter账户转账,事务需要让这三个操作原子执行并保证顺序。
为了保证每个事务都原子性的执行了所有子操作。一种Native执行方法是顺序执行每个事务,每当开始执行事务前拷贝整份数据库到临时文件,事务只操作临时文件,当事务完整的在临时文件上执行完后再将临时文件写回原本数据库。若执行失败,删除临时文件。这种方案的拷贝、写回操作带来的I/O开销巨大,同时会引入大量磁盘碎片降低性能。
上述属于串行执行方式,性能低且没有充分利用硬件。若多个事务交错执行,需要正确性、性能以及公平性(有些操作想提前执行)。
正确性宽泛的说可以指一致性(具体指ACID四个性质),多个事务交错执行后也需要保证数据对象一致,允许临时的不一致,但是在执行完成后需要保证一致。为了保证交错执行最终会产生正确的执行结果,需要检测调度的正确性。
然而对于DBMS的底层而言,其并不能理解应用层事务的业务逻辑,底层能够看到的只是每次不同的读、写不同数据对象的操作,DBMS需要根据这些信息来判断此次并发调度能否满足正确性要求。
一个事务从BEGIN开始,到END/ABORT结束。事务既可以被DBMS给ABORT掉,也可以被用户自己终止。
事务需要满足的ACID性质分别是原子性、一致性、隔离性和持久性。本小节后面主要介绍事务的这四个性质。
2. Atomicity
事务原子性指一个事务中的包含的所有操作要么全部提交,要么全部不提交,最终这些操作的执行必须是原子性的,如果中途有操作执行失败,交易必须ABORT且已经做的操作需要回滚。
例如以下场景,用户命令DBMS把将正在执行的交易ABORT掉,或者出于意外DBMS突然停止工作,当故障恢复时交易依然能够保证原子性。
集中式DBMS通常有以下两种方式来保证事务的原子性,分别是写前日志(Logging)和临时页(Shadow Page)。写前日志指所有操作执行前先确保日志已经记录,这样当故障恢复后,DBMS都可以根据日志回滚或者继续未完成的事务(好奇DBMS是如何知道上一次是在哪一步终止了)。另一种方案则是上面提到的Native方案的近似版,不过不再复制整个DB,仅仅复制此次操作设计的页,操作完成后写回DB(或者更改原来页的指针)。
3. Consistency
一致性可分为数据库一致性和事务一致性,课程中讨论的一致性一般指的是数据库一致性,执行事务的前后数据库中的状态都满足某种约束。在分布式数据库中,一致性问题被讨论的非常多。
4. Isolation
多个事务交错执行需要避免相互影响,即最终状态和某种串行执行结果一致。事务隔离性指即使事务交错执行,多个事务之间互不影响,每个时刻仿佛只有一个事务在执行。
并发控制方法总体可分为两种策略,分别是乐观并发以及悲观并发。
例如下面的例子,事务T1和T2并发交错执行,但是最终需要满足A+B等于$2120(因为无论采取何种串行执行方式,A+B的和不变且都为$2120)。
例如下面两种交错执行方式分别产生正确和错误的执行结果。
判断某种交错执行是否正确的依据在于,调度是可串行化的,即等价于某种串行化调度。
我们可以通过检查调度中是否存在互相冲突的两个操作判断是否满足可串行化,两个操作中必有一个是写操作。
共有3种冲突的操作类型,分别是读-写、写-读和写-写冲突。
读-写冲突又称不可重复读,是指同一个事务中每次读取的状态值不同,原因在于被其他事务修改了。在串行化执行的情况下,这种问题不会发生。
写-读冲突又称脏读,是指事务读其他事务写后值之后,其他事务ABORT,读取的值失效,并未持久化。在串行化执行的情况下,一个事务COMMIT之后下一个事务才会开始,脏读问题不会发生。
写-写冲突又称覆盖未提交数据(更新撕裂),每个事务的写操作没有全部COMMIT。在串行化执行的情况下,一个事务COMMIT之后下一个事务才会开始,这个问题也不会发生。
通过调换调度中两个不冲突的操作顺序,判断调度是否能够可串行化。
下面是一个通过调整操作顺序检测冲突的例子。
最终发现与串行化执行一致,是可串行化调度。
相同方法判断以下调度不是冲突可串行化的。
另一种简便的方式即构造读写依赖图,若依赖图中没有环则说明调度是冲突可串行化的。
采用这种方法判断如下调度是冲突可串行化的,等价于T2, T1, T3串行执行。
5. Durability
持久化指的是事务的操作最终都持久化到硬盘中,未提交的事务的所有影响不写入磁盘。