pg_wal发展史

2021-03-24 10:28:28 浏览数 (2)

PostgreSQL WAL日志发展史

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国际社区文档中提供详细资料的第一个版本,那我们也从这里开始讲述PostgreSQLwal日志的故事,因为这里够完整也够简单,看下面的图片是不是简单多了。

这描述了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日志,这个策略也因此改变

可能文字描述后比较难懂,举个例子:小明(事务)放学(提交)要打车(刷写)回家,他发现有顺路的其他班级的同学(兄弟事务)没有放学,就想等几个人一起平摊打车的费用。发现在上课的同学太少了(commit_siblings),就想等这么长的时间省一点点钱不划算,就自己走了(刷写磁盘);发现还在上课的同学很多,就想等一段时间吧(commit_delay),等到这个时间长度带上下课的同学就走。

WAL_DEBUG

开启wal debug的参数,可以不用关注它。

PostgreSQL通常使用checkpoint进程来清理一些废弃的文件,对于wal文件来说,会在checkpoint时生成一些备用的wal文件或者清理旧的不使用的wal文件,且看相关配置[GUC2]。注意此处描述的为7.1版本的情况,一些参数已经不适用于最新的PostgreSQL版本。

WAL_FILES

在7.1版本的PostgreSQL中,wal_files参数如果大于0,在做检查点时会预先创建wal_files数量的wal段备用,wal_files参数等于0时,会一个一个的创建wal段。这个参数在7.3版本就删除了,换为另一种wal日志的预生成方法。

CHECKPOINT_TIMEOUT

PostgreSQL执行一次checkpoint的时间间隔,此处这个参数跟wal关系没有那么大,因为这个参数预下面的checkpoint_segment有相似的功用,他们都是触发checopint的时机,所以也写到了这里。

CHECKPOINT_SEGMENT

PostgreSQL执行一次checkpoint的wal段间隔,从上一次checkpoint开始,PostgreSQL在写了一定数量的wal段后,会再次触发checkpoint。

7.3改变wal预生成方式

此版本在7.1版本基础上做了微小的改变,主要是改变了预生成wal段的方法。

在checkpoint时不再预生成wal段,改为了新的策略维护wal生成目录中的wal段:

①wal段的数量原则上不超过2 * checkpoint_segments 1 

②一个流量高峰可能会导致backend极速生成wal日志,可能会将wal段数量增加到2 * checkpoint_segments 1以上。

③做检查点时,如果wal段数量超限,会将超限的wal段删除, 如果wal段数量不超限,会将旧的wal段改名为将来的wal段。

因此WAL_FILES参数不再使用,在这个版本中被移除了。当然这个wal段的管理方式在pg9.5又有所变化。

7.4增加便利性warning

此版本没有功能性的变化,只是增加了一个warning参数checkpoint_warning

CHECKPOINT_WARNING

如果两次因为CHECKPOINT_SEGMENT参数产生的checkpoint的时间间隔小于checkpoint_warning值,会报一个warning,提醒用户增加CHECKPOINT_SEGMENT的数值。

8.0实现PITR

WAL的版图终于有所扩张,实现了基于时间点的还原(Point-in-time recovery)简称PITR。PITR是PostgreSQL的物理备份机制,主要流程为:开启归档;制作基础备份;在备份数据库创建recovery.conf文件并写入恢复参数;启动备份数据库。这个博客是讲历史,不讲功能的使用,感兴趣这个博客的读者相信对PITR的使用一定也是了然于胸。

PITR的基本原理是,从基础备份的checkpoint点开始,对之后的wal日志进行重演,因此PostgreSQL需要保存基础备份之后所有的wal日志。如图中[GUC3]记录了wal日志归档相关的GUC参数。

ARCHIVE_COMMAND

这个参数为PostgreSQL提供一个wal日志归档的方法执行PITR时,需要为基础备份提供wal日志,可以指定恢复的目标停止位置,相关参数需要写入recovery.conf见[GUC4]

RESTORE_COMMAND

为备份数据库提供获取wal归档的命令

RECOVERY_TARGET_TIME

