讲义 #19:记录方案

302 阅读10分钟

讲义 #19:记录方案

1. 崩溃恢复

恢复算法是尽管失败时仍确保数据库原子性、一致性、持久性的技术。发生崩溃时,所有尚未提交到磁盘的内存中的数据都处于丢失的危险之中。恢复算法防止发生意外后的信息丢失。

4b9481370134cc7f17300fc6d9da24a8eeac7ac6.png 每个恢复算法都有两个部分:

  • 在正常事务处理过程中确保DBMS可以从故障中恢复的操作。
  • 故障之后将数据库恢复到确保原子性、一致性、持久性的的操作。

恢复算法中使用的关键原始词是UNDO和REDO。并非所有算法都使用两种原语。

  • UNDO:消除未完成或中断事务的影响的过程。
  • REDO: 重新将提交的事务的影响持久化的过程。

index

  • Failure Classification

  • Buffer Pool Policies

  • Shadow Paging

  • Write-Ahead Log

  • Logging Schemes

  • Checkpoints

2. 存储类型

DBMS根据底层存储设备分为不同的组成部分。 → 易失性与非易失性

我们还必须对DBMS需要处理的不同类型的故障进行分类。

  • 易失性存储

    - 断电或者程序退出后数据不会保存

    - 示例:DRAM,SRAM。

  • 非易失性存储

    - 在失去电力或程序后,数据持续存在。

    - 示例:HDD,SDD。

  • 稳定性存储

    - 一种不存在的非易失性存储形式,可在所有可能的故障场景中幸存下来。

    - 使用多个存储设备进行近似模拟

3. 故障分类

由于DBMS基于基础存储设备分为不同的组件,因此有大量不同类型的故障需要DBMS处理的。这些故障中的一些是可以恢复的,另一些则不行。

类型#1:事务故障

当一个事务产生错误并且必须中止时会发生事务失败。有两种可能导致事务失败的错误类型:

  • 逻辑错误(Logical Errors):由于某些内部错误条件(例如:违反数据一致性约束)

  • 内部状态错误(Internal State Errors):DBMS必须终止由于错误条件而活动的事务(例如:死锁)

类型#2:系统故障

系统故障是在意外恢复协议中也必须考虑的发生在硬件或软件中的不可预料的故障。

  • 软件故障:DBMS实现存在问题(例如,意外的除零异常),系统必须停止。

  • 硬件故障:托管DBMS的计算机崩溃(例如,拉下电源插头)。我们假设非易失性存储内容不会因系统崩溃而破坏。

类型#3:存储媒体故障

存储媒体故障是当物理存储计算机损坏时发生的不可修复的故障。

当存储介质出现故障时,DBMS必须从存档版本中恢复数据。

  • 不可修复的硬件故障:磁盘头损坏或类似的磁盘故障破坏了所有或部分 非易失性存储。假定破坏是可检测的。

4. 缓冲池管理策略

DBMS需要确保以下规则:

  • 任何事务,一旦DBMS告诉用户它已经提交,那么它的更改都是持久的。

  • 如果事务中止,则更改不会生效。

控制策略决定DBMS是否允许未提交的事务覆盖最近提交的对象值到非易失性存储中(事务是否将未提交的更改可以写入磁盘)

  • Steal: 允许Buffer Pool里未提交事务所修改的脏页刷回到持久存储

  • No-steal: 不允许Buffer Pool里未提交事务所修改的脏页刷到持久存储中

强制性策略决定了DBMS是否要求事务在被允许提交时的所有更新都反映在非易失性存储中。

  • Force: 事务提交时,所修改的页面必须强制刷回到持久存储中

  • No-Force: 事务提交时,所修改的页面不需要强制刷回到持久存储中

强制写入使恢复变得容易,因为所有更改都保留了,但是导致较差的运行性能。

最简单的缓冲池策略实现被称为NO-STEAL + FORCE。在这个策略中,DBMS不需要撤销中止的事务修改,因为修改没有写入到硬盘。同样不需要重新执行一个已提交的事务,因为所有的修改已经保证在提交时写入到硬盘。图1是 NO-STEAL + FORCE的一个示例。

NO STEAL + FORCE 的限制是事务需要修改的所有数据都必须适合在内存中。否则,该事务就不能执行,因为DBMS不允许在事务提交之前将脏页写出到磁盘。

2022-05-28_16-31.png 图1:NO-Steal + Force示例 - DBMS使用NO-STEAL + FORCE缓冲策略。一个事务的所有变化只有在提交时才会被写入磁盘。一旦时间表从步骤#1开始,来自T1和T2的变化被写到缓冲池中。由于FORCE策略的存在,当T2在步骤#2提交时时,它的所有变化都必须写到磁盘上。要做到这一点,DBMS会在磁盘上制作一个内存的拷贝,只应用T2的变化,并将其写回 写回磁盘。这是因为NO-STEAL阻止了来自T1的未提交的变化被写入磁盘。在步骤#3,DBMS回滚T1是很简单的,因为没有来自T1的脏变化在磁盘上。

5. Shadow Paging

DBMS维护两个独立的数据库副本。

  • master:只包含来自已提交事务的变化。
  • shadow:临时数据库,包含来自未提交事务的变化。

