PostgreSQL全页写机制深度解析:如何平衡WAL性能与数据可靠性

在PostgreSQL的架构设计中,预写日志(WAL)是保障数据一致性和崩溃恢复的核心机制。而全页写(Full Page Write)作为WAL的重要组成部分,却是一个容易被开发者忽视的"幕后功臣"。本文将从原理、性能影响、调优实践等角度,深入剖析全页写机制对WAL日志的影响,助你掌握PostgreSQL性能优化的关键细节。

一、全页写机制的原理与作用

1.1. 什么是全页写?

在PostgreSQL中,每个数据页的大小默认为8KB。全页写机制规定:在每次检查点(Checkpoint)后的第一次修改中,无论数据变动多小,都会将整个数据页的内容写入WAL日志(而非仅记录增量修改)。

默认配置下,该机制通过参数full_page_writes=on启用。

1.2. 为什么需要全页写?

防止部分页写入(Partial Page Write)

当操作系统或存储设备发生崩溃时,可能导致数据页仅部分写入磁盘(例如只写入了4KB)。此时,若WAL仅记录增量修改,恢复时会因基础页损坏而无法修复数据。全页写记录的完整页可作为恢复的"基线",确保后续增量修改的正确性。

1.3. 全页写的代价

WAL日志体积显著增加:全页写记录的8KB完整页,可能比增量日志大数倍。

额外的I/O压力:更大的WAL日志意味着更多的磁盘写入操作。

二、全页写对WAL的四大影响

2.1 WAL日志体积膨胀

典型场景

检查点后的首次数据页修改(如批量插入、索引创建)会触发全页写。例如,一个仅修改了10字节的UPDATE操作,若发生在检查点后,则需写入8KB的完整页到WAL。

数据对比:

操作类型 增量WAL记录大小 全页写WAL记录大小
单行INSERT ~200字节 8KB
创建B-tree索引 若干增量记录 全页写×N个数据页

影响:存储成本增加,日志备份和传输时间延长。

2.2 崩溃恢复的"双刃剑"

优势:

全页写是崩溃恢复的最后一道防线。即使存储层发生部分页写入,PostgreSQL仍能从WAL中提取完整页进行修复。

风险:

若关闭全页写(full_page_writes=off),且存储设备不支持原子写入(如机械硬盘或部分SSD),崩溃后可能面临数据永久损坏。

2.3 性能波动与写入延迟

写入瓶颈:

全页写产生的额外I/O可能成为高并发写入场景的性能瓶颈。例如,在OLTP系统中,频繁的小事务可能导致大量全页写操作,拖慢整体吞吐量。

检查点频率的关联:

参数checkpoint_timeout(默认5分钟)控制检查点触发频率。检查点越频繁,全页写的触发次数越多,但恢复时间更短。需权衡两者关系。

2.4 与WAL压缩的协同效应

缓解空间占用:

PostgreSQL 13+支持wal_compression=on,可对WAL日志(包括全页写内容)进行LZ4或ZSTD压缩,降低日志体积(压缩率通常达30%~70%)。

CPU开销:

压缩操作消耗额外CPU资源,需在高I/O和低CPU环境中谨慎启用。

三、实验验证

PostgreSQL 9.5以后的pg_waldump都带有统计功能,可以查看不同类型的WAL记录的数量,大小以及FPI的比例。

3.1 postgres.conf配置

下面是一个未经特别优化的配置

cpp 复制代码
shared_buffers = 32GB
checkpoint_completion_target = 0.9
checkpoint_timeout = 5min
min_wal_size = 1GB
max_wal_size = 4GB
wal_log_hints = on
wal_level = replica
wal_keep_size = 1000

3.2 测试

3.2.1 准备pgbench测试环境

