1. 概念

  • 什么是原子性?

在同一项事务的处理过程中,事务保证了对多个数据源的修改,要么同时成功,要么同时撤销。

  • 什么是持久性?

事务保证所有成功被提交的数据的修改都能正确的被持久化,不丢失数据。

数据只有写入磁盘、磁带等持久化存储器后才会拥有持久性,只存储在内存中的数据,一旦遇到应用程序突然崩溃,或者数据库、操作系统一侧崩溃,甚至是机器突然宕机等情况数据就会丢失。

但现实最大的困难是“写入磁盘”这个操作并不是原子的,不仅有“写入”和“未写入”状态,还客观存在着“正在写”的中间状态。

但是由于崩溃和中间状态都不能消除,所以如果不采取措施,将内存中的数据写入磁盘,并不能保证原子性和持久性。

2. 怎么办?

只能在崩溃后采取恢复的补救措施,也就是“崩溃恢复”。

为了顺利进行崩溃恢复,在磁盘中写入数据就不能像程序修改内存中的变量值那样,直接改变某表某行的某个值,而是必须将修改数据这个操作的全部信息,包括:修改什么数据、数据物理上位于哪个内存页和磁盘块中、从什么值改为什么值,等等,以日志(仅仅以顺序追加的文件写入方式)的形式记录到磁盘中。

只有日志记录全部安全落盘,数据库在日志中看到代表事务成功提交的“提交记录”(Commit Record)后,才会根据日志上的信息对真正的数据进行修改,修改完成后,再在日志中加入一条“结束记录”(End Record)表示事务已完成持久化,这种实现事务的方法被称为“提交日志”(Commit Logging)。

原理分析:

  1. 只要日志中存在Commit Record记录,即使修改数据时候崩溃了,重启后,可以根据日志信息继续修改数据,保证了持久性。
  2. 只要日志中不存在Commit Record记录,那么整个事务就是失败的,重启后会看到没有Commit Record记录的日志,此时事务就像没有发生过,保证了原子性。

3. Commit Logging的缺陷

所有对数据的真实修改必须发生在事务提交以后,再此之前,即使磁盘I/O有足够的空闲、即使某个事务修改的数据量非常庞大,占用了大量的内存缓存区。无论何种理由,都不允许在事务提交之前就修改磁盘上的数据。这是Commit Logging成立的前提。但是对提升数据库的性能非常不利。

4. Write Ahead Logging-提前写入日志

就是说,允许在事务提交之前写入变动数据。

按照事务提交点,将何时写入变动数据划分为FORCE、STEAL

  • FORCE:事务提交后,又可以细分为:FORCE(变动数据必须同时完成写入)、NO-FORCE;事实上,绝大部分数据库采用的都是NO-FORCE,毕竟只要日志在,变动数据随时可以持久化。
  • STEAL:事务提交前,可以分为:STEAL(允许变动数据提前写入)、NO-STEAL(不允许变动数据提前写入);从优化磁盘I/O的角度,允许数据提前写入,有利于利用I/O资源,也有利于节省数据库缓存区的内存。

Commit Logging允许NO-FORCE,不允许STEAL,一旦发生崩溃这些提前写入的数据就成了错误。

Write Ahead Logging允许NO-FORCE,也允许STEAL,给出的解决方案是增加一种 undo log(一般被称为回滚日志)的日志类型,当变动数据写入磁盘前,必须先记录undo log,注明修改了那个位置的数据,从什么值改为了什么值等等,以便在事务回滚或者崩溃恢复时根据undo log对提前写入的数据变动进行擦除;此前记录的用于崩溃恢复时重演数据变动的日志就称为redo log(一般称为重做日志)。

有了undo log,在发生崩溃恢复时,Write Ahead Logging会经历下面三个阶段:

  1. 分析阶段:找出待恢复事务的集合
  2. 重做阶段:重演历史(待恢复事务集合),执行完成后,将其移出待恢复事务集合
  3. 回滚阶段:经过1、2处理剩余的事务集合,都是需要回滚的事务,根据undo log中的信息,将已经提前写入磁盘的信息重新改回去。

image-20240602185932390


本站由 卡卡龙 使用 Stellar 1.27.0 主题创建

本站访问量 次. 本文阅读量 次.