PG高可用之WAL

1.WAL简介

​ WAL 全称 write ahead log,是PostgreSQL中的在线redo日志。一般的数据库系统为保障数据的安全性,数据文件的改变必须先写入日志,也就是说日志记录刷新到永久存储上后,才能被提交。这样,在数据库宕机时,没有“落盘” 的数据,可以根据 WAL 的记录进行重做。根用WAL日志还能提高事物的效率,因为避免了每次提交时的随机读写,而只是顺序的刷新WAL记录到WAL段。

1.1.WAL的工作原理

  • 事物开始后,提交前:先将变更记录到WAL缓冲,然后再将更新后的数据写入到数据页缓冲
  • 提交时:WAL缓冲立即刷新到WAL段,后续根据不同场景连续地将数据页缓冲写入到数据文件
  • checkpoint时:将所有的数据页缓冲写到数据文件。

2.WAL segment

存储在$PGDATA/pg_wal目录(9.6版本之前在pg_xlog),信息如下:

[root@test pg_wal]# pwd
/data/pgdata/pg_wal
[root@test pg_wal]# ll
total 16384
-rw------- 1 pg14 pg14 16777216 Mar 15 13:07 000000010000000000000001
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status
[root@test pg_wal]# 

​ 参数wal_segment_size控制每个wal文件的大小,默认是16M,max_wal_size控制所有wal文件大小的总和,默认为1G,超过这个大小就会覆盖(重用最早的WAL segment),以默认值为例,允许最多有64个wal segment,超过64个就会重用之前的wal segment。在11版本之前wal_segment_size只能在编译时指定,指定后不能变更,在11版本之后,wal_segment_size在初始化时(initdb)指定,并且只能使用pg_resetwal插件变更。

2.1 wal segment 命名格式

由24位十六进制数组成:前8位 代表 timeline,中间8位代表logid,最后8位代表segment id。

2.2 wal segment 内部结构

一个16M的segment由 2048个8k的block(块,也叫做页)组成。

每个个block包含一个blcok header(块头)和若干个 wal record,wal record就存放数据变更记录。

第一个block header由xLogLongPageHeaderData定义数据结构,其他block header由xLogPageHeaderData定义数据结构,这两个都在/soft/postgresql-14.6/src/include/access/xlog_internal.h中。

wal recored 由 header和data两部分,header由xLogRecored定义数据结构,data可能包含多种数据结构,xLogRecored在/soft/postgresql-14.6/src/include/access/xlog_internal.h中(9.5版本前在xlog.h)。

3 WAL相关参数

postgres=# select name,setting,context from pg_settings where name like '%wal%';
             name              |  setting  |  context   
-------------------------------+-----------+------------
 max_slot_wal_keep_size        | -1        | sighup
 max_wal_senders               | 10        | postmaster
 max_wal_size                  | 1024      | sighup
 min_wal_size                  | 80        | sighup
 track_wal_io_timing           | off       | superuser
 wal_block_size                | 8192      | internal
 wal_buffers                   | 256       | postmaster
 wal_compression               | off       | superuser
 wal_consistency_checking      |           | superuser
 wal_init_zero                 | on        | superuser
 wal_keep_size                 | 0         | sighup
 wal_level                     | replica   | postmaster
 wal_log_hints                 | off       | postmaster
 wal_receiver_create_temp_slot | off       | sighup
 wal_receiver_status_interval  | 10        | sighup
 wal_receiver_timeout          | 60000     | sighup
 wal_recycle                   | on        | superuser
 wal_retrieve_retry_interval   | 5000      | sighup
 wal_segment_size              | 16777216  | internal
 wal_sender_timeout            | 60000     | user
 wal_skip_threshold            | 2048      | user
 wal_sync_method               | fdatasync | sighup
 wal_writer_delay              | 200       | sighup
 wal_writer_flush_after        | 128       | sighup
(24 rows)

