一:事务系统
1. 事务的工作模型
事务必须满足原子性,所封装的操作或者全做或者全不做。
事务管理系统需要做两件事,1) 让日志系统产生日志,2) 保证多个事务并发执行,满足ACID 特性。
事务系统工作模型,见图1 。
如图,事务管理管理器控制查询处理器的执行、控制日志系统以及缓冲区。日志在缓冲区生成,日志管理器在一定的时候控制缓冲区的刷盘操作。当系统崩溃的时候,恢复管理器就被激活,检查日志并在必要时利用日志恢复数据。
2. 事务的原语操作
在事务系统的运行当中,有三个地址空间供元素存储:1) 磁盘空间、2) 缓冲区、3) 事务的局部地址空间。
一个简单的读、修改X 元素操作的流程如:事务到缓冲中读取元素X ,如果命中,则读取事务局部地址空间并返回,如果未命中,则先将相关页从磁盘读入缓冲区。事务在它的局部地址空间中修改元素X ,然后写入缓冲区,再从缓冲区写入磁盘。当然缓冲区的数据也可能不是立即拷贝入磁盘的,这取决于具体的缓冲区管理策略。
为了便于描述,我们描述了五个操作原语:
1) INPUT(X) :将包含数据库元素X 的磁盘块拷贝到内存缓冲区
2) READ(X,t) :将数据库元素X 拷贝到事务的局部变量t 。更准确地说,如果包含数据库元素X 的块不在内存缓冲区中,则首先执行INPUT(X) 。接着将X 的值赋给局部变量t 。
3) WRITE(X,t) :将局部变量t 的值拷贝到内存缓冲区中的数据库元素X 。更准确地说,如果包含数据库元素X 的块不在内存缓冲区中,则首先执行INPUT(X) 。将着将t 的值拷贝到缓冲区中的X 。
4) OUTPUT(X) :将包含X 的缓冲区拷贝到回磁盘。
3. 应用
假设银行系统数据库中有两个元素,元素A( 表示用户1 的余额,值为1000 ,单位:RMB) 与元素B( 表示用户2 的余额,值为500 ,单位:RMB) 。这时候用户1 需要向用户2 转帐50 元。相应的过程为:
A := A – 50;
B := B + 50;
执行之前,两个用户的总余额为1500(1000+500) ,两个操作执行成功之后总余额还是1500(950+550) 。处于一致性状态。
如果只有前一条执行成功,那总额只为1450(950+50) 。处于不一致状态。
为了避免这种不一致状态,我们需要将这两个操作封装成一个事务T 。
我们将这两个操作分解为原语操作。如下:
READ(A,t); t := t-50; WRITE(A,t);
READ(B,t); t := t+50; WRITE(B,t);
OUTPUT(A); OUTPUT(B) // 这两个OUTPUT 原语操作由缓冲区管理器发出。
表1 给出了这8 个原语操作的执行步骤,给出了每一步中A 和B 的内存值、磁盘拷贝的值以及事务T 地址空间中局部变量t 的值
表1 : 一个事务的步骤及其对内存和磁盘的影响
步骤 | 动作 | t | 内存A | 内存B | 磁盘A | 磁盘B |
1 | READ (A,t ) | 1000 | 1000 |
| 1000 | 500 |
2 | t := t-50 | 950 | 1000 |
| 1000 | 500 |
3 | WRITE(A,t) | 950 | 950 |
| 1000 | 500 |
4 | READ (B,t ) | 500 | 950 | 500 | 1000 | 500 |
5 | t := t+50 | 550 | 950 | 500 | 1000 | 500 |
6 | WRITE(B,t) | 550 | 950 | 550 | 1000 | 500 |
7 | OUTPUT (A ) | 550 | 950 | 550 | 950 | 500 |
8 | OUTPUT (B ) | 550 | 950 | 550 | 950 | 550 |
在表1 中不难发现,只要所有的这些步骤都执行成功,数据库的一致性就能得到保持。如果在执行OUTPUT(A) 前系统发生了故障,那么磁盘上存储的数据库不会受到任何影响,就好象事务T 从来没有发生过一样。但是如果故障在OUTPUT(A) 后而在OUTPUT(B) 前发生,那么数据就会处于 不一致状态( 从表中可以看出,磁盘中A 为950 ,B 为500) 。我们不能防止这种情况的发生,但可以安排当这些情况发生时对问题进行修复----- 或者A 为1000 、B 为为500 ,或者A 为950 ,B 为550 。
二:undo 日志
1. 概述
日志是日志记录的一个序列。在多事务的数据库系统中,每个事务有若干个操作步骤。每个日志记录记载有关某个事务已做的某些情况。几个事务的行为可以是“ 交错的” ,因此可能是一个事务的某个步骤被执行,并且其效果被记录到日志中,接着执行另外一个事务的某个步骤并记入日志,接着可能接着做第一事务的下一个步骤,也可能执行另外一个事务的某个步骤。依次类推。事务的交错执行使日志更复杂,因为仅在事务结束后记载事务的全过程是不够的。
如果系统崩溃,恢复管理器就被激活,检查日志以重建数据库的一致性状态。恢复时,有些事务的工作将会重做,它们写到数据库的新值会重写一次。而另外一些事务的工作将被撤消,数据库被恢复,将仿佛这些事务从来没执行过一样。
Undo 日志是日志类型的一种,这类日志仅仅进行第二类修复。对于要被撤消的事务,因为不能肯定它对数据库的修改是否已经写到磁盘中,所以对于该事务的所有更新都将被撤消,数据库恢复到事务发生以前的状态。
2. 日志记录
日志只允许以附加的方式写入数据。日志块最初在主存中创建,像数据块一样也由缓冲区管理,在确当的时刻,日志块会从缓冲区写入到磁盘。
关于undo 记录形式有四种:
1) : 这一记录表示事务T 开始
2) : 事务T 已经完成。
3) : 事务T 不能成功执行。
4) : 事务T 改变了数据库元素X 的值,元素X 原来的值为v 。
3.undo 日志规则
要想让undo 日志能使我们从系统故障中恢复,事务必须遵循两条规则。
规则1) 如果事务T 改变了数据库元素X ,那么事务的日志记录必须在X 的新值写到磁盘前写到磁盘
规则2) 如果事务提交,则其COMMIT 日志记录必须在事务改变的所有数据库元素已写到磁盘后再写到磁盘,但应尽快。
简单概括,undo 日志系统顺序如下:
1) 指明所改变数据库元素的日志记录
2) 改变的数据库元素自身
3) COMMIT 日志记录。
4. 应用
对于前面所举的例子(A 转帐50 元给B 帐号) ,如果使用了undo 日志系统,则相关的工作流程如表2 如下。
表2 :undo 日志系统的工作原理
步骤 | 动作 | t | 内存A | 内存B | 磁盘A | 磁盘B | undo 日志 |
1 |
|
|
|
|
|
| <START T> |
2 | READ (A,t ) | 1000 | 1000 |
| 1000 | 500 |
|
3 | t := t-50 | 950 | 1000 |
| 1000 | 500 |
|
4 | WRITE(A,t) | 950 | 950 |
| 1000 | 500 | <T, A, 1000> |
5 | READ (B,t ) | 500 | 950 | 500 | 1000 | 500 |
|
6 | t := t+50 | 550 | 950 | 500 | 1000 | 500 |
|
7 | WRITE(B,t) | 550 | 950 | 550 | 1000 | 500 | <T, B, 500> |
8 | FLUSH LOG |
|
|
|
|
|
|
9 | OUTPUT (A ) | 550 | 950 | 550 | 950 | 500 |
|
10 | OUTPUT (B ) | 550 | 950 | 550 | 950 | 550 |
|
11 |
|
|
|
|
|
| <COMMIT T> |
12 | FLUSH LOG |
|
|
|
|
|
|
在表2 ,我们可以看到FLUSH LOG 这个命令。该命令的用于强制将还没有刷盘日志记录写到磁盘上。对于步骤8 执行之前,三个undo 记录(,,) 是存储在缓冲区中的( 这样描述是为将问题简单化) ,执行步骤8 之后,三条undo 记录便写入了磁盘日志文件。在步骤12 再次执行FLUSH LOG 命令时,只将未刷盘的日志记录写入磁盘。
步骤9 、10 ,在前面已经描述过,是将数据库元素的修改从缓冲区写入到磁盘文件,因为WRITE 操作仅仅是将修改反应到缓冲区中( 这样描述也是为了将问题简单化) 。
关注步骤8 ,执行完步骤8 之后,将满足undo 的规则1( 如果事务T 改变了数据库元素X ,那么形如的日志记录必须在X 的新值写到磁盘前写到磁盘) 。在我们真正将数据库元素A 与B 的修改反应到磁盘前,我们已经将它们对应的与写入到磁盘日志文件。
关注步骤11 与步骤12 ,执行它们将满足undo 的规则2( 如果事务提交,则其COMMIT 日志记录必须在事务改变的所有数据库元素已写到磁盘后再写到磁盘,但应尽快) 。我们已经将数据库元素A 与B 的修改反应到磁盘上,接着可以写入来表示事务T 的成功执行,但是该日志记录还在缓冲区中,所以我们在步骤12 执行FLUSH LOG 进行日志刷盘。
5. 使用undo 日志进行数据库的恢复
现在假设系统故障发生了。有可能给定事务的某些数据库更新已经写到磁盘上,而同一事务的另外一些更新尚未到达磁盘。如果这样,事务的执行就不是原子的,数据库状态就可能不一致。这时候,我们就有必要使用日志将数据库恢复到一致的状态。
比如,在表2 中,如果故障发生在步骤9 之后、步骤10 之前。数据库的状态就是不一致的。
恢复管理的第一个任务就是将事务划分为已经提交事务和未提交事务。如果在日志中,根据undo 的规则2 ,事务