cpp 复制代码
[postgres@db2 ~]$ pgbench -i
dropping old tables...
NOTICE:  table "pgbench_accounts" does not exist, skipping
NOTICE:  table "pgbench_branches" does not exist, skipping
NOTICE:  table "pgbench_history" does not exist, skipping
NOTICE:  table "pgbench_tellers" does not exist, skipping
creating tables...
generating data (client-side)...
100000 of 100000 tuples (100%) done (elapsed 0.09 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done in 0.33 s (drop tables 0.00 s, create tables 0.01 s, client-side generate 0.13 s, vacuum 0.09 s, primary keys 0.10 s).

3.2.2先手动执行checkpoint

cpp 复制代码
[postgres@db2 ~]$ pgbench -n -c 64 -j 64 -T 10
pgbench (16.1)

transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 64
number of threads: 64
maximum number of tries: 1
duration: 10 s
number of transactions actually processed: 4474
number of failed transactions: 0 (0.000%)
latency average = 138.536 ms
initial connection time = 491.308 ms
tps = 461.973032 (without initial connection time)

3.2.3记录当前的LSN

cpp 复制代码
[postgres@db2 ~]$  psql -c "select pg_current_wal_lsn()"
 pg_current_wal_lsn 
--------------------
 5/D5022100
(1 row)

3.2.4 日志统计

统计压测期间产生的WAL

cpp 复制代码
[postgres@db2 ~]$ pg_waldump -t 6 --stats=record -s  5/D4000000 -e  5/D5022100
WAL statistics between 5/D4000028 and 5/D5022100:
Type                                           N      (%)          Record size      (%)             FPI size      (%)        Combined size      (%)
----                                           -      ---          -----------      ---             --------      ---        -------------      ---
Transaction/COMMIT                          4477 ( 12.68)               152474 (  6.22)                    0 (  0.00)               152474 (  0.91)
Standby/RUNNING_XACTS                          3 (  0.01)                  162 (  0.01)                    0 (  0.00)                  162 (  0.00)
Standby/INVALIDATIONS                          3 (  0.01)                  270 (  0.01)                    0 (  0.00)                  270 (  0.00)
Heap2/PRUNE                                 2276 (  6.45)               150920 (  6.15)                    0 (  0.00)               150920 (  0.90)
Heap2/VACUUM                                  29 (  0.08)                 1556 (  0.06)                    0 (  0.00)                 1556 (  0.01)
Heap2/VISIBLE                                 94 (  0.27)                 5561 (  0.23)                24576 (  0.17)                30137 (  0.18)
Heap/INSERT                                 4420 ( 12.52)               351748 ( 14.34)                10756 (  0.08)               362504 (  2.16)
Heap/UPDATE                                 2190 (  6.20)               369994 ( 15.09)                 9860 (  0.07)               379854 (  2.26)
Heap/HOT_UPDATE                            11129 ( 31.52)               803331 ( 32.76)                 7644 (  0.05)               810975 (  4.83)
Heap/LOCK                                   8198 ( 23.22)               450277 ( 18.36)             12251380 ( 85.46)             12701657 ( 75.66)
Heap/INPLACE                                   3 (  0.01)                  470 (  0.02)                 7388 (  0.05)                 7858 (  0.05)
Heap/INSERT+INIT                              60 (  0.17)                 4740 (  0.19)                    0 (  0.00)                 4740 (  0.03)
Heap/UPDATE+INIT                             110 (  0.31)                16026 (  0.65)                    0 (  0.00)                16026 (  0.10)
Btree/INSERT_LEAF                           2306 (  6.53)               144537 (  5.89)              2024340 ( 14.12)              2168877 ( 12.92)
Btree/VACUUM                                   2 (  0.01)                  264 (  0.01)                    0 (  0.00)                  264 (  0.00)
CommitTs/ZEROPAGE                              6 (  0.02)                  180 (  0.01)                    0 (  0.00)                  180 (  0.00)
                                        --------                      --------                      --------                      --------
Total                                      35306                       2452510 [14.61%]             14335944 [85.39%]             16788454 [100%]

这个统计结果显示FPI的比例占到了85.39%。Record size的比例占了14.61%,也就是说WAL被放大了近6倍,这个的比例是相当高的。

可以通过计算WAL距离的方式,算出准确的FPI比例。

cpp 复制代码
[postgres@db2 ~]$ pg_waldump -t 6 --stats=record -s  5/D4000000 -e  5/D5022100

postgres=#  select pg_wal_lsn_diff('5/D5022100','5/D4000000');
 pg_wal_lsn_diff 
-----------------
        16916736
(1 row)

postgres=# select 16916736.0/ 16788454;
      ?column?      
--------------------
 1.0076410847597998
(1 row)

这里计算的有点问题,大小1了

三、调优实践:如何平衡性能与可靠性?

3.1 逻辑层

在应用的写负载不变的情况下,减少WAL生成量主要有下面几种办法。

1)延长checkpoint时间间隔

FPI产生于checkpoint之后第一次变脏的page,在下次checkpoint到了之前,已经输出过PFI的page是不需要再次输出FPI。因此checkpoint时间间隔越长,FPI产生的频度会越低。增大checkpoint_timeout和max_wal_size可以延长checkpoint时间间隔。

2)增加HOT_UPDATE比例

