【简记】Operating System——Atomic Transaction Overview

来源:互联网 发布:大数据r语言 编辑:程序博客网 时间:2024/04/30 09:00

This memo is based on the course of Dr.Li with Operating System as the reference book.

本章内容:

  • 原子事务

原子事务

6.9.1 系统模型

执行单个逻辑功能的一组指令或操作称为事务。可以认为事务是访问且可能更新各种驻留在磁盘文件中的数据项的程序单元。

从用户观点来看,事务只是一系列read 操作和write 操作, 井以commit 操作或abort 操作终止。

操作commit 表示事务已成功执行;操作abort 表示因各种逻辑错误,事务必须停止执行。已成功完成执行的终止事务称为提交(commited); 否则, 称为撤销(aborted ) 。

被中止的事务所访问的数据状态必须恢复到事务刚刚开始执行之前,即这个事务已经回退(rolled back ) 。确保这一属性是系统的责任。

为了决定系统如何确保原子性,首先需要识别用于存储事务所访问的各种数据的设备的属性。不同类型的存储介质可以通过它们的相对速度、容量和容错能力来区分。

  • 易失性存储:驻留在易失性存储上的信息通常在系统崩溃后不能保存。内存和高速缓存就是这种存储的例子。对易失性存储的访问非常快, 这是由于内存访问本身的速度以及易失性存储内的任何数据项都可以直接访问。
  • 非易失性存储:驻留在非易失性存储上的信息通常在系统崩溃后能保存。磁盘和磁带就是这种存储介质的例子。磁盘比内存更为可靠,磁带比磁盘更为可靠。然而,磁盘和磁带也会出错,从而导致信息遗失。当前,非易失性存储要比易失性存储慢几个数量级,因为磁盘和磁带设备为机电的且要求物理运动以访问数据。
  • 稳定存储:驻留在稳定存储上的信息绝不会损失(“绝不”应该打个折扣, 因为从理论上来说这样的保证是不成立的)。为了实现这种存储的近似,需要在多个具有失败模式独立的非易失性存储介质(通常是磁盘)上复制信息, 并按一定的控制方式来更新信息(参见12.8 节)。

本章只关心在易失性存储上出现信息损失的情况下,确保事务的原子性。


6.9.2 基于日志的恢复

确保原子性的一种方法是在稳定存储上记录有关事务对其访问的数据所做各种修改的描述信息。实现这种形式记录最为常用的方法是先记日志后操作。系统在稳定存储上维护一个被称为日志的数据结构。每个日志记录描述了一个事务写出的单个操作,并具有如下域:

  • 事务名称:执行写操作事务的唯一名称。
  • 数据项各称:所写数据项的唯一名称。
  • 旧值:写操作前的数据项的值。
  • 新值:写操作后的数据项的值。

在将相应日志记录写出到稳定存储之前,不能允许真正地更新数据项。因此,要求在执行操作write(X)之前,对应于X 的日志记录要先写到稳定存储上。

采用日志,系统可处理错误,以便不会在非易失性存储上造成数据损失。恢复算法采用两个步骤:

  • undo(Ti): 事务Tj 更新的所有数据的值恢复到原来值。
  • redo(Tj): 事务Tj 更新的所有数据的值设置成新值。

操作undo 和redo 必须幂等(即一个操作的多次执行与一次执行有同样结果) , 以确保正确的行为(无论恢复过程是否有错误发生)。

如果系统出现错误,可检测日志来决定事务是要撤销还是重做。

  • 当日志中有事务的开始记录,但没有commit记录时,需要做undo操作
  • 当日志包括开始和commit记录,仍然需要redo。这里的redo是保证性操作。

6.9.3 检查点

当系统出现了错误,必须参考日志以确定哪些事务需要重做而哪些事务需要撤销。从原理上来说,需要搜索整个日志以便做出这些决定。这种方法有两个主要缺点:

  • 搜索进程费时
  • 绝大多数所根据的算法需要重做的事务(如日志记录所说的那样)已经更新了数据。虽然重做数据修改并没有什么损坏(因为幂等),但是它会导致恢复需要较长时间。

为了降低这些类型的额外开销,在此引入了检测点( checkpoint ) 的概念。在执行时,系统维护写前日志。另外, 系统定期执行检查点并执行如下动作:

  1. 将当前驻留在易失性存储( 通常是内存)上的所有日志记录输出到稳定存储上。
  2. 将当前驻留在易失性存储上的所有修改数据输出到稳定存储上。
  3. 在稳定存储上输出一个日志记录< checkpoint> 。

如果< Ti commits>记录在日志记录中,出现在< checkpoint>记录之前。则恢复的时候不需要考虑这个事务。

所以,引入了检测点后可以简化恢复的过程。在出现差错后,找到满足条件的最后一个Ti,然后只需检查Ti之后发生的事务。用集合T表示这类事务。

  • 对于属于T 的所有事务,只要commit出现在日志中,就执行redo 。
  • 对于属于T 的所有事务,只要commit没有出现在日志中,就执行undo。

6.9.4 并发原子操作

因为每个事务是原子性的,所以事务的并发执行必须相当于这些事务按任意顺序串行执行。

这一属性称为串行化,在许多情况下,可以允许这些事务互相重叠,而又能保证其串行化。有多个不同并发控制算法可确保串行化。下面将对它们进行讨论:

1.串行化能力
这里写图片描述

如果调度S 可以通过一系列非冲突操作的交换而转换成串行调度S’, 说调度S 为冲突可串行化。

2.加锁协议
确保串行化能力的一种方法是为每个数据项关联一个锁,并要求每个事务遵循加锁协议(locking protocol)以控制锁的获取与释放。对数据项加锁有许多方式。在这里,只讨论两种方式:

  • 共享( shared ) : 如果事务T获得了数据项Q 的共享模式锁( 记为S)的,那么T可读取这一项,但不能修改它。
  • 排他(exclusive) :如果事务T获得了数据项Q的排他模式锁( 记为X),那么T可读和写。

为了访问数据项Q,事务Ti必须首先按适当模式锁住Q 。如果Q 当前未被加锁,那么就允许加锁, Ti就可访问它了。假定Ti 请求Q 的排他锁。在这种情况下,Ti必须等待直到Q 上的锁被释放为止。如果Ti请求Q 的共享锁,而且Q已按排他方式加锁, 那么Ti必须等待。否则,它可得到锁并访问Q。

两阶段加锁协议( two-phase locking protocol) 。这个协议要求每个事务按两个阶段来发出加锁和放锁请求:

  • 增长阶段: 事务可获取锁,但不能释放锁。
  • 收缩阶段:事务可释放锁,但不能获取新锁。

3.基于时间戳的协议

确定串行化顺序的另一方法是事先在事务之前选择一个顺序。这样做的最为常用的方法是使用时间戳排序方案。

对于系统内的每个事务Ti,都为之关联一个唯一固定的时间戳,并记为TS(Ti)。后进入系统的事务,其时间戳值也相对更大。

  • 采用系统时钟值作为时间戳,即事务的时间戳等于当事务进入系统时的时钟值。这种方法不适用于处于不同系统上的事务,也不适用于不共享时钟的多处理器系统。
  • 采用逻辑计数器作为时间戳。即事务的时间戳等于当事务进入系统时的计数器的值。在赋予新时间戳之后, 计数器的值会增加。

事务的时间戳决定了串行化的顺序。
因此,如果TS(Ti) < TS(Tj),那么系统必须确保所产生的调度相当于事务Ti在事务Tj之前的串行化调度。

这里写图片描述

阅读全文
0 0
原创粉丝点击