目录一、日志的物理本质从理论框架到工程实现二、物理日志字节级的修改记录三、逻辑日志操作语义的高层记录四、缓冲策略与Undo/Redo协议分类五、Undo/Redo协议最完整的恢复能力六、Undo/No-Redo与No-Undo/Redo协议七、三种协议的权衡矩阵八、结语日志——恢复机制的基石一、日志的物理本质从理论框架到工程实现上一篇我们确立了日志先行原则的理论基础——在数据页写入磁盘之前描述该修改的日志记录必须先落盘。我们也定义了Undo与Redo的语义——Undo以日志的前像为数据源撤销未提交事务的修改Redo以日志的后像为数据源重放已提交事务的修改。这些概念在理论层面是清晰而自洽的。然而当从理论迈入工程实现时一系列必须回答的细节问题浮现出来。日志记录应该包含什么内容——是记录被修改的每一个字节还是记录执行了什么操作缓冲池中的脏页应该在什么时机刷入磁盘——是事务提交时强制刷盘还是由缓冲管理器自主调度Undo操作本身是否也需要写日志——如果需要如何在Undo中途再次崩溃时保证恢复的幂等性这些问题的答案并不唯一。不同的日志类型、不同的缓冲策略、不同的Undo/Redo分工构成了WAL协议的不同变体。理解这些变体的差异与权衡是理解现代数据库恢复机制的工程深度的关键。二、物理日志字节级的修改记录物理日志记录数据修改的最底层细节——被修改页面的页面ID、页内偏移量、修改前的字节序列前像和修改后的字节序列后像。一条典型的物理日志记录形如事务T₁将页面P₅偏移量120处的4字节数据从0x00000064前像十进制100修改为0x00000050后像十进制80。物理日志的核心优势是简单。Undo操作的语义极其直截——将指定页面的指定偏移量处的字节序列恢复为前像。Redo操作同样直截——将指定位置更新为后像。这种操作的简洁性带来了两个重要特性。其一Undo和Redo操作天然具有幂等性——无论执行一次还是多次将同一个位置的字节设置为同一个值最终结果不变。其二物理日志不依赖任何上层的逻辑模式知识——日志管理器无需理解B树的分裂逻辑或表的列类型只需机械地读写页面的字节块。物理日志的代价是空间开销大。如果一个事务修改了一行记录而该行横跨了页面的多个位置如行头、各列分储、行尾每一个位置的变化都需要一条日志记录。如果一个事务执行了UPDATE 表 SET 列 新值 WHERE 条件匹配了数十万行物理日志将产生数十万条日志记录每一条都包含完整的前像和后像。日志量可能膨胀到接近甚至超过数据修改本身的大小。物理日志的另一个限制是阻碍了某些并发优化。由于物理日志绑定了页面和偏移量的物理坐标如果Undo操作需要移动数据例如B树节点分裂的回滚导致页面布局变化物理日志的固定坐标假设将面临挑战。这一限制是逻辑日志得以引入的重要动因。三、逻辑日志操作语义的高层记录逻辑日志不记录字节级的物理变化而是记录被执行的操作本身。一条逻辑日志记录形如事务T₁在表“员工”中插入了元组工号E001姓名张三工资8000。逻辑日志记录的是操作的意图和参数而非操作在磁盘页面上的具体物理效果。逻辑日志的空间效率显著优于物理日志。DELETE FROM 日志表 WHERE 时间 2020-01-01这样一条语句删除了数百万行逻辑日志只需要一条记录记载该操作。Undo该操作时只需执行逆操作——将符合条件的行重新插入。逻辑日志的紧凑性在批量操作和数据仓库场景中尤为突出。逻辑日志为Undo操作提供了更高的灵活性。Undo一个插入操作逻辑上等同于执行一个删除操作——系统无需关心该插入当时在哪个页面的哪个偏移量创建了元组只需根据操作语义执行相应的逆操作。这种“语义级Undo”能够自然地应对数据物理位置的变化也使得在Undo过程中发生的新页面分配和索引更新能够被正确处理。然而逻辑日志的代价是复杂性。Undo不再是机械的字节拷贝而是需要执行逆向操作——这要求恢复管理器具备一个完整的“微型SQL执行引擎”能够在恢复过程中执行基本的插入、删除和更新操作。逻辑Undo操作本身必须生成新的日志记录——否则如果在Undo中途再次崩溃系统将丢失Undo的进度。这些Undo过程中生成的日志记录通常包含物理信息和逻辑信息是下一节讨论的混合日志方案。逻辑日志面临的另一个深层挑战是操作的原子性问题。一个逻辑操作——如“删除符合条件的所有行”——可能在物理层面涉及数十个页面的修改。如果系统在逻辑操作的执行中途崩溃该逻辑操作的部分效果可能已经写入磁盘。此时Undo该逻辑操作不再是一个简单的逆操作而是需要识别并撤销那些已经持久化的部分效果——这模糊了逻辑操作的原子性边界。因此在实践中纯逻辑日志通常用于数据库的逻辑复制和变更数据捕获场景而非用于底层的崩溃恢复。恢复层的逻辑日志通常与物理日志混合使用。四、缓冲策略与Undo/Redo协议分类日志的类型——物理还是逻辑——是WAL协议设计的一个维度。另一个独立的维度是缓冲策略——缓冲池中被事务修改的脏页在什么时机允许刷入磁盘。缓冲策略有两种基本选择。Steal策略允许在事务提交之前将事务修改的脏页刷入磁盘。这种策略提高了缓冲池的灵活性——当事务修改了大量页面导致缓冲池空间紧张时系统可以将这些页面刷盘以释放内存无需等待事务提交。No-Steal策略禁止在事务提交前刷盘——所有脏页必须留在缓冲池中直到事务提交。No-Steal简化了Undo逻辑未提交事务的修改不会出现在磁盘上但要求缓冲池足够大以容纳每个活跃事务的全部修改。对应的Force策略要求事务提交时必须将其所有脏页强制刷入磁盘。这种策略确保已提交事务的修改一定存在于磁盘上简化了Redo逻辑。No-Force策略允许事务提交时不刷脏页——已提交的修改可以仅存在于缓冲池中由缓冲管理器在后续的某个时间点异步刷盘。Steal/No-Steal与Force/No-Force两两组合产生了四种理论上的缓冲策略方案。但Force策略在工程实践中极少被采用——事务提交时强制刷盘意味着每次提交都需要大量随机IO提交延迟将高到不可接受。几乎所有现代数据库系统都采用No-Force策略——提交时只需将日志强制落盘数据页的刷盘在后台异步完成。因此实践中存在两种主要的方案组合No-Steal No-Force也称为Undo/No-Redo方案以及Steal No-Force也称为Undo/Redo方案。五、Undo/Redo协议最完整的恢复能力Undo/Redo协议是目前主流数据库系统的标准选择与Steal No-Force策略匹配。这个组合赋予了系统最大的运行灵活性Steal允许缓冲池在内存紧张时自由驱逐未提交事务的脏页No-Force允许事务提交时无需等待数据页落盘。但灵活性以恢复复杂性为代价——崩溃后系统需要同时执行Undo和Redo。Redo的必要性来自No-Force策略。事务提交时其修改的数据页可能仍滞留在缓冲池中未刷入磁盘。如果系统在此时崩溃这些已提交的修改从内存中蒸发。重启后系统必须通过Redo将这些修改从日志重放到磁盘上——将数据页更新为已提交事务修改后的状态。Undo的必要性来自Steal策略。未提交事务的脏页可能已经被刷入了磁盘。崩溃后这些未提交修改残留在磁盘上破坏了原子性。重启后系统必须通过Undo将这些修改从磁盘上抹去——将数据页恢复为未提交事务修改前的状态。日志的物理/逻辑混合设计支撑了这一双重需求。日志记录同时包含物理信息页面ID、偏移量用于Redo和逻辑信息操作类型、参数用于Undo。这种混合设计被称为物理逻辑日志——日志记录在物理层面描述了修改位置确保Redo的机械性在逻辑层面描述了操作语义赋予Undo处理的灵活性。一个典型的物理逻辑日志记录包含页面ID物理定位用于Redo页内偏移量物理定位逻辑操作类型如“插入元组”以及操作参数如插入的元组各列的值。Redo时系统在指定页面的指定位置应用物理修改。Undo时系统根据逻辑操作类型执行逆向操作——在Undo一个“插入”操作时执行相应的逻辑删除并在补偿日志中记录该删除操作以确保可重续性。六、Undo/No-Redo与No-Undo/Redo协议Undo/No-Redo协议对应于No-Steal No-Force策略。No-Steal禁止未提交事务的脏页刷盘因此崩溃后磁盘上不存在未提交修改——Undo不再需要。Redo仍然需要因为No-Force允许已提交修改留在缓冲池中未落盘。Undo/No-Redo协议的日志设计可以简化——日志记录只需要包含后像用于Redo不需要前像因为不需要Undo。恢复流程也更简洁Redo所有已提交事务的修改即可无需Undo阶段。简化以缓冲池约束为代价——系统必须确保有足够的内存容纳每个活跃事务的所有脏页。对于长事务或修改量大的事务这一约束可能迫使缓冲池容量膨胀。No-Undo/Redo协议对应于Steal Force策略。Force要求事务提交时所有脏页强制落盘因此已提交修改不会因崩溃而丢失——Redo不再需要。但Steal允许未提交脏页刷盘Undo仍然需要。这一协议在工程实践中极为罕见因为Force策略的提交刷盘开销在OLTP场景中不可接受。但在某些嵌入式数据库或内存数据库中由于数据总量小且常驻内存Force刷盘的开销可控No-Undo/Redo协议因其恢复逻辑的简洁而有一定应用。七、三种协议的权衡矩阵三种WAL协议变体构成了从简单到复杂的工程选择光谱。Undo/No-Redo协议实现最简单恢复仅需Redo日志仅需后像但以缓冲池容量的硬约束为代价。No-Undo/Redo协议恢复仅需Undo但以事务提交的强制刷盘IO为代价。Undo/Redo协议恢复最复杂同时需要Undo和Redo日志需要同时记录前像和后像或等效信息但它赋予了系统最大的运行时灵活性——缓冲池管理不受事务边界约束事务提交不受数据刷盘延迟约束。几乎所有主流关系数据库系统——MySQL InnoDB、PostgreSQL、Oracle、SQL Server——都选择了Undo/Redo协议。这不是偶然的趋同。现代OLTP工作负载的特点是事务短小、并发量大、缓冲池竞争激烈。Steal策略让缓冲管理器可以根据页面的访问热度而非事务边界来决定刷盘时机显著提升了缓冲池的有效利用率。No-Force策略让事务提交延迟仅受日志落盘延迟约束而非数据页刷盘的随机IO延迟约束。这两项灵活性对于高并发下的吞吐量和响应延迟至关重要。支付Undo/Redo双重恢复逻辑的复杂度代价换取运行时的每一分性能空间这是工业级数据库系统的一致工程判断。八、结语日志——恢复机制的基石物理日志与逻辑日志的混合设计Steal与No-Force策略的联合选择Undo与Redo的互补分工——这些WAL协议的设计要素共同构筑了现代数据库系统恢复机制的工程基石。它们并非独立的点状决策而是一个紧密耦合的设计矩阵缓冲策略的选择决定了恢复时需要执行Undo还是Redo日志类型的选择影响了Undo和Redo的实现复杂度而恢复流程的设计反过来又约束了缓冲策略和日志格式的自由度。在下一篇中我们将站在这套工程基石之上系统展开数据库恢复机制中最具影响力的算法框架——ARIES。ARIES全称“Algorithm for Recovery and Isolation Exploiting Semantics”由IBM Almaden研究中心的C. Mohan等人在1992年提出将WAL协议、物理逻辑日志、Steal/No-Force策略、补偿日志与检查点技术整合为一套完整的恢复体系。ARIES的三阶段恢复流程——分析、Redo、Undo——是现代数据库恢复机制的标准范式。