普通的UPDATE经常需要更新2个数据块,并且可能还要更新索引page,这些又都有可能产生FPI。而HOT_UPDATE只修改1个数据块,需要写的WAL量也大大减少。

3)压缩

PostgreSQL9.5新增加了一个wal_compression参数,设为on可以对FPI进行压缩,削减WAL的大小。另外还可以在外部通过SSL/SSH的压缩功能减少主备间的通信流量,已经自定义归档脚本对归档的WAL进行压缩。

3.2 参数层

3.2.1. 关键参数配置

参数 推荐值 说明
full_page_writes on(默认) 除非存储支持原子写入(如ZFS或企业级存储),否则勿关闭!
checkpoint_timeout 10min~1h 延长检查点间隔以减少全页写频率,但需配合max_wal_size避免磁盘占满。
wal_compression on(可选) 启用压缩以缓解WAL体积,需监控CPU利用率。

3.2.2. 高性能存储场景的特殊处理

原子写入存储设备:

若使用支持原子写入的存储(如ZFS、带断电保护的NVMe SSD),可尝试关闭全页写:

cpp 复制代码
ALTER SYSTEM SET full_page_writes = off;
SELECT pg_reload_conf();

警告:需通过pg_test_fsync工具验证存储的原子性,并严格测试崩溃恢复流程。

3.3.3. 监控与诊断工具

  • 查看全页写占比:

通过WAL解析工具pg_waldump,观察FPW(全页写)记录的比例:

cpp 复制代码
pg_waldump -p /data/pg_wal 0000000100000001000000A0 | grep FPW
  • 检查点性能监控:

查询pg_stat_bgwriter视图,关注以下指标:

(1)checkpoints_timed:计划内检查点次数

(2)checkpoints_req:计划外检查点次数

(3)buffers_checkpoint:检查点期间写入的缓冲区数量

3.4优化测试

3.4.1 延长checkpoint时间

首先优化checkpoint相关参数

postgres.conf:

cpp 复制代码
shared_buffers = 32GB
checkpoint_completion_target = 0.1
checkpoint_timeout = 60min
min_wal_size = 4GB
max_wal_size = 64GB
wal_log_hints = on
wal_level = replica
wal_keep_segments = 1000

1)先手动执行checkpoint

cpp 复制代码
[postgres@db2 pg_wal]$ psql -c "checkpoint"
CHECKPOINT

2) 再利用pgbench做一个压测10w个事务

cpp 复制代码
psql -c "select pg_current_wal_lsn()"  ; pgbench -n -c 100 -j 100 -t 1000 ;psql -c "select pg_current_wal_lsn()"

 pg_current_wal_lsn 
--------------------
 5/D8FD807

pgbench (16.1)

transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 100
number of threads: 100
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 100000/100000
number of failed transactions: 0 (0.000%)
latency average = 182.966 ms
initial connection time = 554.102 ms
tps = 546.550591 (without initial connection time)

 pg_current_wal_lsn 
--------------------
 5/DCCD5B20
(1 row)

3) 记录当前的LSN

cpp 复制代码
[postgres@db2 pg_wal]$ psql -c "select pg_current_wal_lsn()"
 pg_current_wal_lsn 
--------------------
 5/D8FC31A8
(1 row)

4) 日志统计

统计压测期间产生的WAL

  • 第一次执行
