WAL日志是Postgres何其重要的一个部分,它活跃在Postgres的各个功能模块,绝大多数的数据库行为都会被记录在WAL日志中。正因为如此我们可以把WAL日志看作是数据库变更的履历,也因为这一特性,WAL日志在数据库恢复、高可用、流复制、逻辑复制等Postgres模块中扮演着极其重要的角色。如下一张图片描述了WAL日志从产生到使用过程中涉及的各种各样的wal相关的配置参数(基于PG12)。 弄清楚每一个参数的意义对我们优化数据库性能,配置高可用集群等有举足轻重的作用。我们可以从PostgreSQL文档中找到每一个配置参数的定义,但是我们从简单的几行描述很能很难理解参数的内在意义,或者不知道为什么要有这个参数的存在,更有甚者你在根据别人的博客配置数据库时,发现你的数据库版本不认识博客里面的配置参数。这篇博客将从PostgreSQL 7.1版本最原始的WAL日志开始理解wal日志,迭代wal日志的发展过程。
PostgreSQL山海经
1986年伯克利大学开始研究的POSTGRES项目,1987年完成第一个版本的开发,历经三个版本后,在1994年改名为Postgres95, 然后在1996年正式更名为PostgreSQL。所有的上面这些我称之为PostgreSQL史前文明,有兴趣可以去探寻‘PostgreSQL山海经’,发掘PostgreSQL历史起源。
7.1开天辟地
7.1是PostgreSQL国际社区文档中提供详细资料的第一个版本,那我们也从这里开始讲述PostgreSQL wal日志的故事,因为这里够完整也够简单,看下面的图片是不是简单多了。
这描述了wal日志的本职工作,PostgreSQL在执行数据修改操作时,修改的数据在写入磁盘之前首先要将修改的内容写入wal日志文件,这样我们就可以不必时时的将共享缓存中的数据文件刷新到磁盘中,因为如果数据库发生崩溃我们可以从wal日志获取共享缓存中未写入到磁盘的数据。这样做的目的是以顺序写入的wal代替随机的数据写入,以获取更高的执行效率。
‘首先要将修改的内容写入wal日志文件’ 在通常的对wal日志的介绍中都会有类似的文字,通常人们对这行文字一笔带过,但就这么一个小小的文字内涵巨大玄机。这个玄机就是PostgreSQL使用了一堆GUC参数来让你控制wal日志写入到磁盘的过程,且看上图中的[GUC1]。
WAL_BUFFERS
PostgreSQL每一个backend进程,都会产生wal日志,wal日志一定不是直接写到wal文件中的,他也会经过PostgreSQL为wal设计的一层缓存,这个参数就是定义wal缓存注的个数,PostgreSQL设定了一些策略来刷写wal的缓存,下面的内容会有介绍。
注:每个wal的缓存大小为XLOG_BLCKSZ,这不是一个GUC参数,一般这个值是数据库编译时通过configure命令指定的,在pg11(包含)之后,可以通过initdb命令指定这个值的大小。
FSYNC
PostgreSQL的数据缓存刷写到持久化存储之前要经过操作系统缓存,PostgreSQL默认将数据刷写入操作系统缓存之后就完成了整个写入过程,但是如果操作系统完成操作系统缓存刷写之前就崩溃了,那么这时会有数据丢失的风险。因此FSYNC参数提供了一种方法,使得数据最终写入持久化存储后,才能算完成一个写入过程。但是这会导致数据库执行效率下降。这个参数不仅对wal的刷写生效,对其他数据文件同样生效。
WAL_SYNC_METHOD
这个参数是对fsync参数的补全,它指定了保证数据写入持久化存储的方法。
COMMIT_DELAY && COMMIT_SIBLINGS
前面提到刷写wal缓存的策略注,在这里描述一下①在wal缓存写满时,会触发刷写wal缓存。②在事务提交时会触发刷写wal缓存。在事务提交刷写缓存时,PostgreSQL又做了一点小动作,来提升性能:commit_delay指事务提交之后允许wal缓存延迟刷写的时间,这个延迟的目的是想等一下并行执行的兄弟事务,等兄弟事务完成提交后,一起将wal日志刷写入磁盘,如果兄弟事务超过这个commit_delay时间还未提交,那么当前进程就完成wal刷写。commit_siblings意义是当前事务做这个等待的条件,如果并行执行的兄弟事务小于commit_siblings,那当前事务就不做等待了。
注:在8.3版本增加了walwriter进程可以定时刷写wal日志,这个策略也因此改变
可能文字描述后比较难懂,举个例子