RECOVERY_TARGET_XID,RECOVERY_TARGET_INCLUSIVE,RECOVERY_TARGET_TIMELINE这是一些恢复目标相关的参数。

8.2全页写与温备

在操作系统发生崩溃时,可能会导致PostgreSQL数据页部分写的情况,因此会出现数据不一致的情况。为了应对这一情况,PostgreSQL在一个检查点之后,每次对一个数据页进行修改时,会在wal日志中备份这个数据页(全页写)。

另外本版本有了PostgreSQL温备的概念, 温备是基于wal段级别的wal传递,后面出现的热备是基于walrecord级别的wal传递。温备的备机周期性的通过restore_command获取wal段,并执行wal重演,使得备机在不停的追赶主机的数据。

这里可以理解一下温备和PITR,事实上如果让PITR在wal重演完所有的wal日志后停下来等待后续wal的出现,这就是温备。这个版本‘停下来等待后续wal的出现’这个操作不是在内核中实现的,而是需要你把这一步集成到 restore_command 中。

FULL_PAGE_WRITE

这个参数的意义是使得PostgreSQl可以对抗操作系统崩溃导致导致的部分写问题。

另一方面因为在wal日志中记录了大量的数据页信息,因此会使得wal日志发生膨胀。

另外在做基础备份时执行的checkpoint是不关心FULL_PAGE_WRITE而强制执行全页写的。

ARCHIVE_TIMEOUT

因为wal归档只会对已经写完的wal段生效,因此如果有一个wal段长时间没有写满,那么这个wal段就不会归档,也因此无法在温备功能中被备机使用。

arcihve_timeout定义了一个时间段,如果一个wal段超过这个时间段还没有写满,就强制发生一次wal切换归档。

8.3自动刷写wal缓存

8.3版本有一些小的改进和功能增强

SYNCHRONOUS_COMMIT

这个参数定义了,一个事务最终完成的条件。如果参数值为on,那么需要等到事务提交这一条wal日志同步到wal段中,事务才算完成提交。如果参数值为off,那么事务事务提交这一条wal日志刷写到wal缓存即可。在以后的版本中还会对这个参数进行升级使之对应热备的一些同步提交。

WAL_WRITE_DELAY

在说明COMMIT_DELAY 参数时讲到在没有事务提交的情况下,wal缓存只有等到写满才会刷写到磁盘。这里定义了一个时间间隔,在这个时间范围内如果没有刷写wal缓存,那么就触发一次wal缓存刷写。值得注意的是PostgreSQL通过walwrite进程来做这件事情。

ARCHIVE_MODE

这个参数增加是为了将归档模式和archive_command参数分离开。

9.0实现流复制

这里实现了流复制,为了流复制也增加了很多相应的GUC参数。与温备相对应,流复制也可以称为热备,他实现了主备之间以wal记录的粒度同步数据。

WAL_LEVEL

wal的级别当前版本支持minimal、aichive、hot_standby,三种级别。

MAX_WAL_SENDERS

每一个流复制连接,在主机都会有一个对应的wal发送进程,这个参数定义最大支持的连接数。

WAL_SENDER_DELAY

wal发送进程每隔一段时间,向备机发送一次主机产生的wal日志。这个参数配置了这个时间间隔的大小。

WAL_KEEP_SEGMENTS

设想一下,如果因为某种原因,主备之间的同步延迟比较大,就会造成主机的wal日志记录在发送到备机之前就已经被移除。这样备机就无法同步主机数据了。因此在主机上配置wal_keep_segments参数,就可以使主机生成的wal日志不会被立即清除,可以让备机有充足的时间完成数据同步。

VACUUM_DEFER_CLEAN_AGE

主机上执行的vacuum操作会打断备机正在执行的查询,设置这个参数后主机对死亡元组的vacuum可以延迟几个事务的时间。这个参数在后面会有更为有效的hot_standby_feedback参数作为替代。

HOT_STANDBY

配置是否可以连接到这个备机进行查询操作。

MAX_STANDBY_ARCHIVE_DELAY&&MAX_STANDBY_STREAM_DELAY

当一个wal redo的操作与当前正在执行的查询冲突时,需要判断等待查询完成再redo,还是取消查询执行redo。

