CLOG日志管理器

模块概述

CLOG(Commit Log,事务提交日志)是PostgreSQL中记录事务最终状态的核心模块,CLOG日志管理器负责CLOG日志的全生命周期管理,包括日志缓冲池维护、日志读写、页面初始化、段文件管理等核心操作。其底层依赖SLRU(Simple LRU)缓冲池 实现日志页面的内存管理,CLOG缓冲池是全局唯一的共享内存对象,确保数据库所有进程对事务状态的访问一致性。

1. 核心数据结构与日志定位规则
1.1 事务状态定义

CLOG仅记录事务的4种最终状态(宏定义),每个状态占用2个比特位,具体如下:

宏定义 十六进制值 状态说明 备注
TRANSACTION_STATUS_IN_PROGRESS 0x00 事务执行中 初始默认值,非最终状态,无法主动设置
TRANSACTION_STATUS_COMMITTED 0x01 事务已提交 事务最终状态之一
TRANSACTION_STATUS_ABORTED 0x02 事务已中止 事务最终状态之一
TRANSACTION_STATUS_SUB_COMMITTED 0x03 子事务已提交 仅适用于嵌套事务,父事务未提交时子事务的最终状态
1.2 日志存储与容量计算
  • 存储粒度:1个字节可存储4个事务的CLOG状态(每个状态占2bit);
  • 单页容量:PostgreSQL默认页面大小为8KB,单页可存储 8KB * 8bit / 2bit = 32768(32K) 个事务的状态;
  • 段容量:1个CLOG段包含32个页面,可存储 32 * 32K = 1024K(1M) 个事务的状态;
  • 存储路径:CLOG日志文件存储于数据库数据目录 PGDATA/pg_clog 下,段文件以16进制段号命名(仅需4位16进制数,对应12位二进制,可覆盖全部事务ID范围)。
1.3 日志定位规则

通过事务ID(XID,32比特)可计算出该事务CLOG日志的4元组定位信息,核心宏定义如下:

宏定义 功能
TransactionIdToPage(xid) 根据事务ID计算其CLOG日志所在的页面号
TransactionIdToPgIndex(xid) 根据事务ID计算其在页面内的整体偏移
TransactionIdToByte(xid) 根据事务ID计算其在页面内的字节偏移
TransactionIdToBIndex(xid) 根据事务ID计算其在字节内的位偏移(0/2/4/6,对应4个事务/字节)

4元组 <Segmentno, Pageno, Byte, Bindex> 可唯一定位一条CLOG日志记录:

  • Segmentno:段号(由页面号/32计算得出,1个段包含32页);
  • Pageno:段内页面号;
  • Byte:页面内字节偏移;
  • Bindex:字节内位偏移。
1.4 缓冲池控制结构

CLOG缓冲池是全局共享的SLRU缓冲池,核心控制结构如下:

  • 名称:共享内存中注册名称为CLOGCtl
  • 控制变量:静态变量ClogCtl(SLRU缓冲池控制结构),记录缓冲池在共享内存的地址、写操作同步信息(默认强制同步写);
  • 锁机制:通过缓冲池控制锁(排他锁/共享锁)保证并发访问安全。
2. 核心操作说明

CLOG日志管理器的核心操作围绕日志生命周期展开,所有操作均基于SLRU缓冲池实现,关键操作如下:

2.1 缓冲池初始化(CLOGShmemInit)

功能:在共享内存中初始化CLOG SLRU缓冲池,为后续操作提供基础环境。
核心流程

  1. 设置CLOG共享内存控制信息(如日志页面前后关系对比函数);
  2. 调用SimpleLruInit初始化SLRU缓冲池,指定CLOG日志磁盘存储路径;
  3. 在共享内存中注册唯一的CLOG缓冲池,初始化ClogCtl控制结构;
  4. 分配缓冲池控制锁,用于后续并发控制。
2.2 日志写操作(TransactionIdSetStatus)

功能:设置指定事务的最终状态(写入CLOG日志),底层操作,不对外直接暴露。
前置条件:仅允许设置COMMITTED/ABORTED/SUB_COMMITTED三种状态(IN_PROGRESS为初始值,非最终状态,无需设置)。
核心流程

  1. 校验状态合法性,申请CLOG缓冲池排他锁(修改操作需独占);
  2. 调用SimpleLruReadPage将目标日志页面加载至缓冲池(已加载则直接使用);
  3. 通过事务ID计算4元组,修改缓冲池中对应位置的事务状态;
  4. 标记该页面为脏页(需后续刷盘),释放缓冲池锁。
2.3 日志读操作(TransactionIdGetStatus)