cpp 复制代码
[postgres@db2 pg_wal]$  pg_waldump -t 6 --stats=record -s  5/D8FD8070 -e  5/DCCD5B20
WAL statistics between 5/D8FD8070 and 5/DCCD5B20:
Type                                           N      (%)          Record size      (%)             FPI size      (%)        Combined size      (%)
----                                           -      ---          -----------      ---             --------      ---        -------------      ---
Transaction/COMMIT                        100018 ( 13.77)              3402996 (  7.29)                    0 (  0.00)              3402996 (  5.47)
Storage/TRUNCATE                               1 (  0.00)                   46 (  0.00)                    0 (  0.00)                   46 (  0.00)
CLOG/ZEROPAGE                                  4 (  0.00)                  120 (  0.00)                    0 (  0.00)                  120 (  0.00)
Standby/LOCK                                   1 (  0.00)                   42 (  0.00)                    0 (  0.00)                   42 (  0.00)
Standby/RUNNING_XACTS                         11 (  0.00)                 4364 (  0.01)                    0 (  0.00)                 4364 (  0.01)
Standby/INVALIDATIONS                         10 (  0.00)                 1140 (  0.00)                    0 (  0.00)                 1140 (  0.00)
Heap2/PRUNE                                77430 ( 10.66)              5120674 ( 10.97)             12716608 ( 82.03)             17837282 ( 28.68)
Heap2/VACUUM                                 186 (  0.03)                11740 (  0.03)                    0 (  0.00)                11740 (  0.02)
Heap2/VISIBLE                                861 (  0.12)                50814 (  0.11)                24576 (  0.16)                75390 (  0.12)
Heap/INSERT                                99306 ( 13.67)              7844974 ( 16.80)                53180 (  0.34)              7898154 ( 12.70)
Heap/UPDATE                                 5780 (  0.80)               875796 (  1.88)               117896 (  0.76)               993692 (  1.60)
Heap/HOT_UPDATE                           294009 ( 40.48)             21248245 ( 45.51)               457920 (  2.95)             21706165 ( 34.90)
Heap/LOCK                                 141566 ( 19.49)              7644594 ( 16.37)                48456 (  0.31)              7693050 ( 12.37)
Heap/INPLACE                                  36 (  0.00)                 7084 (  0.02)                 7388 (  0.05)                14472 (  0.02)
Heap/INSERT+INIT                             694 (  0.10)                54826 (  0.12)                    0 (  0.00)                54826 (  0.09)
Heap/UPDATE+INIT                             279 (  0.04)                34720 (  0.07)                    0 (  0.00)                34720 (  0.06)
Btree/INSERT_LEAF                           6059 (  0.83)               384729 (  0.82)              2075940 ( 13.39)              2460669 (  3.96)
Btree/VACUUM                                   8 (  0.00)                 3212 (  0.01)                    0 (  0.00)                 3212 (  0.01)
CommitTs/ZEROPAGE                            122 (  0.02)                 3660 (  0.01)                    0 (  0.00)                 3660 (  0.01)
                                        --------                      --------                      --------                      --------
Total                                     726381                      46693776 [75.08%]             15501964 [24.92%]             62195740 [100%]
[postgres@db2 pg_wal]$ 
  • 第二次执行

注意不要执行检查点

cpp 复制代码
[postgres@db2 pg_wal]$ psql -c "select pg_current_wal_lsn()"  ; pgbench -n -c 100 -j 100 -t 1000 ;psql -c "select pg_current_wal_lsn()"
 pg_current_wal_lsn 
--------------------
 5/E45E9D00
(1 row)

pgbench (16.1)
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 100
number of threads: 100
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 100000/100000
number of failed transactions: 0 (0.000%)
latency average = 164.796 ms
initial connection time = 295.767 ms
tps = 606.811368 (without initial connection time)
 pg_current_wal_lsn 
--------------------
 5/E7387038
(1 row)

查看WAL产生的情况

cpp 复制代码
[postgres@db2 pg_wal]$ pg_waldump -t 6 --stats=record -s  5/E09C0E70 -e  5/E45E4F38
WAL statistics between 5/E09C0E70 and 5/E45E4F38:
Type                                           N      (%)          Record size      (%)             FPI size      (%)        Combined size      (%)
----                                           -      ---          -----------      ---             --------      ---        -------------      ---
Transaction/COMMIT                        100019 ( 14.01)              3402998 (  7.46)                    0 (  0.00)              3402998 (  5.53)
CLOG/ZEROPAGE                                  3 (  0.00)                   90 (  0.00)                    0 (  0.00)                   90 (  0.00)
Standby/RUNNING_XACTS                         12 (  0.00)                 5045 (  0.01)                    0 (  0.00)                 5045 (  0.01)
Standby/INVALIDATIONS                          4 (  0.00)                  504 (  0.00)                    0 (  0.00)                  504 (  0.00)
Heap2/PRUNE                                76745 ( 10.75)              5193777 ( 11.38)             12584052 ( 79.36)             17777829 ( 28.92)
Heap2/VACUUM                                 208 (  0.03)                12024 (  0.03)                    0 (  0.00)                12024 (  0.02)
Heap2/VISIBLE                                572 (  0.08)                33763 (  0.07)                24576 (  0.15)                58339 (  0.09)
Heap/INSERT                                99320 ( 13.92)              7844680 ( 17.19)               277344 (  1.75)              8122024 ( 13.21)
Heap/UPDATE                                 1542 (  0.22)               172852 (  0.38)               108344 (  0.68)               281196 (  0.46)
Heap/HOT_UPDATE                           298298 ( 41.79)             21518813 ( 47.16)              1175964 (  7.42)             22694777 ( 36.91)
Heap/LOCK                                 134183 ( 18.80)              7245882 ( 15.88)                    0 (  0.00)              7245882 ( 11.79)
Heap/INPLACE                                  31 (  0.00)                 6103 (  0.01)                 7388 (  0.05)                13491 (  0.02)
Heap/INSERT+INIT                             680 (  0.10)                53720 (  0.12)                    0 (  0.00)                53720 (  0.09)
Heap/UPDATE+INIT                             216 (  0.03)                18160 (  0.04)                    0 (  0.00)                18160 (  0.03)
Btree/INSERT_LEAF                           1758 (  0.25)               110191 (  0.24)              1672340 ( 10.55)              1782531 (  2.90)
Btree/DELETE                                   2 (  0.00)                  193 (  0.00)                 7400 (  0.05)                 7593 (  0.01)
Btree/VACUUM                                   8 (  0.00)                 2440 (  0.01)                    0 (  0.00)                 2440 (  0.00)
CommitTs/ZEROPAGE                            122 (  0.02)                 3660 (  0.01)                    0 (  0.00)                 3660 (  0.01)
                                        --------                      --------                      --------                      --------