这个参数设定了一个允许查询继续执行的时间,经过这个时间之后,就会取消查询执行redo。

MAX_STANDBY_ARCHIVE_DELAY用于文件级别的wal传递,也就是温备的情况

MAX_STANDBY_STREAM_DELAY用于流复制,或者说热备。

ARCHIVE_CLEANUP_COMMAND

配置一个命令在遇到一个重启点时,清理归档路径中的wal段

RECOVERY_END_COMMAND

配置一个命令在完成恢复后,做一些清理工作

STANDBY_MODE

界定这是一次PITR还是一个备机,这个参数在PG12中删除了。

PRIMARY_CONNINFO

备机启动时连接主机的字符串

TRIGGER_FILE

指定一个触发文件,当文件存在时,备机退出recovery模式

9.1备机同步提交

SYNCHRONOUS_COMMIT

这不是一个新的参数,而是为这个参数增加了一个可选值‘local’

ON:wal record在本地和备机中刷写到磁盘之后,事务才完成提交状态。

LOCAL:wal record在本地刷写到磁盘之后,事务才完成提交状态。

OFF:异步提交

SYNCHRONOUS_STANDBY_NAMES

指定primary_conninfo字符串中可以指定备机的名字,通过将名字添加到这个参数中,可以指定一个同步备机。

当前版本只支持一个同步备机。

REPLICATION_TIMEOUT

这里界定一个时间间隔,如果超过这个时间没有收到备机的消息,那么主机将断开与备机的连接。

WAL_RECEIVER_STATUS_INTERVAL

备机每隔一段时间会向主机报告复制情况,也是向主机报告备机存活的方法。

HOT_STANDBY_FEEDBACK

备机向主机发送备机正在执行的查询,目的是通知主机不要清理相关的死亡元组。

RECOVERY_TARGET_NAME

为PITR指定一个恢复点的名字,恢复点可以用 pg_create_restore_point()创建

9.2级联复制

9.2版本的特点是出现了级联复制,一个备机可以从他的上游服务器获取wal日志,也可以向下游服务器传递wal日志。不过没有为级联复制准备特殊的GUC参数,我们只要把上游服务器配置为一个备机就可以了。

PAUSE_AT_RECOVERY_TARGET

在PITR时如果指定了恢复目标,那么当达到恢复目标时,startup进程停止redo,这时的数据库还是在恢复状态,你可以连接数据库查看当前数据库状态是否满足你的预期,如果不满足这提供了一次让你重新配置recovery.conf继续恢复的机会。当pause_at_recovery_target参数配置为false时,redo过程在达到恢复目标会立即会进入一个可读可写的状态。

SYNCHRONOUS_COMMIT

这个参数又来了,这次它提供了一个remote_write的可选项,remote_write比ON严谨一些,当walrecord在同步备机apply之后,事务才会被认为是完成了。

9.3小改动

删除了replication_timeout参数,增加了wal_sender_timeout 替代参数,并增加了wal_receiver_timeout 参数。

WAL_SENDER_TIMEOUT

代替replication_timeout

WAL_RECEIVER_TIMEOUT

这个参数用来探测主机的状态,如果主机长时间没有回复则断开连接。

9.4复制槽与Logical

实现了复制槽,使得发送服务器刚好能保存适当的wal文件。

出现了logical的wal日志级别,内置了wal日志的解析插件test_decoding和工具pg_recvlogical。

WAL_LEVEL

增加logical级别

WAL_LOG_HINTS

一些page页如果发生了不重要的页数据改动,也遵循全页写机制。

MAX_REPLICATION_SLOTS

9.4版本新增加了复制槽功能,每一个流复制备机可以配置使用一个复制槽,这个复制槽记录了对应备机的wal记录的apply情况,并在主机为备机保存还要使用的wal段不被清理。

PRIMARY_SLOT_NAME

在备机配置其要使用的复制槽,并为复制槽命名。

RECOVERY_MIN_APPLY_DELAY

备机收到wal日志后会延迟一段时间才完成wal记录的redo操作。同步复制不受此配置的影响。

