第十七章:事务
1. 事务概念
- 事务是访问并可能更新各种数据项的一个程序执行单元。
这些操作要么都做,要么都不做,是一个不可分割的工作单位。 例如银行转帐。
- SQL中事务的定义
Commit work表示提交,事务正常结束,事务对于数据项的所有更新要保存在数据库中
Rollback work表示回滚,事务非正常结束,撤消事务已完成的操作,回滚到事务开始时状态
2.事务特性(ACID)
- 原子性(Atomicity)
事务中包含的所有操作要么全做,要么全不做
原子性由恢复系统实现
- 一致性(Consistency)
事务的隔离执行必须保证数据库的一致性
事务开始前,数据库处于一致性的状态;事务结束后,数据库必须仍处于一致性状态;事务的执行过程中可以暂时的不一致
数据库的一致性状态由用户来负责,由并发控制系统实现
如银行转帐,转帐前后两个帐户金额之和应保持不变
- 隔离性(Isolation)
系统必须保证事务不受其它并发执行事务的影响
对任何一对事务T1,T2,在T1看来,T2要么在T1开始之前已经结束,要么在T1完成之后再开始执行 隔离性通过并发控制系统实现
- 持久性(Durability)
一个事务一旦提交之后,它对数据库的影响必须是永久的
系统发生故障不能改变事务的持久性
持久性通过恢复系统实现
- 事务运用以下两个操作访问数据:
read(X):从数据库把数据项X传送到执行read操作的事务的局部缓冲区(缓冲区在主存中)
Write(X):从执行write操作的事务的局部缓冲区把数据项X传回数据库
特别注意:在实际数据库系统中,write操作不一定立即更新磁盘上的数据;write操作的结果可以临时存储在内存中,以后再写到磁盘上,当前假设write操作立即更新数据库,在“恢复系统”章节再进行讨论
3.并发执行
并行 Vs 串行
- 基本比较
并行事务会破坏数据库的一致性
串行事务效率低
- 并行的优点
一个事务由不同的步骤组成,所涉及的系统资源也不同。这些步骤可以并发执行,以提高系统的吞吐量(throughput)
系统中存在着周期不等的各种事务,串行会导致难于预测的延迟。如果各个事务所涉及的是数据库的不同部分,采用并行会减少平均响应时间(average response time)
4.并发操作带来的数据不一致性
- 丢失修改(lost update)
丢失修改是指事务1与事务2从数据库中读入同一数据并修改,事务2的提交结果破坏了事务1提交的结果,导致事务1的修改被丢失。
- 不可重复读(non-repeatable read)
事务1读取某一数据后:
- 事务2对其做了修改,当事务1再次读该数据时,得到与前一次不同的值。
- 事务2删除了其中部分记录,当事务1再次读取数据时,发现某些记录神密地消失了。
- 事务2插入了一些记录,当事务1再次按相同条件读取数据时,发现多了一些记录。
后两种不可重复读有时也称为幻影现象(phantom row)
- 读“脏”数据(dirty read)
事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1由于某种原因被撤消,这时事务1已修改过的数据恢复原值,事务2读到的数据就与数据库中的数据不一致,是不正确的数据,又称为“脏”数据。
5.事务调度(Schedule)
事务的执行顺序称为一个调度(schedule),表示事务的指令在系统中执行的时间顺序
一组事务的调度必须保证
- 包含所有事务的操作指令
- 一个事务中指令的顺序必须保持不变
串行调度
- 在串行调度中,属于同一事务的指令紧挨在一起
- 对于有n个事务的事务组,可以有n!个有效调度
并行调度
- 在并行调度中,来自不同事务的指令可以交叉执行
- 当并行调度结果等价于某个串行调度时,则称它是正确的
数据库系统的调度应该保证任何调度执行后数据库总处于一致状态
通过保证任何调度执行的效果与没有并发执行的调度执行效果一样,可以保证数据库的一致性。
并行调度应该在某种意义上等价于一个串行调度
5.可串行化(Serializability)
- 只考虑read和write两种操作
- 冲突可串行化(conflict serializability)
当一个调度S与一个串行调度冲突等价时,则称该调度S是冲突可串行化的(conflict serializable)
存在结果相同,但非冲突等价的调度
例子:
- 视图可串行化(view serializability)
6.可恢复性
可恢复调度(Recoverable Schedule)
事务的恢复:一个事务失败了,应该能够撤消该事务对数据库的影响。如果有其它事务读取了失败事务写入的数据,则该事务也应该撤消
级联调度:由于一个事务故障而导致一系列事务回滚
无级联调度必是可恢复调度
7.事务隔离性级别
对于某些应用,保证可串行的协议只允许极小的并发度,SQL标准规定一个事务可以以一种与其他事务不可串行化的方式执行。
按照隔离性级别从低到高的顺序:
- 未提交读
- 已提交读
- 可重复读
- 可串行化
SQL标准规定的隔离性级别如下:
- 未提交读。允许读取未提交数据。(当事务A更新某条数据时,不容许其他事务来更新该数据,但可以读取。)
在t2时刻,事务A对数据库进行了一次更新操作,而它提交之前,事务B就可以在t3时刻观察到这种变化,读取到更新后的价格(94.23);也就是说,事务A中的更新操作完全没有被隔离。如果事务A因为异常回滚,那么事务B中读取的数据就是脏数据。这一隔离级别违反了最基本的ACID特性,因此很多数据库都不支持(包括Oracle)。
- 已提交读
只允许读取已提交数据,但不要求可重复读。(当事务A更新某条数据时,不容许其他事务进行任何操作包括读取,但事务A读取时,其他事务可以进行读取、更新。)
当事务A在t2时刻更新了价格(94.23)之后,事务B在t3时刻仍然看不到该更新,此时读取价格仍然是90.00。这是一种使用较多的隔离级别,它既允许了事务B获取数据(支持并发性),同时又隐藏了其它事务(事务A)对该数据的更新,直到(事务A)提交的那一刻为止。几乎所有的数据库都支持“读已提交”的隔离级别,并且大部分将其作为默认的隔离级别。
- 可重复读。
只允许读取已提交数据,而且一个事务两次读取一个数据项期间,其他事务不得更新该数据,但是该事务不要求与其他事务可串行化。
例如:当一个事务在查找满足条件的数据时,它可能找到一个已提交事务插入的一些数据,但是可能找不到该事务插入的其他数据
尽管在事务B执行期间,事务A插入了一条数据(QRS),但是在事务B在t2时刻查询所得的结果依然和t0时刻一样(不包含QRS),即使到了t4时刻,事务A已经提交了也是如此(这一点不同于“读已提交”)。只有在事务B也提交了,它才会看见事务A对数据库所作的修改。值得注意的是,该隔离级别下,会在被查询或修改的数据上加上读写锁,因此任何想要修改该数据的其它事务会等待(或失败),直到“可重复读”的事务提交为止。
- 可串行化
在该隔离级别下,所有同时到达的事务将会“排队进入”,保证每次只允许一个事务操作数据。使用“串行化”的隔离级别,应用的并发性明显下降,而数据完整性则显著提高。
隔离级别实现
- 封锁
两阶段封锁:一种可以保证可串行化的技术 - 时间戳
为每个事务和每个数据项均分配时间戳,通过时间戳确定事务应该执行还是回滚 - 多版本和快照隔离
多版本:维护数据项的多个版本,允许事务读取数据项的旧版本 - 快照隔离:每个事务开始时拥有自己的数据库版本或者快照
END