Total                                     713723                      45624895 [74.21%]             15857408 [25.79%]             61482303 [100%]

这个统计结果显示FPI的占比明显减少

次数 TPS 非FPI FPI FPI占比 WAL总量
1 553 46693776 15501964 24.92% 62195740
2 606 46059408 245240 0.53% 46304648

可以多做几次测试对比效果。

3.4.2 增加HOT_UPDATE比例

HOT_UPDATE比例过低的一个很常见的原因是更新频繁的表的fillfactor设置不恰当。fillfactor的默认值为100%,可以先将其调整为90%。

对于宽表,要进一步减小fillfactor使得至少可以保留一个tuple的空闲空间。可以查询pg_class系统表估算平均tuple大小,并算出合理的fillfactor值。

cpp 复制代码
postgres=# select 1 - relpages/reltuples max_fillfactor from pg_class where relname='big_tb';
    max_fillfactor    
----------------------
 0.69799901185770750988
(1 row)

再上面估算出的69%的基础上,可以把fillfactor再稍微设小一点,比如设成65% 。

  • 在前面优化过的参数的基础上,先保持fillfactor=100不变,执行10w事务的压测
cpp 复制代码
[postgres@db2 pg_wal]$  psql -c "checkpoint;select pg_current_wal_lsn()"  ; pgbench -n -c 100 -j 100 -t 1000 ;
psql -c "select pg_current_wal_lsn()"
CHECKPOINT
 pg_current_wal_lsn 
--------------------
 5/E738CB90
(1 row)

pgbench (16.1)


transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 100
number of threads: 100
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 100000/100000
number of failed transactions: 0 (0.000%)
latency average = 165.636 ms
initial connection time = 398.058 ms
tps = 603.732351 (without initial connection time)
[postgres@db2 pg_wal]$ psql -c "select pg_current_wal_lsn()"
 pg_current_wal_lsn 
--------------------
 5/EAFB96D8
(1 row)

查看WAL占比

cpp 复制代码
postgres@db2 pg_wal]$ pg_waldump -t 6 --stats=record -s  5/E738CB90 -e  5/EAFB96D8
WAL statistics between 5/E738CB90 and 5/EAFB96D8:
Type                                           N      (%)          Record size      (%)             FPI size      (%)        Combined size      (%)
----                                           -      ---          -----------      ---             --------      ---        -------------      ---
Transaction/COMMIT                        100011 ( 13.88)              3401830 (  7.40)                    0 (  0.00)              3401830 (  5.53)
CLOG/ZEROPAGE                                  3 (  0.00)                   90 (  0.00)                    0 (  0.00)                   90 (  0.00)
Standby/RUNNING_XACTS                         10 (  0.00)                 3863 (  0.01)                    0 (  0.00)                 3863 (  0.01)
Standby/INVALIDATIONS                          5 (  0.00)                  642 (  0.00)                    0 (  0.00)                  642 (  0.00)
Heap2/PRUNE                                79265 ( 11.00)              5349495 ( 11.64)             12960248 ( 83.19)             18309743 ( 29.75)
Heap2/VACUUM                                 127 (  0.02)                 8216 (  0.02)                    0 (  0.00)                 8216 (  0.01)
Heap2/VISIBLE                                908 (  0.13)                53587 (  0.12)                24576 (  0.16)                78163 (  0.13)
Heap/INSERT                                99352 ( 13.79)              7846308 ( 17.07)               564988 (  3.63)              8411296 ( 13.67)
Heap/UPDATE                                 1397 (  0.19)               138376 (  0.30)                 4072 (  0.03)               142448 (  0.23)
Heap/HOT_UPDATE                           298490 ( 41.43)             21517154 ( 46.81)               909692 (  5.84)             22426846 ( 36.44)
Heap/LOCK                                 138431 ( 19.21)              7475274 ( 16.26)                    0 (  0.00)              7475274 ( 12.15)
Heap/INPLACE                                  24 (  0.00)                 4705 (  0.01)                 7388 (  0.05)                12093 (  0.02)
Heap/INSERT+INIT                             648 (  0.09)                51192 (  0.11)                    0 (  0.00)                51192 (  0.08)
Heap/UPDATE+INIT                             148 (  0.02)                12456 (  0.03)                    0 (  0.00)                12456 (  0.02)
Btree/INSERT_LEAF                           1545 (  0.21)                97340 (  0.21)              1108660 (  7.12)              1206000 (  1.96)
Btree/DELETE                                   4 (  0.00)                  526 (  0.00)                    0 (  0.00)                  526 (  0.00)
Btree/VACUUM                                   6 (  0.00)                 2420 (  0.01)                    0 (  0.00)                 2420 (  0.00)
CommitTs/ZEROPAGE                            122 (  0.02)                 3660 (  0.01)                    0 (  0.00)                 3660 (  0.01)
                                        --------                      --------                      --------                      --------