考虑到如果你在主机执行了一个没有条件筛选的delete操作,幡然醒悟。这时delete操作还没有同步到备机,这时可以赶紧采取紧急措施补救数据。

9.5许多小改动

WAL_COMPRESSION

是否压缩wal日志中的全页写数据

MAX_WAL_SIZE && MIN_WAL_SIZE

软限制wal日志在wal生成目录的size,当wal目录下wal段的size小于min_wal_size时,checkpoint进程不会处理wal段;当wal目录下wal段的size大于max_wal_size时,checkpoint会移除最旧的wal段直到wal段size为max_wal_size; 剩余的情况,checkpoint会将不在使用的wal段重命名为未来的的wal段名。

ARCHIVE_MODE

增加always选项,一个备机是否继续归档它接受到的wal日志

RECOVERY_TARGET_ACTION

在完成恢复后,数据库的动作,有三个可选项:pause,promote, shutdown.

这个参数替代了pause_at_recovery_target参数。

pause指停止redo,但此时数据库还是处于恢复模式,这个参数可以让你查看数据库当前值得状态,若当前状态不符合你的预期,可以停库修改恢复recovery.conf,启动数据库后继续redo,使用pg_xlog_replay_resume() 命令可以使数据库退出恢复模式。

promote:恢复完成后,直接将数据库提升为主机状态。

shutdown:这相当于在pause的基础上,多加了个停库的操作。

TRACK_COMMIT_TIMESTAMP

为提交的事务记录提交时间。

WAL_RETRIEVE_RETRY_INTERVAL

这是一个等待时间,当备机已经重演完所有的wal日志时,walreciver进程探测是否有新的wal日志的时间间隔。

9.6多同步备机

9.6版本最大的亮点是支持多个同步备机,另外修整了wal级别为(minimal,replica,logical)

WAL_LEVEL

wal级别支持minimal,replica,logical三种。

SYNCHRONOUS_COMMIT

添加remote_apply选项,walrecord在synchronous_standby_names指定的同步备机上完成redo后,主机事务才算完成。

WAL_WRITE_FLUSH_AFTER

在事务异步提交时,会通知wal writer进程将wal日志刷盘,但这个刷盘只是指刷入操作系统缓存。

当刷入操作系统缓存,但是没有完成硬盘同步的wal日志的数量大于wal_write_flush_after值时,会触发一次将wal日志同步到硬盘的过程。

SYNCHRONOUS_STANDBY_NAMES

这个参数增加了值的类型,用以支持多同步备机

10.0逻辑复制

10.0版本趋向于将用户可见的xlog改为wal,最直观的就是将wal的生成目录名从pg_xlog改为pg_wal,另外有很多wal相关的函数也由xlog字样改为wal字样。在9.4版本就实现了logical级别的wal日志,从10.0开始PostgreSQL内核正式加入了对logical wal的内置使用。

RECOVERY_TARGET_LSN

为PITR指定一个停止redo的lsn点。

MAX_LOGICAL_REPLICATION_WORKES

最大的逻辑复制进程数

MAX_SYNC_WORKERS_PER_SUBSCRIPTION

在创建订阅时,订阅者可以选择将发布端所有表中的数据全部拷贝过来,

这个参数指在拷贝过程中允许并行拷贝的最大进程数。

12.0流复制配置微调

流复制、PITR、或者温备功能中,不再使用recovery.conf配置文件,所有的相关参数转移至Postgresql.conf配置文件。使用一个空的recovery.signal或者standby.signal文件来标志这是一个恢复过程。

WAL_INIT_ZERO

创建wal段时,将wal段数据全部初始化为0,以保证wal段申请了空间。

WAL_RECYCLE

是否重用旧的wal段。

PROMOTE_TRIGGER_FILE

替换trigger_file参数

总结

本文展现了PostgreSQL的wal日志的成长过程,一些功能慢慢的添加到了PostgreSQL中,GUC参数也越来越多,当然也有一些参数被弃用。本来想详细介绍每一个参数的意义和出现原因,但这会是一个更文庞大的文章。后续会间接性的写一些短文来具体介绍每一个参数的意义、由来和实现

0 人点赞