CLOG日志管理器
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缓冲池,为后续操作提供基础环境。
核心流程:
- 设置CLOG共享内存控制信息(如日志页面前后关系对比函数);
- 调用
SimpleLruInit初始化SLRU缓冲池,指定CLOG日志磁盘存储路径; - 在共享内存中注册唯一的CLOG缓冲池,初始化
ClogCtl控制结构; - 分配缓冲池控制锁,用于后续并发控制。
2.2 日志写操作(TransactionIdSetStatus)
功能:设置指定事务的最终状态(写入CLOG日志),底层操作,不对外直接暴露。
前置条件:仅允许设置COMMITTED/ABORTED/SUB_COMMITTED三种状态(IN_PROGRESS为初始值,非最终状态,无需设置)。
核心流程:
- 校验状态合法性,申请CLOG缓冲池排他锁(修改操作需独占);
- 调用
SimpleLruReadPage将目标日志页面加载至缓冲池(已加载则直接使用); - 通过事务ID计算4元组,修改缓冲池中对应位置的事务状态;
- 标记该页面为脏页(需后续刷盘),释放缓冲池锁。
2.3 日志读操作(TransactionIdGetStatus)
功能:读取指定事务的CLOG日志,获取其最终状态,底层操作,不对外直接暴露。
核心流程:
- 申请CLOG缓冲池共享锁(读操作共享);
- 调用
SimpleLruReadPage加载目标日志页面至缓冲池(已加载则直接使用); - 通过事务ID计算4元组,从缓冲池中读取对应位置的事务状态;
- 释放缓冲池锁,返回事务状态。
2.4 日志页面初始化(ZeroCLOGPage)
功能:将指定CLOG页面初始化为全0(默认状态IN_PROGRESS),可选记录XLOG日志。
核心流程:
- 调用
SimpleLruZeroPage在缓冲池中分配可用缓冲区,初始化为全0; - 若
writeXlog=true(如扩展新页面),调用WriteZeroPageXlogRec写入XLOG日志(记录新页面号);否则直接返回。
2.5 初始段创建(BootStrapCLOG)
功能:数据库系统初始化时创建第一个CLOG日志段,为事务系统提供基础存储。
核心流程:
- 获取CLOG缓冲池排他锁;
- 分配缓冲区,调用
ZeroCLOGPage初始化第0号页面为全0; - 调用
SimpleLruWritePage将初始化页面刷盘; - 标记页面为“干净页”,释放缓冲池锁。
2.6 日志管理器启动(StartupCLOG)
功能:Postmaster/服务进程启动时,初始化CLOG日志管理器运行状态。
核心流程:
- 从共享内存获取下一个可分配的事务ID,申请缓冲池排他锁;
- 根据该事务ID计算对应的CLOG页面,设为当前最大CLOG页面;
- 调用
SimpleLruReadPage加载该页面至缓冲池,将页面中该事务偏移后的区域置为全0; - 标记页面为脏页,释放缓冲池锁。
2.7 日志管理器关闭(ShutdownCLOG)
功能:Postmaster/服务进程关闭时,安全退出CLOG日志管理器。
核心逻辑:调用SimpleLruFlush将缓冲池中所有脏页刷盘,保证日志数据不丢失。
2.8 检查点操作(CheckPointCLOG)
功能:XLOG检查点时触发,保证CLOG日志持久化。
核心逻辑:调用SimpleLruFlush将缓冲池中所有脏页刷盘,完成CLOG日志的检查点持久化。
2.9 日志扩展(ExtendCLOG)
功能:为新事务ID分配CLOG日志空间(创建新页面)。
触发条件:新事务的CLOG日志位于页面首部时,需扩展新页面。
核心流程:
- 根据事务ID计算目标页面号;
- 申请缓冲池排他锁;
- 调用
ZeroCLOGPage初始化新页面(全0),并写入XLOG日志记录页面号; - 释放缓冲池锁。
2.10 日志清理(TruncateCLOG)
功能:删除过时的CLOG日志段(基于检查点,释放磁盘空间)。
核心流程:
- 根据指定事务ID计算参照页面号;
- 调用
SlruScanDirectory扫描目录,识别过时的CLOG段; - 执行检查点(保证数据已持久化);
- 调用
SimpleLruTruncate删除过时的段文件。
2.11 XLOG日志记录(WriteZeroPageXlogRec)
功能:创建新CLOG页面时,写入XLOG日志记录页面号,用于系统崩溃恢复。
核心流程:
- 构造XLOG日志记录,包含新CLOG页面号,标记资源管理器号为
RM_CLOG_ID; - 调用
XLogInsert将该记录插入XLOG日志。
2.12 REDO恢复(clog_redo)
功能:XLOG REDO阶段重建CLOG页面,恢复事务状态。
触发条件:XLOG REDO时遇到RM_CLOG_ID且类型为ZEROPAGE的日志记录。
核心流程:
- 从XLOG记录中解析出CLOG页面号;
- 申请缓冲池排他锁,调用
ZeroCLOGPage初始化该页面为全0; - 调用
SimpleLruWritePage将页面刷盘; - 释放缓冲池锁,完成CLOG页面重建。
总结
- 核心定位:CLOG日志管理器通过SLRU缓冲池管理事务最终状态,每个事务状态占2bit,通过4元组精准定位日志记录,保证存储高效性;
- 核心约束:仅支持写入
提交/中止/子事务提交三种最终状态,所有写操作默认同步刷盘,通过锁机制保证并发安全; - 核心保障:通过XLOG记录CLOG页面创建操作,在系统崩溃时可通过
clog_redo恢复CLOG日志,保证事务状态不丢失。
更多推荐
所有评论(0)