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开销,但在大多数生产环境中,开启全页写仍是必要选择。

相关推荐
Watink Cpper2 小时前
[MySQL初阶]MySQL(1)MySQL的理解、库的操作、表的操作
linux·运维·服务器·数据库·c++·后端·mysql
尘世壹俗人2 小时前
spark写数据库用连接池找不到driver类
大数据·数据库·spark
计算机学长大白3 小时前
Redis是什么?如何使用Redis进行缓存操作?
数据库·redis·缓存
nicepainkiller4 小时前
go 分布式redis锁的实现方式
数据库·redis·redis分布式锁
007php0075 小时前
企微审批中MySQL字段TEXT类型被截断的排查与修复实践
大数据·开发语言·数据库·后端·mysql·重构·golang
Elastic 中国社区官方博客5 小时前
使用 Elasticsearch 进行集成测试初始化数据时的注意事项
大数据·数据库·elasticsearch·搜索引擎·集成测试·可用性测试
V+zmm101345 小时前
美食推荐系统的微信小程序+论文源码调试讲解
java·数据库·微信小程序·小程序·毕业设计
SelectDB技术团队6 小时前
Apache Doris 3.0.4 版本正式发布
大数据·数据库·数据分析·doris·存算分离
自不量力的A同学6 小时前
GreptimeDB v0.12 发布,开源 Rust 时序数据库
数据库·开源·时序数据库