​ 常用的有max_wal_size 、wal_level 、wal_buffers、wal_segment_size。当不是PostgreSQL 流复制时还需要配置max_wal_senders、wal_keep_size、wal_sender_timeout。

  • max_wal_size:在自动 WAL 检查点之间允许 WAL 增长到的最大尺寸。这是一个软限制,在特殊的情况下 WAL 尺寸可能会超过max_wal_size, 例如在重度负荷下、archive_command失败或者高的 wal_keep_size设置。 如果指定值时没有单位,则以兆字节为单位。默认为 1 GB。增加这个参数可能导致崩溃恢复所需的时间。 这个参数只能在postgresql.conf 或者服务器命令行中设置。
  • min_wal_size:只要 WAL 磁盘用量保持在这个设置之下,在检查点时旧的 WAL 文件总是 被回收以便未来使用,而不是直接被删除。这可以被用来确保有足够的 WAL 空间被保留来应付 WAL 使用的高峰,例如运行大型的批处理任务。 如果指定值时没有单位,则以兆字节为单位。默认是 80 MB。这个参数只能在postgresql.conf 或者服务器命令行中设置。
  • wal_level:wal_level决定多少信息写入到 WAL 中。默认值是replica,它会写入足够的数据以支持WAL归档和复制,包括在后备服务器上运行只读查询。minimal会去掉除从崩溃或者立即关机中进行恢复所需的信息之外的所有记录。最后,logical会增加支持逻辑解码所需的信息。每个层次包括所有更低层次记录的信息。这个参数只能在服务器启动时设置。要启用WAL归档就必须使用replica或logical级别。
  • wal_buffers:wal缓冲大大小,默认值 -1 选择等于shared_buffer的 1/32 的尺寸(大约3%),但是不小于64kB也不大于 WAL 段的尺寸(通常为)。如果自动的选择太大或太小可以手工设置该值,但是任何小于32kB的正值都将被当作32kB。 如果指定值时没有单位,则以WAL块作为单位,即为 XLOG_BLCKSZ 字节,通常为8kB。这个参数只能在服务器启动时设置。
  • wal_segment_size:单个WAL文件的大小,默认为16MB。

4.WAL实验

#为了尽快覆盖,我们将max_wal_size设置为64M,即最多允许4个wal segment
postgres=# show max_wal_size;

 max_wal_size 
--------------

 64MB
(1 row)

postgres=# show wal_segment_size;

 wal_segment_size 
------------------

 16MB
(1 row)
#查看pg_wal目录下wal_segment 只有1个
[root@test access]# cd /data/pgdata/pg_wal/
[root@test pg_wal]# ll
total 16384
-rw------- 1 pg14 pg14 16777216 Mar 16 11:55 000000010000000000000001
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status

#执行wal切换
postgres=# select pg_switch_wal();

 pg_switch_wal 
---------------

 0/1AB4300
(1 row)
#目录下多个一个,segment_id(后8位) 加1
[root@test pg_wal]# ll
total 32768
-rw------- 1 pg14 pg14 16777216 Mar 16 11:56 000000010000000000000001
-rw------- 1 pg14 pg14 16777216 Mar 16 11:56 000000010000000000000002
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status
#又切换了2次,
postgres=# select pg_switch_wal();

 pg_switch_wal 
---------------

 0/2000078
(1 row)

postgres=# select pg_switch_wal();

 pg_switch_wal 
---------------

 0/30000F0
#查看WAL目录
[root@test pg_wal]# ll
total 49152
-rw------- 1 pg14 pg14 16777216 Mar 16 11:59 000000010000000000000003
-rw------- 1 pg14 pg14 16777216 Mar 16 11:59 000000010000000000000004
-rw------- 1 pg14 pg14 16777216 Mar 16 11:59 000000010000000000000005
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status
只有3个? 显然4*16M触发了max_wal_size,执行了清理。
#再切换一次
postgres=# select pg_switch_wal();

 pg_switch_wal 
---------------

 0/4000078
(1 row)
[root@test pg_wal]# ll
total 49152
-rw------- 1 pg14 pg14 16777216 Mar 16 12:02 000000010000000000000005
-rw------- 1 pg14 pg14 16777216 Mar 16 11:59 000000010000000000000006
-rw------- 1 pg14 pg14 16777216 Mar 16 12:02 000000010000000000000007
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status
清理了3和4,保留了5,多给了6和7
postgres=# select pg_walfile_name(pg_current_wal_lsn());

     pg_walfile_name      
