介绍数据库中的wal技术_PostgreSQL中的XLOG:一、基础概念和初始化

# 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)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值