
今天碰到有人问起,将wal_keep_size从10G调整到1G,结果发现pg_wal目录中的wal日志总量并没有降到1G,反而还是原来的10G以上,没啥变化。是啥原因呢?
今天在回答这个问题的同时,我们可以通过读文档来补补漏,原文文档在这里:
https://www.postgresql.org/docs/16/runtime-config-replication.html#GUC-WAL-KEEP-SIZE
https://www.postgresql.org/docs/16/wal-configuration.html
涉及到wal文件保留,不光是wal_keep_size (始自PG13)。在12及以前的老版本,用的是wal_keep_segments这个参数。
在13.0 的release notes里头有如下说明:
Rename configuration parameter
wal_keep_segmentsto wal_keep_size (Fujii Masao)This determines how much WAL to retain for standby servers. It is specified in megabytes, rather than number of files as with the old parameter. If you previously used
wal_keep_segments, the following formula will give you an approximately equivalent setting:wal_keep_size = wal_keep_segments * wal_segment_size (typically 16MB)
wal_keep_size: 用于指定pg_wal目录下要保留的wal日志的最少尺寸,主要是用于从节点接收这些WAL日志用于流复制。如果一个连接到发送服务器的备库节点落后了超过wal_keep_size兆字节,发送服务器可以移除一个后备节点仍然需要的 WAL 段,在这种情况下复制连接将被中断。最终结果是下行连接也将最终失败(当然这个时候,如果在使用 WAL 归档,后备服务器仍然可以通过从归档获取段来恢复,所以,强调一下的是,在生产环境中,主节点上的归档无论如何是要开启的)。
max_slot_wal_keep_size: 指定replication slots 所允许的在检查点时保留在 pg_wal 目录中的 WAL 文件的最大尺寸。 如果max_slot_wal_keep_size 为 -1(默认值),复制槽可能会保留无限数量的 WAL 文件。 否则,如果复制槽的restart_lsn比当前LSN滞后超过给定的大小,由于删除了所需的WAL文件,使用插槽的备用服务器可能无法继续复制。
然后还有另外两个参数:min_wal_size, max_wal_size
pg_wal目录中的 WAL 段文件数量取决于min_wal_size、max_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中,直到该情况被解决。一个使用了复制槽的较慢或者失败的后备服务器也会带来同样的效果。
综合上边的描述,可以得到下边的结论:
archive操作以及从库节点上的replay操作都正常的情况下:
pg_wal目录至少要保留:min_wal_size + wal_keep_size的大小, 即使 min_wal_size + wal_keep_size > max_wal_size,仍然要保留min_wal_size + wal_keep_size的大小,因为它要优先为standby streaming服务。
当max_wal_size > (min_wal_size + wal_keep_size)时,实际大小可能在这两者之间浮动,但是基本不会超出:max_wal_size + wal_keep_size。
如果archive命令不能正常工作时,系统日志会不断出现archive失败,需要重做的日志项。这个时候,基本上wal_keep_size这些参数都不起作用了。随着数据库事务的往前推进,WAL日志会越积越多。
max_wal_size对checkpoint还有一定的影响,它和参数checkpoint_completion_target(默认值是0.9)可以一起控制checkpoint执行的频率。其基本规则如下:
target = (double) ConvertToXSegs(max_wal_size_mb) / (2.0 + CheckPointCompletionTarget);
如果都按默认值来算,大概1024/16/2.9 = 22.068个WAL,每产生22个左右的WAL文件,就会自动触发一个checkpoint。
如此说来,前边提到的那个问题也就很好重现了,就是说在什么情况下,设置了wal_keep_size参数,但是它不起作用。
min_wal_size设得特别大,比如它本身就设成大于1G,就会造成假象
max_slot_wal_keep_size也设了一个比较大的值,然后确实有复制槽
archive启用,但是它的cmd不好使
在配置相关值之前,我们查一下相关size的默认值:
postgres=# select name, setting, unit, reset_val, boot_val, min_val, max_val from pg_settings where name ~ 'size';
name | setting | unit | reset_val | boot_val | min_val | max_val
------------------------------+----------+------+-----------+----------+---------+------------
block_size | 8192 | | 8192 | 8192 | 8192 | 8192
effective_cache_size | 524288 | 8kB | 524288 | 524288 | 1 | 2147483647
geqo_pool_size | 0 | | 0 | 0 | 0 | 2147483647
huge_page_size | 0 | kB | 0 | 0 | 0 | 2147483647
log_rotation_size | 0 | kB | 0 | 10240 | 0 | 2097151
max_slot_wal_keep_size | -1 | MB | -1 | -1 | -1 | 2147483647
max_wal_size | 1024 | MB | 1024 | 1024 | 2 | 2147483647
min_parallel_index_scan_size | 64 | 8kB | 64 | 64 | 0 | 715827882
min_parallel_table_scan_size | 1024 | 8kB | 1024 | 1024 | 0 | 715827882
min_wal_size | 80 | MB | 80 | 80 | 2 | 2147483647
segment_size | 131072 | 8kB | 131072 | 131072 | 131072 | 131072
track_activity_query_size | 1024 | B | 1024 | 1024 | 100 | 1048576
wal_block_size | 8192 | | 8192 | 8192 | 8192 | 8192
wal_keep_size | 0 | MB | 0 | 0 | 0 | 2147483647
wal_segment_size | 16777216 | B | 16777216 | 16777216 | 1048576 | 1073741824
(15 rows)
postgres=# select name, setting, unit, reset_val, boot_val, min_val, max_val from pg_settings where name ~ 'checkpoint';
name | setting | unit | reset_val | boot_val | min_val | max_val
------------------------------+---------+------+-----------+----------+---------+------------
checkpoint_completion_target | 0.9 | | 0.9 | 0.9 | 0 | 1
checkpoint_flush_after | 32 | 8kB | 32 | 32 | 0 | 256
checkpoint_timeout | 300 | s | 300 | 300 | 30 | 86400
checkpoint_warning | 30 | s | 30 | 30 | 0 | 2147483647
log_checkpoints | off | | off | off | |
(5 rows)
以下是默认值:
wal_keep_size: 0
max_wal_size: 1024 (MB)
min_wal_size: 80 (MB)
checkpoint_completion_target: 0.9
log_checkpoints: off
我们修改一下postgresql.conf如下:
archive_mode = on
archive_command = 'cp %p /iihero/archive/data/%f'
wal_keep_size = 256MB
min_wal_size = 64MB
max_wal_size = 480MB
log_checkpoints = on
checkpoint_completion_target = 0.5
--注意,如果你去掉了上边size值后边的单位:MB,可能会出现意想不到的结果
我们基于此,可以算一下 max_wal_size/16/2.5 = 12,意味着每12个wal log到最多30个wal log的flush会引发一个checkpoint。我们可以看下。
我们看看系统日志文件中关于checkpoint的记录:
2023-05-27 02:01:08.604 UTC [18333] LOG: checkpoint starting: wal
2023-05-27 02:03:06.919 UTC [18333] LOG: checkpoint complete: wrote 6261 buffers (38.2%); 0 WAL file(s) added, 1 removed, 12 recycled; write=118.270 s, sync=0.018 s, total=118.340 s; sync files=7, longest=0.018 s, average=0.003 s; distance=207312 kB, estimate=207312 kB
2023-05-27 02:03:07.463 UTC [18333] LOG: checkpoint starting: wal
2023-05-27 02:03:08.175 UTC [18333] LOG: checkpoint complete: wrote 12485 buffers (76.2%); 0 WAL file(s) added, 1 removed, 12 recycled; write=0.635 s, sync=0.005 s, total=0.713 s; sync files=8, longest=0.003 s, average=0.001 s; distance=212988 kB, estimate=212988 kB
2023-05-27 02:03:08.711 UTC [18333] LOG: checkpoints are occurring too frequently (1 second apart)
2023-05-27 02:03:08.711 UTC [18333] HINT: Consider increasing the configuration parameter "max_wal_size".
2023-05-27 02:03:08.711 UTC [18333] LOG: checkpoint starting: wal
这里大都是因为wal达到量了以后触发的。因为默认的5分钟触发时限都没有达到。
在一个窗口:使用大数据量插入进行模拟:
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t select n, 'test'||n from generate_series(1, 200000) as n;
INSERT 0 200000
mydb=# insert into t select n, 'test'||n from generate_series(1, 200000) as n;
INSERT 0 200000
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# delete from t;
DELETE 4200000
我们通过一个命令:
watch -n 2 -c "ls -lrt pg_wal; du -sh pg_wal; du -sh /iihero/archive/data"
观察pg_wal目录以及archive目录下边wal log大小变化
直接修改配置文件:
archive_mode = on
archive_command = ''
wal_keep_size = 256MB
min_wal_size = 64MB
max_wal_size = 480MB
log_checkpoints = on
checkpoint_completion_target = 0.5
然后执行相同的操作,你会发现WAL日志文件不断堆积。
Every 2.0s: du -sh pg_wal; du -sh /iihero/archive/data Sat May 27 02:31:52 2023
1.7G pg_wal
1.9G /iihero/archive/data
你会发现它可以一直不断的往上涨。
最后再回归到,我们不使用: wal_keep_size,让它为0的那种情况:
配置文件postgresql.conf:
archive_mode = on
archive_command = 'cp %p /iihero/archive/data/%f'
# wal_keep_size = 256MB
min_wal_size = 64MB
max_wal_size = 480MB
log_checkpoints = on
checkpoint_completion_target = 0.5
起始大小如下:
Every 2.0s: ls -lrt pg_wal; du -sh pg_wal; du -sh /iihero/archive/data Sat May 27 02:36:16 2023
total 65548
-rw------- 1 postgres postgres 16777216 May 27 02:31 000000010000000100000096
-rw------- 1 postgres postgres 16777216 May 27 02:31 000000010000000100000094
-rw------- 1 postgres postgres 16777216 May 27 02:31 000000010000000100000095
drwx------ 2 postgres postgres 12288 May 27 02:34 archive_status
-rw------- 1 postgres postgres 16777216 May 27 02:35 000000010000000100000093
65M pg_wal
56K /iihero/archive/data
然后跑一些SQL在另一个窗口:
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t2 select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t2 select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t2 select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t2 select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t2 select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# insert into t select n, 'test'||n from generate_series(1, 800000) as n;
INSERT 0 800000
mydb=# delete from t;
DELETE 4000000
系统有如下日志:
2023-05-27 02:37:13.861 UTC [26338] LOG: checkpoint starting: wal
2023-05-27 02:37:47.264 UTC [26338] LOG: checkpoint complete: wrote 13063 buffers (79.7%); 0 WAL file(s) added, 1 removed, 19 recycled; write=33.304 s, sync=0.045 s, total=33.427 s; sync files=10, longest=0.042 s, average=0.005 s; distance=331777 kB, estimate=331777 kB
2023-05-27 02:37:51.895 UTC [26338] LOG: checkpoint starting: wal
2023-05-27 02:37:53.122 UTC [26338] LOG: checkpoint complete: wrote 15482 buffers (94.5%); 0 WAL file(s) added, 2 removed, 18 recycled; write=1.148 s, sync=0.030 s, total=1.227 s; sync files=5, longest=0.025 s, average=0.006 s; distance=327686 kB, estimate=331368 kB
两个目录下日志大小变化:
Every 2.0s: ls -lrt pg_wal; du -sh pg_wal; du -sh /iihero/archive/data Sat May 27 02:38:58 2023
total 491532
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D7
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D0
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D2
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D1
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000CF
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D6
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D3
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D5
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D4
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000D8
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000BB
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000BC
........................
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000C9
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000CA
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000CB
-rw------- 1 postgres postgres 16777216 May 27 02:37 0000000100000001000000CC
-rw------- 1 postgres postgres 16777216 May 27 02:38 0000000100000001000000CD
drwx------ 2 postgres postgres 12288 May 27 02:38 archive_status
-rw------- 1 postgres postgres 16777216 May 27 02:38 0000000100000001000000CE
481M pg_wal
945M /iihero/archive/data
此后一直稳定在481M左右。该移去的log都移向archive目录下边了。

原文作者:数据库杂记;如有侵权,请联系管理员删除。