开发者
oGRAC redo日志简介

oGRAC redo日志简介

oGRAC

发表于 2026/05/26

0

oGRAC redo日志简介

  • redo日志记录对数据页所做的所有修改操作(如插入、更新、删除)。它采用追加写的方式,顺序写入磁盘上的日志文件。在系统崩溃或故障后,通过重放这些日志,将数据库恢复到故障发生前的状态,确保持久性。
  • redo日志会先在内存组好(log_batch),再写到redo文件(log_flush->log_flush_to_disk刷盘),先写日志,再刷脏页。
// log_file_ctrl_bk_t is behind it.
typedef struct st_log_file_head {
    knl_scn_t first;
    knl_scn_t last;
    volatile uint64 write_pos;
    uint32 asn;
    int32 block_size : 16;
    int32 cmp_algorithm : 4;
    int32 reserve : 12;
    uint32 rst_id;
    uint32 checksum;
    uint64 first_lsn;
    uint64 last_lsn;
    uint32 dbid;
    uint32 recid;
    int64 arch_ctrl_stamp;
    int64 real_size;
    uint32 dest_id;
    uint8 pad[4];
    uint8 unused[OG_LOG_HEAD_RESERVED_BYTES];  // padded log_file_head_t to 512 bytes
} log_file_head_t;
// log_batch组装后写入磁盘,磁盘读取时是通过地址解析出各个值,所以数据异常时读取时算的size不一定是真实值
typedef struct st_log_batch {
    log_batch_id_t head;
    knl_scn_t scn;
    uint32 padding;
    uint32 size;        // batch length, include log_batch_t head size

    uint32 space_size;  // The actual space occupied by the batch
    uint8 part_count;   // a batch contains multiple buffers,less or queue to buffer count
    uint8 version;
    uint16 batch_session_cnt;

    union {
        uint64 raft_index;
        uint64 lsn;  // max lsn of log groups inside this batch, clustered database not support PAXOS/RAFT
    };

    uint16 checksum;
    bool8 encrypted : 1;
    uint8 reserve : 7;
    uint8 unused[5];
} log_batch_t;

几个概念:

  • 脏页:内存中的缓冲页(Buffer Pool)被修改后,内存中的内容与磁盘上的对应页不一致,这种页称为脏页。
  • 刷脏页:将内存中的脏页内容写回磁盘上的数据文件的过程。
  • 落盘:广义上指任何内存数据(日志、数据页)被写入持久化存储(磁盘、SSD)的动作。这里常特指数据页落盘(即刷脏页)和日志落盘。
  • 检查点:检查点是一个数据库事件,系统将所有内存中的脏页(已修改但未写回磁盘的数据页)强制刷写到磁盘,并记录下此时 Redo 日志的最新位置(LSN 或日志文件偏移),然后将这个位置持久化到控制文件。
  • pin住:指的是将一个对象固定在内存中,使其不会被换出或失效。

崩溃恢复时:

  • 修改已提交:redo重做;
  • 修改未提交:undo回滚;
  • 刷脏页(pagewritercheckpoint)不关心事务状态,它只是将内存页面内容写入磁盘。如果刷入了未提交事务修改的页面,磁盘上就会暂时包含未提交的数据。这没关系,因为恢复时仍然可以通过日志中的事务状态进行回滚。

redo中的几个序列号:

LSN日志序列号

唯一标识每一条日志记录(或每个日志批次 log_batch_t)在日志流中的逻辑位置

  • 作用:

(1)用于恢复:确定需要重做的日志起点和终点;

(2)用于检查点:记录脏页对应的最新 LSN,恢复时只需从检查点 LSN 开始重放;

(3)用于事务提交:确保日志已落盘到该 LSN。

  • 特点:不是简单的文件偏移,而是与日志记录一一对应的逻辑序号。某些数据库中 LSN 也可能包含文件号和块号的编码,但通常单独作为 64 位整数。

LFN日志文件号

  • 作用:

(1)与 ASN、Block ID 结合,可以定位日志在磁盘上的物理位置;

(2)用于日志切换:当当前文件写满时,LFN 递增,创建新的日志文件;

(3)用于归档:归档日志按 LFN 顺序存储。

  • 存储:通常与 ASN 一起出现在 log_point_t 结构中,表示某个 LSN 对应的物理文件。

ASN 归档序列号

表示日志的归档序号或节点内的日志流序号。有时也用于标识日志批次序号。

WAL 原则:日志先落盘,数据页后落盘

数据库保证:在脏页落盘之前,该页对应的 Redo 日志必须已经安全写入磁盘。这是崩溃恢复的基础。

  • 如果事务提交,必须先将该事务产生的 Redo 日志强制刷盘(log flush),然后才能向客户端返回成功。此时脏页可能还在内存中。
  • 如果数据库崩溃,重启后通过读取磁盘上的 Redo 日志,重新执行(重做)那些已提交但尚未落盘的事务修改,从而恢复数据一致性。

相关函数

写盘:

log_flush

log_flush_to_disk

读盘:

dtc_rcy_process_batches

<font style="color:#DF2A3F;">dtc_rcy_read_node_log_proc:</font>DTC恢复阶段读盘上的redo

    dtc_read_node_log 

        dtc_rcy_read_node_log

            <font style="color:#DF2A3F;">drc_rcy_read_online_log</font>:读盘rcy_write_point位置到read_buf,读取file head

                dtc_rcy_read_log

            dtc_rcy_read_archived_log

     -read_size=0:
    dtc_rcy_next_file:读下一个文件
     -未结束:

    dtc_rcy_read_node_log:继续读日志

    -read_size!=0
      find_max_lsn_and_move_point:推进读盘位置rcy_write_point:一次覆盖本段读入数据内连续多个 batch,LSN 取最后一个

dtc_rcy_fetch_log_batch:读取缓冲区日志进行恢复:每次一个被选中的 batch(全局 LSN 最小)

dtc_rcy_pitr_replay_end

dtc_rcy_process_batch

本页内容