--------------------------

 000000010000000000000005
 当前使用的wal 是5 

 一次清理2个是固定的吗?显然不是的,每当检查点启动时,PG都会估计并准备下一个检查点周期所需的wal segment数量,这个估计基于前一个检查点周期中消耗的文件数据量,这个值应该在min_wal_size和max_wal_size之间。如果我们调大max_wal_size。那么能保留的wal segment就会变多,当达到阈值需要清理时,一次清理的也会变多下面验证

 postgres=# alter system set max_wal_size='129MB';
ALTER SYSTEM
postgres=# exit
[pg14@test ~]$  pg_ctl stop -D $PGDATA -l /tmp/logfile
waiting for server to shut down.... done
server stopped
[pg14@test ~]$  pg_ctl start -D $PGDATA -l /tmp/logfile
waiting for server to start.... done
server started
[pg14@test ~]$  psql -Upostgres -dpostgres
psql (14.6)
Type "help" for help.

postgres=# show max_wal_size;

 max_wal_size 
--------------

 129MB

 postgres=# select pg_switch_wal();   --5 ->6

 pg_switch_wal 
---------------

 0/50001D8
(1 row)

postgres=# select pg_switch_wal(); --6 ->7

 pg_switch_wal 
---------------

 0/6000078
(1 row)

postgres=# select pg_switch_wal(); --7 ->8

 pg_switch_wal 
---------------

 0/7000000
(1 row)

[root@test pg_wal]# ll
total 65536
-rw------- 1 pg14 pg14 16777216 Mar 16 12:17 000000010000000000000005
-rw------- 1 pg14 pg14 16777216 Mar 16 12:17 000000010000000000000006
-rw------- 1 pg14 pg14 16777216 Mar 16 12:18 000000010000000000000007
-rw------- 1 pg14 pg14 16777216 Mar 16 12:18 000000010000000000000008
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status
postgres=# select pg_switch_wal();  --8->9 

 pg_switch_wal 
---------------

 0/7000078
(1 row)
[root@test pg_wal]# ll
total 81920
-rw------- 1 pg14 pg14 16777216 Mar 16 12:18 000000010000000000000009
-rw------- 1 pg14 pg14 16777216 Mar 16 12:17 00000001000000000000000A
-rw------- 1 pg14 pg14 16777216 Mar 16 12:17 00000001000000000000000B
-rw------- 1 pg14 pg14 16777216 Mar 16 12:18 00000001000000000000000C
-rw------- 1 pg14 pg14 16777216 Mar 16 12:18 00000001000000000000000D
drwx------ 2 pg14 pg14        6 Mar 15 10:14 archive_status

这次直接清理了4个。


wal segment的数量还受wal_keep_size 控制,针对wal segment的数量官网文档如下解释:

pg_wal目录中的 WAL 段文件数量取决于min_wal_sizemax_wal_size以及在之前的检查点周期中产生的 WAL 数量。当旧的日志段文件不再被需要时,它们将被移除或者被再利用(也就是被重命名变成数列中未来的段)。如果由于日志输出率的短期峰值导致超过max_wal_size,不需要的段文件将被移除直到系统回到这个限制以下。低于该限制时,系统会再利用足够的 WAL 文件来覆盖直到下一个检查点之前的需要。这种需要是基于之前的检查点周期中使用的 WAL 文件数量的移动平均数估算出来的。如果实际用量超过估计值,移动平均数会立即增加,因此它能在一定程度上适应峰值用量而不是平均用量。min_wal_size对回收给未来使用的 WAL 文件的量设置了一个最小值,这个参数指定数量的 WAL 将总是被回收给未来使用,即便系统很闲并且 WAL 用量估计建议只需要一点点 WAL 时也是如此。

独立于max_wal_size之外,始终保留最新的 wal_keep_size 兆字节的 WAL 文件和一个额外的 WAL 文件。还有,如果使用了 WAL 归档,旧的段在被归档之前不能被移除或者再利用。如果 WAL 归档无法跟上产生 WAL 的步伐,或者如果archive_command重复失败,旧的 WAL 文件将累积在pg_wal中,直到该情况被解决。一个使用了复制槽的较慢或者失败的后备服务器也会带来同样的效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南風_入弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值