Total                                     720496                      45967134 [74.69%]             15579624 [25.31%]             61546758 [100%]
  • 设置fillfactor=90
cpp 复制代码
postgres=# alter table pgbench_accounts set (fillfactor=90);
ALTER TABLE
postgres=# vacuum full pgbench_accounts;
VACUUM
postgres=# alter table pgbench_tellers set (fillfactor=90);
ALTER TABLE
postgres=# vacuum full pgbench_tellers;
VACUUM
postgres=# alter table pgbench_branches set (fillfactor=90);
ALTER TABLE
postgres=# vacuum full pgbench_branches;
VACUUM

再执行10w事务的压测

cpp 复制代码
postgres@db2 pg_wal]$  psql -c "checkpoint;select pg_current_wal_lsn()"  ; pgbench -n -c 100 -j 100 -t 1000 ;psql -c "select pg_current_wal_lsn()"

CHECKPOINT
 pg_current_wal_lsn 
--------------------
 5/EBE90698
(1 row)

pgbench (16.1)
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 100
number of threads: 100
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 100000/100000
number of failed transactions: 0 (0.000%)
latency average = 202.112 ms
initial connection time = 281.672 ms
tps = 494.775446 (without initial connection time)
[postgres@db2 pg_wal]$ psql -c "checkpoint;select pg_current_wal_lsn()" 
CHECKPOINT
 pg_current_wal_lsn 
--------------------
 5/EF94B3E8
(1 row)

查看WAL日志量

cpp 复制代码
[postgres@db2 pg_wal]$ pg_waldump -t 6 --stats=record -s  5/EBE90698 -e  5/EF94B3E8
WAL statistics between 5/EBE90698 and 5/EF94B3E8:
Type                                           N      (%)          Record size      (%)             FPI size      (%)        Combined size      (%)
----                                           -      ---          -----------      ---             --------      ---        -------------      ---
XLOG/CHECKPOINT_ONLINE                         1 (  0.00)                  114 (  0.00)                    0 (  0.00)                  114 (  0.00)
Transaction/COMMIT                        100017 ( 13.92)              3402514 (  7.45)                    0 (  0.00)              3402514 (  5.67)
Storage/TRUNCATE                               1 (  0.00)                   46 (  0.00)                    0 (  0.00)                   46 (  0.00)
CLOG/ZEROPAGE                                  3 (  0.00)                   90 (  0.00)                    0 (  0.00)                   90 (  0.00)
Standby/LOCK                                   1 (  0.00)                   42 (  0.00)                    0 (  0.00)                   42 (  0.00)
Standby/RUNNING_XACTS                         13 (  0.00)                 4994 (  0.01)                    0 (  0.00)                 4994 (  0.01)
Standby/INVALIDATIONS                          5 (  0.00)                  594 (  0.00)                    0 (  0.00)                  594 (  0.00)
Heap2/PRUNE                                78624 ( 10.94)              5189982 ( 11.36)                14040 (  0.10)              5204022 (  8.68)
Heap2/VACUUM                                 279 (  0.04)                16034 (  0.04)                    0 (  0.00)                16034 (  0.03)
Heap2/VISIBLE                               1182 (  0.16)                69753 (  0.15)                24576 (  0.17)                94329 (  0.16)
Heap/INSERT                                99359 ( 13.83)              7846936 ( 17.17)               450048 (  3.15)              8296984 ( 13.83)
Heap/UPDATE                                 1121 (  0.16)                98550 (  0.22)                    0 (  0.00)                98550 (  0.16)
Heap/HOT_UPDATE                           298679 ( 41.58)             21519152 ( 47.09)             13504184 ( 94.58)             35023336 ( 58.39)
Heap/LOCK                                 136656 ( 19.02)              7379429 ( 16.15)                 6904 (  0.05)              7386333 ( 12.32)
Heap/INPLACE                                  26 (  0.00)                 5028 (  0.01)                 8984 (  0.06)                14012 (  0.02)
Heap/INSERT+INIT                             641 (  0.09)                50639 (  0.11)                    0 (  0.00)                50639 (  0.08)
Heap/UPDATE+INIT                             250 (  0.03)                22193 (  0.05)                    0 (  0.00)                22193 (  0.04)
Btree/INSERT_LEAF                           1371 (  0.19)                87315 (  0.19)               269200 (  1.89)               356515 (  0.59)
Btree/VACUUM                                   8 (  0.00)                 3042 (  0.01)                    0 (  0.00)                 3042 (  0.01)
CommitTs/ZEROPAGE                            122 (  0.02)                 3660 (  0.01)                    0 (  0.00)                 3660 (  0.01)
                                        --------                      --------                      --------                      --------