功能:读取指定事务的CLOG日志,获取其最终状态,底层操作,不对外直接暴露。
核心流程

  1. 申请CLOG缓冲池共享锁(读操作共享);
  2. 调用SimpleLruReadPage加载目标日志页面至缓冲池(已加载则直接使用);
  3. 通过事务ID计算4元组,从缓冲池中读取对应位置的事务状态;
  4. 释放缓冲池锁,返回事务状态。
2.4 日志页面初始化(ZeroCLOGPage)

功能:将指定CLOG页面初始化为全0(默认状态IN_PROGRESS),可选记录XLOG日志。
核心流程

  1. 调用SimpleLruZeroPage在缓冲池中分配可用缓冲区,初始化为全0;
  2. writeXlog=true(如扩展新页面),调用WriteZeroPageXlogRec写入XLOG日志(记录新页面号);否则直接返回。
2.5 初始段创建(BootStrapCLOG)

功能:数据库系统初始化时创建第一个CLOG日志段,为事务系统提供基础存储。
核心流程

  1. 获取CLOG缓冲池排他锁;
  2. 分配缓冲区,调用ZeroCLOGPage初始化第0号页面为全0;
  3. 调用SimpleLruWritePage将初始化页面刷盘;
  4. 标记页面为“干净页”,释放缓冲池锁。
2.6 日志管理器启动(StartupCLOG)

功能:Postmaster/服务进程启动时,初始化CLOG日志管理器运行状态。
核心流程

  1. 从共享内存获取下一个可分配的事务ID,申请缓冲池排他锁;
  2. 根据该事务ID计算对应的CLOG页面,设为当前最大CLOG页面;
  3. 调用SimpleLruReadPage加载该页面至缓冲池,将页面中该事务偏移后的区域置为全0;
  4. 标记页面为脏页,释放缓冲池锁。
2.7 日志管理器关闭(ShutdownCLOG)

功能:Postmaster/服务进程关闭时,安全退出CLOG日志管理器。
核心逻辑:调用SimpleLruFlush将缓冲池中所有脏页刷盘,保证日志数据不丢失。

2.8 检查点操作(CheckPointCLOG)

功能:XLOG检查点时触发,保证CLOG日志持久化。
核心逻辑:调用SimpleLruFlush将缓冲池中所有脏页刷盘,完成CLOG日志的检查点持久化。

2.9 日志扩展(ExtendCLOG)

功能:为新事务ID分配CLOG日志空间(创建新页面)。
触发条件:新事务的CLOG日志位于页面首部时,需扩展新页面。
核心流程

  1. 根据事务ID计算目标页面号;
  2. 申请缓冲池排他锁;
  3. 调用ZeroCLOGPage初始化新页面(全0),并写入XLOG日志记录页面号;
  4. 释放缓冲池锁。
2.10 日志清理(TruncateCLOG)

功能:删除过时的CLOG日志段(基于检查点,释放磁盘空间)。
核心流程

  1. 根据指定事务ID计算参照页面号;
  2. 调用SlruScanDirectory扫描目录,识别过时的CLOG段;
  3. 执行检查点(保证数据已持久化);
  4. 调用SimpleLruTruncate删除过时的段文件。
2.11 XLOG日志记录(WriteZeroPageXlogRec)

功能:创建新CLOG页面时,写入XLOG日志记录页面号,用于系统崩溃恢复。
核心流程

  1. 构造XLOG日志记录,包含新CLOG页面号,标记资源管理器号为RM_CLOG_ID
  2. 调用XLogInsert将该记录插入XLOG日志。
2.12 REDO恢复(clog_redo)

功能:XLOG REDO阶段重建CLOG页面,恢复事务状态。
触发条件:XLOG REDO时遇到RM_CLOG_ID且类型为ZEROPAGE的日志记录。
核心流程

  1. 从XLOG记录中解析出CLOG页面号;
  2. 申请缓冲池排他锁,调用ZeroCLOGPage初始化该页面为全0;
  3. 调用SimpleLruWritePage将页面刷盘;
  4. 释放缓冲池锁,完成CLOG页面重建。

总结

  1. 核心定位:CLOG日志管理器通过SLRU缓冲池管理事务最终状态,每个事务状态占2bit,通过4元组精准定位日志记录,保证存储高效性;
  2. 核心约束:仅支持写入提交/中止/子事务提交三种最终状态,所有写操作默认同步刷盘,通过锁机制保证并发安全;
  3. 核心保障:通过XLOG记录CLOG页面创建操作,在系统崩溃时可通过clog_redo恢复CLOG日志,保证事务状态不丢失。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