日志系统原理
一:事务系统
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) <START T>: 这一记录表示事务T开始
2) <COMMIT T>: 事务T已经完成。
3) <ABORT T>: 事务T不能成功执行。
4) <T, X, v>: 事务T改变了数据库元素X的值,元素X原来的值为v。
3.undo日志规则
要想让undo日志能使我们从系统故障中恢复,事务必须遵循两条规则。
规则1)如果事务T改变了数据库元素X,那么形如<T, X, v>的日志记录必须在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 &n