Total                                     718359                      45700107 [76.19%]             14277936 [23.81%]             59978043 [100%]
次数 TPS 非FPI FPI FPI占比 WAL总量
1 603 45967134 45967134 25.31% 61546758
2 494 46059408 45700107 23.81% 59978043

结论:设置fillfactor=90后,生成的WAL量从61546758减少到59978043 。

3.4.3 设置WAL压缩

修改postgres.conf,开启WAL压缩

cpp 复制代码
wal_compression = on

再次测试

cpp 复制代码
postgres@db2 pg_wal]$  psql -c "checkpoint;select pg_current_wal_lsn()"  ; pgbench -n -c 100 -j 100 -t 1000 ;psql -c "checkpoint;select pg_current_wal_lsn()" 
CHECKPOINT
 pg_current_wal_lsn 
--------------------
 5/EBE90698
(1 row)

pgbench (16.1)
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 100
number of threads: 100
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 100000/100000
number of failed transactions: 0 (0.000%)
latency average = 202.112 ms
initial connection time = 281.672 ms
tps = 494.775446 (without initial connection time)
[postgres@db2 pg_wal]$ psql -c "checkpoint;select pg_current_wal_lsn()" 
CHECKPOINT
 pg_current_wal_lsn 
--------------------
 5/EF94B3E8
(1 row)

(1 row)

生成的WAL统计如下:

cpp 复制代码
[postgres@db2 pg_wal]$ pg_waldump -t 6 --stats=record -s  5/F0000188 -e  5/F2FFDB80
WAL statistics between 5/F0000188 and 5/F2FFDB80:
Type                                           N      (%)          Record size      (%)             FPI size      (%)        Combined size      (%)
----                                           -      ---          -----------      ---             --------      ---        -------------      ---
XLOG/CHECKPOINT_ONLINE                         1 (  0.00)                  114 (  0.00)                    0 (  0.00)                  114 (  0.00)
Transaction/COMMIT                        100012 ( 13.85)              3401832 (  7.40)                    0 (  0.00)              3401832 (  6.97)
Storage/TRUNCATE                               1 (  0.00)                   46 (  0.00)                    0 (  0.00)                   46 (  0.00)
CLOG/ZEROPAGE                                  3 (  0.00)                   90 (  0.00)                    0 (  0.00)                   90 (  0.00)
Standby/LOCK                                   1 (  0.00)                   42 (  0.00)                    0 (  0.00)                   42 (  0.00)
Standby/RUNNING_XACTS                         11 (  0.00)                 4248 (  0.01)                    0 (  0.00)                 4248 (  0.01)
Standby/INVALIDATIONS                          3 (  0.00)                  414 (  0.00)                    0 (  0.00)                  414 (  0.00)
Heap2/PRUNE                                82271 ( 11.39)              5479079 ( 11.91)              2397301 ( 85.86)              7876380 ( 16.14)
Heap2/VACUUM                                 198 (  0.03)                11880 (  0.03)                    0 (  0.00)                11880 (  0.02)
Heap2/VISIBLE                                155 (  0.02)                 9155 (  0.02)                  244 (  0.01)                 9399 (  0.02)
Heap/INSERT                                99245 ( 13.75)              7840332 ( 17.05)                  957 (  0.03)              7841289 ( 16.07)
Heap/UPDATE                                 1199 (  0.17)               112761 (  0.25)                    0 (  0.00)               112761 (  0.23)
Heap/HOT_UPDATE                           298653 ( 41.37)             21524549 ( 46.79)                31778 (  1.14)             21556327 ( 44.18)
Heap/LOCK                                 137773 ( 19.08)              7439742 ( 16.17)                    0 (  0.00)              7439742 ( 15.25)
Heap/INPLACE                                  20 (  0.00)                 3740 (  0.01)                 3615 (  0.13)                 7355 (  0.02)
Heap/INSERT+INIT                             755 (  0.10)                59645 (  0.13)                    0 (  0.00)                59645 (  0.12)
Heap/UPDATE+INIT                             183 (  0.03)                16131 (  0.04)                    0 (  0.00)                16131 (  0.03)
Btree/INSERT_LEAF                           1382 (  0.19)                87602 (  0.19)               358051 ( 12.82)               445653 (  0.91)
Btree/VACUUM                                   6 (  0.00)                 2676 (  0.01)                    0 (  0.00)                 2676 (  0.01)
CommitTs/ZEROPAGE                            122 (  0.02)                 3660 (  0.01)                    0 (  0.00)                 3660 (  0.01)
                                        --------                      --------                      --------                      --------
