网上关于clog的介绍比较多,但相对零散,而搜索总是找到些大同小异的知识点,标题党居多。所以希望能整合下相关内容。
定义
clog记录的是事务的最终状态,需要对比的是xlog,xlog除了记录事务最终状态之外,还记录着事务的执行过程。
PostgreSQL中的事务共有四种状态:
#define TRANSACTION_STATUS_IN_PROGRESS 0x00 //事务正在执行
#define TRANSACTION_STATUS_COMMITTED 0x01 //事务已提交
#define TRANSACTION_STATUS_ABORTED 0x02 //事务被中止
#define TRANSACTION_STATUS_SUB_COMMITTED 0x03 //子事务已提交
因此,一个事务的状态可以用2bit来表示,如01。所以一个字节可以存放4个事务的状态,而PG系统定义每个page大小为8K(定义于/src/include/pg_config.h: #define BLCKSZ 8192),故而一个page中可以存放个事务的状态。
- 在物理磁盘上,clog存储是以segment为文件单位,一个segment为一个单独的clog文件,而每个segment中有
SLRU_PAGES_PER_SEGMENT = 32个page - 在共享内存中,clog被放在SLRU缓存池,每份大小为一个page,总个数如下。其中
NBuffers默认值为4096,因此下面这个函数的返回值就是8
NBuffers也可以作为postgres参数自由设置,但通常并无调优的必要,具体可参见Postgresql官方文档
Size CLOGShmemBuffers(void){
return Min(32, Max(4, NBuffers / 512));
}

因此,一个事务在clog中的状态可以根据4元素来查找到,即<Segment偏移,Segment中page偏移,page中字节偏移,字节内位偏移>。其中,Segment偏移是当磁盘中存在多个Segment文件时有效。
#功能
使用
为提高处理速度,新增事务的状态都是先储存在SLRU缓存池中,只有当一个page全部写满之后才会一次性落盘,当然,此时你也可以通过sql命令checkpoint立即落盘。
而想要从clog中读取某个事务的状态时,也是先从缓存池中读取,如果缓存池中没有,那么会从segment中拿到对应的page,然后把整个page的数据加载到缓存中,再进行读取。
题外话,clog的相关流程都清晰简单,想要了解pg系统中的log类,可以考虑从clog开始
初始化与文件增长
在pg系统中,数据库经initdb安装时会初始化一个page大小的clog文件。此后,一旦事务id超过32678的倍数时,将触发自动落盘(或者在此之前手动触发落盘),该文件大小将增加8K。直到满足一个segment大小后,系统将重新生成一个segment文件
在新的clog缓存生成时,所有数据的初始值都是0,如果让clog这时候落盘,会看到对应的二进制文件中全部是0
举例验证
下面在pg系统中对clog的状态及文件系统进行验证。在此之前,先说明下Linux下查看二进制文件的方法:
- 对clog的二进制文件,直接用vim打开:
vim 0000 - 这时候看到的是一堆乱码,然后在文件中按
:输入命令,合起来为:%!xxd -b,然后回车,这时候就能看到正确的二进制比特数据
注意:该二进制文件中记录的数据也有大小端区分,因为我这台机器的CPU是Intel的,经确认,属于小端模式
此处不再赘述如何安装xxd。而且如果不采用xxd的话,也可以在windows系统中,采用其它读取二进制文件的方式,如notepad++插件
步骤如下:
- 先查看下当前的事务号,然后利用checkpoint将缓存中的数据写入到磁盘。注意,虽然申请事务号看似什么都没做,但是在pg中,这也代表事务被正确提交了,即占用了一个事务号
postgres=# select txid_current();
txid_current
--------------
1319
(1 row)
postgres=# checkpoint;
CHECKPOINT
Xum:/home/postgres/install/data/pg_clog # ll
total 8
-rw------- 1 postgres postgres 8192 Aug 7 08:51 0000
- 打开clog文件,输入
:set nu命令打开行号,查看当前的最新的一个非00值,记下相应位置,这就是当前最新的一个事务状态了,记录下地址位是000014c,所在行是56,最新的状态为01,即事务已提交
54 000013e: 01010101 01010101 01010101 01010101 01010101 01010101 UUUUUU
55 0000144: 01010101 01010101 01010101 01100101 10100110 01100101 UUUe.e
56 000014a: 01100101 10011001 00000001 00000000 00000000 00000000 e.....
- 在PostgreSQL中开启事务,然后提交或者回滚,这里我连续实验了多个事务:一个回滚,一个只是占用了事务号,另一个正确提交。因此,这三个事务的状态分别是
10,01,01
test=# begin;
BEGIN
test=# select txid_current();
txid_current
--------------
1329
(1 row)
test=# insert into t values(3,'dahuang');
INSERT 0 1
test=# rollback;
ROLLBACK
test=# select txid_current();
txid_current
--------------
1330
(1 row)
test=# begin;
BEGIN
test=# insert into t values(3,'dahuang');
INSERT 0 1
test=# commit;
COMMIT
- 然后用checkpoint将缓存中的clog数据落盘,再次查看clog文件的更新时间,可以发现此时的时间被刷新了
Xum:/home/postgres/install/data/pg_clog # ll
total 8
-rw------- 1 postgres postgres 8192 Aug 7 08:56 0000
- 重新打开clog文件,直接用
:56定位到之前的行。按照之前的分析,以及Intel X86 CPU中大小端的影响,刚好000014c这个地址下还有6bit可以用于存放,所以上述三个事务在二进制文件中存放的就是010110
54 000013e: 01010101 01010101 01010101 01010101 01010101 01010101 UUUUUU
55 0000144: 01010101 01010101 01010101 01100101 10100110 01100101 UUUe.e
56 000014a: 01100101 10011001 01011001 00000000 00000000 00000000 e.Y...