更新只在影子副本中进行。当一个事务提交时,影子被原子化地切换为成为新的主数据库。这是一个NO-STEAL+FORCE系统的例子。 图2显示了影子页的高级例子。

2022-05-28_16-56.png 图2:Shadow Paging - 数据库根目录指向一个主页面表,该表指向磁盘上的页面。当一个更新事务发生时,一个影子页表被创建,它指向与主表相同的页面。修改是在磁盘上的一个临时空间进行的,并且影子表被更新。提交时,数据库根指针被重定向到影子表,影子表成为新的主表。

实现

DBMS将数据库页面组织成一个树状结构,根部是一个单一的磁盘页面。有树的两个副本,主副本和影子副本。根部总是指向当前的主副本。当一个事务执行时,它只对影子副本进行修改。

当一个事务想要提交时,DBMS必须安装其更新。要做到这一点,它只需要覆盖根,使其指向数据库的影子副本,从而将主副本和影子副本对调。在覆盖根目录之前,没有一个事务的更新是驻留在磁盘上的数据库的一部分。在覆盖后,所有的事务更新都是磁盘常驻数据库的一部分。

恢复

Undo:删除影子页面。使主指针和DB根指针变为独立的。

Redo:完全不需要

劣势

影子分页的一个缺点是复制整个页表的成本很高。实际上,只有树中导致更新叶子节点的路径需要被复制,而不是整个树。此外,影子分页的提交开销也很高。提交时需要刷新每一个更新的页、页表和根。这导致了碎片化的数据,也需要垃圾收集。另一个问题是,它只支持一个写事务或一批写事务一次性持久化。

6. 日志文件(shadow paging 策略实现)

当一个事务修改了一个页面时,DBMS在覆盖主版本之前将原始页面复制到一个单独的日志文件。在重新启动后,如果日志文件存在,那么DBMS就会恢复它,以撤销来自未提交的事务的修改。

在 2010 年之前,SQLite 采用的就是类似 shadow paging 的策略,当写事务需要修改某页数据时,DBMS 会先将原始数据页保存到一个独立的 journal 文件中,然后再将对应的修改持久化到磁盘中。当 SQLite 重启后,如果发现磁盘中存在 journal 文件,则直接将对应的数据页覆盖到磁盘中即可。

7. Write-Ahead Logging

有了写前日志,DBMS在对磁盘页进行修改之前,将所有对数据库的修改记录在一个日志文件中(在稳定的存储上)。日志包含足够的信息来执行必要的撤销和重做操作,以便在崩溃后恢复数据库。DBMS必须将对应于对数据库对象所做更改的日志文件记录写入磁盘,然后才能将该对象刷新到磁盘。图#3中显示了一个WAL的例子。WAL是一个STEAL+NO-FORCE系统的例子。

在影子分页中,DBMS被要求对磁盘上的随机非连续页进行写入。预写日志允许DBMS将随机写入转换为顺序写入,以优化性能。因此,几乎每一个DBMS都使用预写日志(WAL),因为它具有最快的运行时性能。但是,DBMS使用WAL的恢复时间要比影子分页慢,因为它必须重新播放日志。

2022-05-28_17-47.png 图3:预写日志 - 当事务开始时,所有的变化都被记录在内存中的WAL缓冲区中,然后才被送到缓冲池中。当需要提交时,WAL缓冲区会被刷出到磁盘。一旦WAL缓冲区安全地在磁盘上,交易结果就可以被写入。

实现

DBMS首先将一个事务的所有日志记录放在易失性存储中。然后,在允许非易失性存储中的页面本身被覆盖之前,所有与更新的页面有关的日志记录被写入非易失性存储。在所有的日志记录被写入稳定存储之前,一个事务不被认为是提交的。

当事务开始时,为每个事务写一条<BEGIN>记录到日志,以标记其起点。 当一个事务完成时,向日志写一条<COMMIT>记录,并确保在它向应用程序返回确认之前,所有的日志记录都被刷新。

每个日志条目包含关于单个对象变化的信息:

  • Transaction ID.

  • Object ID.

  • Before Value (used for UNDO).

  • After Value (used for REDO).

DBMS在告诉外界一个事务已经成功提交之前,必须将一个事务的所有日志条目刷新到磁盘。系统可以使用 "分组提交 "优化,将多个日志刷新放在一起,以分摊开销。DBMS可以随时将脏页写入磁盘,只要是在刷新相应的日志记录之后。

8. 记录方案

日志记录的内容可以根据实现情况而变化。

Physical Logging:

  • 记录对数据库中某一特定位置所做的字节级改变。
  • 例子:一条记录在页面中的位置

Logical Logging:

  • 记录事务执行的高层次操作。
  • 不一定限制在一个页面上。
  • 与物理日志相比,需要在每个日志记录中写入更少的数据,因为每个记录可以在多个页面上更新多个图元。然而,在非确定性并发控制方案中存在并发事务时,很难用逻辑日志实现恢复。此外恢复需要更长的时间,因为你必须重新执行每个事务。
  • 例子:一个事务所调用的UPDATE、DELETE和INSERT查询。

Physiological Logging:

  • 混合方法,日志记录针对单一页面,但不指定页面的数据组织。也就是说,根据页面中的槽号来识别图元,而不指定变化在页面中的确切位置。因此,DBMS可以在日志记录被写入磁盘后重新组织页面。
  • 在DBMS中最常用的方法。