Skip to content

Latest commit

 

History

History
104 lines (57 loc) · 5.72 KB

3.transaction-model.md

File metadata and controls

104 lines (57 loc) · 5.72 KB

5.2 故障类型和事务模型

上节中提到数据库可能会发生“故障”,实际上数据库的故障类型有很多种,本节主要讨论这些故障的产生和处理方法,并建立数据库的存储管理模型,以便后面讨论事务的并发访问。

故障类型

  • 数据输入错误

    数据内容的错误是无法避免的,如果用户在输入姓名或身份证号时输错了一位,那么错误是很难被直接发现的。处理数据输入错误的常见技术就是编写约束和触发器,可以及时找出不满足格式的错误数据。

  • 介质故障

    磁盘的部分区域损坏,一般只会影响少数几位数据,可以通过奇偶校验等方式检测到。如果整个磁盘损坏,要么为数据维护一个备份,要么采用 RAID 阵列保存数据。此外还可以将数据的副本保存在多个远程节点上,一个节点的故障不会导致数据丢失,这就是分布式的思想。

  • 系统故障

    系统故障主要包括掉电和软件错误,由于内存是“易失性的”,如果数据提交了但没有写入到磁盘,当系统掉电时这部分数据就会丢失。软件错误可能会直接覆盖内存中的数据,就相当于数据已经丢失。解决这类问题的办法就是维护日志,将所有对数据库的修改操作都记录下来,以便重启系统后进行恢复。

  • 机房故障

    机房故障有可能是整个机房发生了火灾、爆炸等意外,或是自然灾害导致一片地区受到破坏,这种情况下 RAID 和数据校验都无法发挥作用,只有备份机制(异地)才可以防止数据丢失。

事务模型

为了更好地理解事务机制,这里建立一个如下图所示的模型。

  • 事务管理器统筹管理事务的执行。

  • 查询处理器负责解析 SQL 命令。

  • 缓冲区管理器负责维护内存缓冲区和刷写数据。

  • 日志管理器负责维护日志。

  • 恢复管理器负责在系统重启后恢复数据。

img

事务原语

在数据库运行过程中,刚插入的数据往往不会直接写入磁盘,而是先缓存在内存中。对于一个运行中的数据库,可以将其地址空间简单分成三个部分:

  1. 持久化保存数据的磁盘空间。

  2. 缓冲区对应的内存或虚拟内存空间。

  3. 事务的局部地址空间(也在内存中)。

事务要读取数据,首先要将数据取到缓冲区中,然后缓冲区的数据可以被事务读取到局部空间。事务的写入过程与此相反,先在局部空间中创建新值,然后再将新数据拷贝到缓冲区中。缓冲区中的数据通常是由缓冲区管理器决定何时写入磁盘,而不是立刻持久化到磁盘。

为了便于研究日志和事务管理的细节,我们使用一系列原语来描述数据库操作:

  1. INPUT(X):将数据库元素 X 从磁盘拷贝到缓冲区。

  2. READ(X, t):将数据库元素 X 从缓冲区拷贝到事务的局部变量 t。

  3. WRITE(X, t):将局部变量 t 的值拷贝到缓冲区的数据库元素 X,如果 X 不在缓冲区,先执行 INPUT(X)。

  4. OUTPUT(X):将数据库元素 X 从缓冲区拷贝到磁盘。

在这里假设:数据库元素 X 的大小 = 磁盘块大小 = 缓冲区块大小。

【例 5.1】银行转账事务

一个数据库中有 A、B 两个账户(元素),A 和 B 之间进行转账操作,在任何一致的状态中它们的值的总和是固定的。

一个转账事务 T 主要有两个步骤:

A:A 账户减 10

B:B 账户加 10

假设 A 和 B 的初值都为 15,事务 T 从一个一致的状态(A+B=15+15=30)开始,事务正常执行且期间没有发生系统故障,那么最终的状态必然也是一致的,A 和 B 的值发生了变化,但他们的和没有发生变化(A+B=5+25=30)。

T 的执行包括从磁盘读取 A 和 B,执行运算,将 A 和 B 的新值写入缓冲区。之后缓冲区管理器会执行 OUTPUT 原语,将数据写回磁盘。表 5-1 中展示了事务 T 的执行过程,以及每个步骤执行之后 A 和 B 在缓冲区和磁盘中的值。

表 5-1

操作 t A(内存) B(内存) A(磁盘) B(磁盘)
READ(A, t) 15 15 15 15
t:= t-10 5 15 15 15
WRITE(A, t) 5 5 15 15
READ(B, t) 15 5 15 15 15
t:= t+10 25 5 15 15 15
WRITE(B, t) 25 5 25 15 15
OUTPUT(A) 25 5 25 5 15
OUTPUT(B) 25 5 25 5 25
  1. 第 1 步 READ(A, t) 命令将 A 的值拷贝到局部变量 t 中,如果 A 不在缓冲区中,那么就会先执行 INPUT(A) 命令。

  2. 第 2 步将 t 减 10,这一步不会改变 A 在缓冲区和磁盘上的值。

  3. 第 3 步将 t 写到缓冲区的 A 中,这一步也不会影响磁盘上 A 的值。

  4. 直到第 7 步,OUTPUT(A) 将 A 的新值写入磁盘,完成持久化。

如果表 5-1 中的步骤顺利执行,那么事务前后数据库都处于一致性状态。

  • 如果在 OUTPUT(A) 之前系统发生故障,因为磁盘上的数据没有任何变化,一致性得以保持。

  • 如果系统故障在 OUTPUT(B) 之后发生,磁盘上的 A 和 B 都已经修改,仍然满足一致性要求。

  • 如果系统故障发生在 OUTPUT(A) 和 OUTPUT(B) 之间,那么数据库就会处于不一致的状态,这种情况下就需要进行修复,要么将 A 和 B 都重置为原值,要么将它们都更新为新值。