Total                                     721994                      45997738 [94.28%]              2791946 [5.72%]              48789684 [100%]

设置`wal_compression = on后,生成的WAL量从59978043减少到48789684。

四、全页写的适用场景与规避策略

4.1 如何判断是否需要优化WAL?

关于如何判断是否需要优化WAL,可以通过分析WAL,然后检查下面的条件,做一个粗略的判断:

  • FPI比例高于70%
  • HOT_UPDATE比例低于70%

以上仅仅是粗略的经验值,仅供参考。并且这个FPI比例可能不适用于低写负载的系统,低写负载的系统FPI比例一定非常高,但是,低写负载系统由于写操作很少,因此FPI比例即使高一点也没太大影响。

4.2 优化WAL的副作用

前面用到了3种优化手段,如果设置不当,也会产生副作用,具体如下:

  • 延长checkpoint时间间隔

导致crash恢复时间变长。crash恢复时需要回放的WAL日志量一般小于max_wal_size的一半,WAL回放速度(wal_compression=on时)一般是50MB/s~150MB/s之间。可以根据可容忍的最大crash恢复时间,估算出允许的max_wal_size的最大值。

  • 调整fillfactor

过小的设置会浪费存储空间,这个不难理解。另外,对于频繁更新的表,即使把fillfactor设成100%,每个page里还是要一部分空间被dead tuple占据,不会比设置成一个合适的稍小的fillfactor更节省空间。

  • 设置wal_compression=on

需要额外占用CPU资源进行压缩,但影响不大。

五、总结

全页写机制通过"以空间换安全"的策略,成为PostgreSQL抵御部分页写入风险的终极防线。尽管它会增加WAL日志量和I/O开销,但在大多数生产环境中,开启全页写仍是必要选择。

相关推荐
.Eyes1 小时前
OceanBase 分区裁剪(Partition Pruning)原理解读
数据库·oceanbase
MrZhangBaby2 小时前
SQL-leetcode— 2356. 每位教师所教授的科目种类的数量
数据库
一水鉴天2 小时前
整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接 之2
数据库·人工智能·智能合约
翔云1234563 小时前
Python 中 SQLAlchemy 和 MySQLdb 的关系
数据库·python·mysql
孙霸天3 小时前
Ubuntu20系统上离线安装MongoDB
数据库·mongodb·ubuntu·备份还原
Java 码农3 小时前
nodejs mongodb基础
数据库·mongodb·node.js
TDengine (老段)3 小时前
TDengine IDMP 运维指南(4. 使用 Docker 部署)
运维·数据库·物联网·docker·时序数据库·tdengine·涛思数据
TDengine (老段)3 小时前
TDengine IDMP 最佳实践
大数据·数据库·物联网·ai·时序数据库·tdengine·涛思数据
彬彬醤4 小时前
Mac怎么连接VPS?可以参考这几种方法
大数据·运维·服务器·数据库·线性代数·macos·矩阵
废喵喵呜4 小时前
达梦数据库-实时主备集群部署详解(附图文)手工搭建一主一备数据守护集群DW
网络·数据库·tcp/ip