# PostgreSQL中的XLOG(一)基础概念和初始化
#### 作者:桑栎
#### 发布:2018-04-28
#### 欢迎大家踊跃投稿,投稿信箱: press@postgres.cn
----
## 写在前面
打算花点时间好好的研究一下WAL(Write-Ahead Logging),在关系型数据库中WAL是必不可少也绕不过去的知识,数据库依旧是PostgreSQL,内容可能会划分为:
1. 基础概念和初始化
2. CKPT & Control File
3. WAL结构和如何写XLOG(Full Page Write)
4. 数据库是如何使用XLOG进行恢复
5. 流复制中XLOG是如何操作的
数据库版本为PG 10.3:https://ftp.postgresql.org/pub/source/v10.3/postgresql-10.3.tar.bz2
## 什么是WAL?
顾名思义,就是写在前面的日志,是事物和数据库故障的一个保护。任何试图修改数据库数据的操作都会写一份日志到磁盘。这个日志在PG中叫XLOG,所有的日志都会写在$PGDATA/pg_wal目录下面。
官方说明:https://www.postgresql.org/docs/current/static/wal-intro.html 我就不啰嗦了,哈哈。
每个XLOG的Page和Seg的默认大小:source:src/include/pg_config.h
![](/images/news/2018/20180429_2240_001.jpg)
```c
XLOG_BLCKSZ 8K
XLOG_SEG_SIZE 16M
```
只允许在编译的时候进行修改:
![](/images/news/2018/20180429_2240_002.jpg)
例如:./configure --with-wal-segsize=64就是修改segsize为64M。
# 一、数据库初始化
初始化数据库执行initdb -D $PGDATA后,会初始化一个数据库,在$PGDATA/pg_wal目录下默认会生成一个xlog文件
![](/images/news/2018/20180429_2240_003.jpg)
该文件是通过管道(popen)调用postgres这个命令生成的:/usr/local/pg_10_3/bin/postgres --boot -x1 -F
![](/images/news/2018/20180429_2240_004.jpg)
这个命令会初始化很多东西,如下图:
![](/images/news/2018/20180429_2240_005.jpg)
我们先看看XLOG,初始化XLOG的函数入口为BootStrapXLOG
```
source:src/backend/access/transam/xlog.c+4959
BootStrapXLOG(void)
```
![](/images/news/2018/20180429_2240_006.jpg)
这个函数只会在初始化的时候调用一次,用来创建控制文件和初始化XLOG segment。
我们先看看第一个XLOG文件名称生成:
![](/images/news/2018/20180429_2240_007.jpg)
XLOGDIR:
![](/images/news/2018/20180429_2240_008.jpg)
XLogSegmentsPerXLogId:
![](/images/news/2018/20180429_2240_009.jpg)
```
XLogSegmentsPerXLogId = (0x100000000UL)/(1024*1024*16) = 256`
```
所以最大的XLOG文件名称为:00000001FFFFFFFF000000FF,而不是理论上的00000001FFFFFFFFFFFFFFFF,因为uint64 % 256 最大是FF。
初始化的第一个timelineID = 1:
![](/images/news/2018/20180429_2240_010.jpg)
该值是标识数据库状态用的。PG的解释如下:
![](/images/news/2018/20180429_2240_011.jpg)
创建的第一个XLOG segment文件 = 1:
![](/images/news/2018/20180429_2240_012.jpg)
所以宏XLogFilePath的结果为:
![](/images/news/2018/20180429_2240_013.jpg)
```
path = pg_wal/000000010000000000000001
```
在创建这个文件之前,PG会创建个临时文件:
![](/images/news/2018/20180429_2240_014.jpg)
pg_wal/xlogtemp.29191("xlogtemp."加个当前进程PID),避免其他process来搞事情,然后循环写入每次写入XLOG_BLOCKSZ个字节
![](/images/news/2018/20180429_2240_015.jpg)
写完之后,文件为16M:
![](/images/news/2018/20180429_2240_016.jpg)
这个文件内容其实什么都没是个空文件。然后通过函数durable_link_or_rename进行rename。
![](/images/news/2018/20180429_2240_017.jpg)
到这里,XLOG就创建完成了,不过这个文件还是空空如也,首先写入的是XLOG的头(XLogPageHeader)。
![](/images/news/2018/20180429_2240_018.jpg)
头长度为24字节,内容如上所示。
XLogPageHeaderData结构体:
![](/images/news/2018/20180429_2240_019.jpg)
每个XLogSegment的第一个page头会写入XLogLongPageHeaderData,结构体如下:
![](/images/news/2018/20180429_2240_020.jpg)
XLogLongPageHeaderData的std还是XLogPageHeaderData,因为这是初始化第一个XlogSegment所以需要写入LongHeader。
xlp_sysid是通过当前时间的sec,usec和PID生成的,生成策略如下:
![](/images/news/2018/20180429_2240_021.jpg)
xlp_seg_size是当前xlog_segment大小=1024*1024*16;xlp_xlog_blcksz是当前xlog的page大小=8192。结果如下:
![](/images/news/2018/20180429_2240_022.jpg)
写入一个page:
![](/images/news/2018/20180429_2240_023.jpg)
这个page的前40个字节就是XLogLongPageHeaderData。写入完之后整个的XLOG_SEGMENT的结构如下图所示:
![](/images/news/2018/20180429_2240_024.jpg)
到这里XLOG就在初始化的时候创建完成了。后面是创建pg_control文件,这个先不写了,等后面写CKPT的时候一起说说。
# 二、数据库启动初始化
前面的是数据库初始化的一个过程,现在是看看数据库启动的时候是如何进行初始化XLOG的呢?
在数据库启动的时候,会从系统申请一份WAL的共享内存。入口函数是XLOGShmemInit(void)(ControlFile也在这个里面初始化共享内存)。
如果数据库不修改任何参宿,默认会初始化4209288字节的内存空间。通过函数XLOGShmemSize(void)计算出来的。内存大小跟GUC参数wal_buffers强相关,因为默认为-1所以PG会用XLOGbuffers = NBuffers / 32 = 512。
source:src/backend/access/transam/xlog.c+4824
![](/images/news/2018/20180429_2240_025.jpg)
分配的共享内存分布如下图所示:
![](/images/news/2018/20180429_2240_026.jpg)
1. XLogCtlData:1544字节
2. XLogRecPtr[]:4096字节(8*512(XLOGBuffers)) = 4096
3. WALInsertLockPadded:1152字节(128*9)
4. XLOG_BLCKSZ:8192字节
5. XLOGbuffers:4194304字节(8192*512(XLOG_BLCKSZ,*XLOGbuffers))
初始化头结构体:
* XLogCtl->xlblocks = 上图中的XLogRecPtr。
* WALInsertLocks = XLogCtl->Insert.WALInsertLocks = 上图中的WALInsertLockPadded。
* XLogCtl->pages = 上图中的XLOGbuffers,这个buffer就是后面存储wal的地方,所有的xlog都会先写入到这个内存中,然后再刷到磁盘中。
**NOTE**:因为XLogCtl结构体很大,大家可以对照的源码看看。共享内存的指针都挂载这个头结构体中。
当数据库启动的时候,共享内存初始化完成后,通过函数StartupXLOG来加载XLOG,通过读取控制文件,然后获取对应的xlog page。
数据都会存储在XLogCtl->pages中,这块感兴趣同学可以看一下:
![](/images/news/2018/20180429_2240_027.jpg)
在这个里面会加载不少文件:
![](/images/news/2018/20180429_2240_028.jpg)
有兴趣的同学gdb跟跟看看,还是蛮有趣的,如果感觉有趣可以关注公众号哦。
先写这么多吧,祝五一劳动节HAPPY以上。
![CENTER_PostgreSQL_Community](/images/news/2016/pg_bot_banner.jpg)