本文介绍NVM WAL BUFFER的email list,详细了解开发者对此的讨论,以此深入
了解机制。
1、Heikki
使用mmap()/msync()映射WAL文件,替代WAL buffer。如果读memory-mapped文件有IO错误时,进程会通过SIGBUS杀掉。
重新阅读了[1][2],使用WAL段文件映射到内存作为WAL BUFFER,依赖于WAL文件是否放在PM上切换CPU指令或者msync()用于持久化WAL记录。
听起来挺合理,但是我没测试过。我会尝试与NVM WAL BUFFER进行比对测试。现在,有点担心对于每个段文件mmap/munmap带来的消耗。
同时提到内存映射IO的SIGBUS问题,从坏的内存块读取时会有这个问题,向这个块[3]写时也有这个问题。未来会处理这个问题。
[1]https://www.postgresql.org/message-id/83eafbfd-d9c5-6623-2423-7cab1be3888c%40iki.fi
[2]https://www.postgresql.org/message-id/2aec6e2a-6a32-0c39-e4e2-aad854543aa8%40iki.fi
[3] https://pmem.io/2018/11/26/bad-blocks.htm
https://www.postgresql.org/message-id/2aec6e2a-6a32-0c39-e4e2-aad854543aa8%40iki.fi
2、mmap/munmap带来的消耗
mmap/munmap每个WAL段文件的消耗与write一个block的消耗谁更大。mmap比read消耗大,但是减小系统调用又可以使mmap更具优势。这只是猜测,并不知道mmap真实消耗是多少。
我有一个不同想法,当重用一个段文件时,会一次写所有整个页,段文件的即使没有读取过,也会被重写。但是使用mmap时就不会有这样的行为了。只要想mapped页写一个字节,老内容就会被加载到内存。VM页在该点设置读写后,系统不知道将要写整个页。读取回收文件的老内容显然代价太过糟糕。
当修改mapped区和write()时,对于write-back行为是否有所不同。不管哪种方式,同一个文件同一个页面都会被写脏,但内核可能对何时写回磁盘有不同行为,这对性能影响很大。
这个问题可以分为两类:性能和一致性。程序的作者同样忧虑这些问题,但是也没有一个很好的答案。作者认为同一个文件被多个后端进程调用mmap(flags | MAP_SHARED)对于PM和非PM设备都是保持一致性的,但是还没找到任何如规范文件来证明。
作者在同一个mmaped文件上做了一个小程序,调用memcpy和msync,并行的地址范围不同,没发现损坏的数据。但这也无妨确保一致性,如果有损坏,作者会放弃...
作者会测试Heikki所说的使用mmap和munmap映射每个段文件,来看是否合理。
Mmap/munmap可伸缩性确实很差,但是不认为会影响我们,因为PG不是多线程。
针对mmap/munmap,这样做有很多改进。即使现在,在快速存储方面,使用open_datasync会更快(至少在O_DIRECT上)。WAL扩展是一个挑战性问题。不修改IO方式,有很多地方可以优化以提升性能:
1)将WALWriteLock分成两个锁:一个用于写,一个用于flush wal。现在,一个会话进行flush一个wal时,其他会话不能write wal,即使该wal位于其他段文件。没有必要这样做。
2)XLogFlush()中刷写WAL时不要将刷写请求的大小增加到最大(cf "try to write/flush later additions to XLOG as well" in XLogFlush()),可以显著减小OLTP负载。在SATA盘上有意义,但是对于SSD来说影响较小。写的多,持久锁时间就更长,增加了事务提交的延迟,组织更多的WAL写。
3)应该立即将所有的XLOG页的writes刷写会操作系统。现在,在OLTP负载中IO永远不会再commit之前出现,也就是说在XLogWrite()和commit之间的完全是在浪费时间。
做了这几点,猜想能有2-3倍的性能提升。但是不得不从根本上改变WAL write的IO方式。使用异步IO,可以像18k一样每秒持久化8kb的write。在我笔记本上,写4k,就是22k。
当然这和PG的wal flush没可比性,因为WAL 经常会重复写最后一个block。但不会记录到组提交里。
NVM WAL BUFFER的path:
0001-Preallocate-more-WAL-segments.patch (3K) Download Attachment
0002-Use-WAL-segments-as-WAL-buffers.patch(40K) Download Attachment
0003-Lazy-unmap-WAL-segments.patch (2K) Download Attachment
0004-Speculative-map-WAL-segments.patch (1K) Download Attachment
0005-Allocate-WAL-segments-to-utilize-hugepage.patch(1K) Download Attachment
3、是否可以模拟PM用于测试
通过“memmap=nnG!ssG”内核参数使用DRAM模拟PM。参考[1]和[2]了解详细模拟步骤。如果不起作用,检查PM和DAX的内核配置选项,比如CONFIG_FOOBAR,是否配置正确。
[1] How to Emulate Persistent Memory Using Dynamic Random-access Memory (DRAM)
[2] how_to_choose_the_correct_memmap_kernel_parameter_for_pmem_on_your_system
[3] Persistent Memory Wiki
https://nvdimm.wiki.kernel.org/
4、对于mmap WAL性能的测试
针对PG12分别进行修改。通过pgbench进行压测。SSD上存储WAL,map后的结果比原生PG性能差很多。VTune显示CopyXLogRecordToWAL的memcpy动作消耗的CPU时间比原生的大的多。使用NVDIMM-N,ext4-dax存储WAL,结果差不多,XLogInsert() 和XLogFlush()消耗的时间mmap和NVM WAL BUFFER的差不多。在PM上通过mmap WAL段文件作为WAL BUFFER是个很好的尝试。但是该path可能还有bug,并不能说性能提升或者下降。
5、作者的NVM WAL BUFFER测试
通过pgbench,指定不同的-c/--client和-j/--job,数据量规模因子s=50或者1000.结果如下:
Results (s=50) :
==============
Throughput [10^3 TPS] Average latency [ms]
( c, j) before after before after
------- --------------------- ---------------------
( 8, 8) 35.7 37.1 (+3.9%) 0.224 0.216 (-3.6%)
(18,18) 70.9 74.7 (+5.3%) 0.254 0.241 (-5.1%)
(36,18) 76.0 80.8 (+6.3%) 0.473 0.446 (-5.7%)
(54,18) 75.5 81.8 (+8.3%) 0.715 0.660 (-7.7%)
Results (s=1000)
================
Throughput [10^3 TPS] Average latency [ms]
( c, j) before after before after
------- --------------------- ---------------------
( 8, 8) 37.4 40.1 (+7.3%) 0.214 0.199 (-7.0%)
(18,18) 79.3 86.7 (+9.3%) 0.227 0.208 (-8.4%)
(36,18) 87.2 95.5 (+9.5%) 0.413 0.377 (-8.7%)
(54,18) 86.8 94.8 (+9.3%) 0.622 0.569 (-8.5%)
每个规模因子下,负载和延迟都有所改进。负载在(c,j)=(36,18)下TPS最高。S=1000案例下,提升百分比较大。规模因子大,对于同表和索引的竞争就小些,也就是加锁和解锁的操作较少。这种情况下WAL对性能更重要。
条件:
1)使用一个物理server,2个numa节点:PG绑定到node 0,pgbench到node 1,每个节点18个core,192GB的DRAM
2)PGDATA在nvme ssd上,pg_wal在交互6-in-1的NVDIMM-N上:都安装在server端,node 0,都用ext4,NVDIMM-N被mounted时有“-o dax”
3)新增nvwal_path和nvwal_size参数到postgresql.conf中
步骤:
对每个(c,j)都做三次,然后取平均值:
(1) Run initdb with proper -D and -X options; and also give --nvwal-path and --nvwal-size options after patch
(2) Start postgres and create a database for pgbench tables
(3) Run "pgbench -i -s ___" to create tables (s = 50 or 1000)
(4) Stop postgres, remount filesystems, and start postgres again
(5) Execute pg_prewarm extension for all the four pgbench tables
(6) Run pgbench during 30 minutes
pgbench command line
====================
$ pgbench -h /tmp -p 5432 -U username -r -M prepared -T 1800 -c ___ -j ___ dbname
I gave no -b option to use the built-in "TPC-B (sort-of)" query.
软件
========
- Distro: Ubuntu 18.04
- Kernel: Linux 5.4 (vanilla kernel)
- C Compiler: gcc 7.4.0
- PMDK: 1.7
- PostgreSQL: d677550 (master on Mar 3, 2020)
硬件
========
- System: HPE ProLiant DL380 Gen10
- CPU: Intel Xeon Gold 6154 (Skylake) x 2sockets
- DRAM: DDR4 2666MHz {32GiB/ch x 6ch}/socket x 2sockets
- NVDIMM-N: DDR4 2666MHz {16GiB/ch x 6ch}/socket x 2sockets
- NVMe SSD: Intel Optane DC P4800X Series SSDPED1K750GA
Patch
v2-0001-Support-GUCs-for-external-WAL-buffer.patch(36K) Download Attachment
v2-0002-Non-volatile-WAL-buffer.patch (53K) Download Attachment
v2-0003-README-for-non-volatile-WAL-buffer.patch(7K) Download Attachment
nvwal-performance-s50.png (39K) Download Attachment
nvwal-performance-s1000.png(40K)Download Attachmentpostgresql.conf (1K) Download Attachment
6、增加支持流复制
v4-0001-Support-GUCs-for-external-WAL-buffer.patch (42K) Download Attachment
v4-0002-Non-volatile-WAL-buffer.patch (73K) Download Attachment
v4-0003-walreceiver-supports-non-volatile-WAL-buffer.patch (7K) Download Attachment
v4-0004-pg_basebackup-supports-non-volatile-WAL-buffer.patch (25K) Download Attachment
v4-0005-README-for-non-volatile-WAL-buffer.patch (9K) Download Attachment
7、其他人对NVM WAL BUFFER测试
数据文件放到nvme ssd,WAL放到PM。使用下面两种方式存储WAL文件:
1)NVM WAL BUFFER的分支,利用libpmem
2)通过文件系统接口访问P,就是说将PM当做传统的块设备。都是APP DIRECT方式使用PM。
进行了两种insert场景:
1)插入小记录,记录长度为24字节
2)插入大记录,记录长度328字节
初衷是看下场景2)对于WAL写密集下性能提升多大,但是发现场景1)NVM WAL BUFFER和原生PG相比有大概5%的性能提升,但是在2)有大概20%的性能衰减。
分析后,XlogFlush函数可以通过NVM WAL BUFFER提升,但是会影响CopyXlogRecordToWAL的性能。可能和PM比DRAM的memcpy延迟高有关。下面是测试结果:
Scenario A (length of record to be inserted: 24 bytes per record):
==============================
NVWAL SoAD
------------------------------------ ------- -------
Througput (10^3 TPS) 310.5 296.0
CPU Time % of CopyXlogRecordToWAL 0.4 0.2
CPU Time % of XLogInsertRecord 1.5 0.8
CPU Time % of XLogFlush 2.1 9.6
Scenario B (length of record to be inserted: 328 bytes per record):
==============================
NVWAL SoAD
------------------------------------ ------- -------
Througput (10^3 TPS) 13.0 16.9
CPU Time % of CopyXlogRecordToWAL 3.0 1.6
CPU Time % of XLogInsertRecord 23.0 16.4
CPU Time % of XLogFlush 2.3 5.9
8、作者对于328字节性能衰减的回复
对于328字节,作者测试没有性能衰减,认为环境及安装步骤等可能不一样。
结果显示NVM WAL BUFFER比原始有更好性能:
步骤
在同一个机器上跑PG server和pgbench,分别绑定到不同的2个numa节点。Server端的numa节点用于PM和PCI SSD:
01) 创建PMEM namespace (sudo ndctl create-namespace -f -t pmem -m fsdax -M dev -e namespace0.0)
02) 在PM上做ext4文件系统并以dax方式mount(sudo mkfs.ext4 -q -F /dev/pmem0 ; sudo mount -o dax /dev/pmem0 /mnt/pmem0)
03) PCIE SSD也是ext4文件系统,正常mount(sudo mkfs.ext4 -q -F /dev/nvme0n1 ; sudo mount /dev/nvme0n1 /mnt/nvme0n1)
04) /mnt/pmem0/pg_wal 作为WAL目录
05) /mnt/nvme0n1/pgdata PGDATA目录
06) 执行initdb初始化 (initdb --locale=C --encoding=UTF8 -X /mnt/pmem0/pg_wal ...)
- Also give -P /mnt/pmem0/pg_wal/nvwal -Q 81920 in the case of Non-volatile WAL buffer
07) 在附件的postgresql.conf中删除nvmwal_*对于原始PG测试
08) PG server绑定到NUMA node 0 (numactl -N 0 -m 0 -- pg_ctl -l pg.log start)
09) 创建database (createdb --locale=C --encoding=UTF8)
10) 使用pgbench 初始化t,s=50 (pgbench -i -s 50)
11) ALTER TABLE pgbench_history ALTER filler TYPE character(300);)
-使表行大小为328 bytes
12) pg_ctl -l pg.log -m smart stop
13) 重新mount PMEM 和 PCIe SSD
14) numactl -N 0 -m 0 -- pg_ctl -l pg.log start
15) 执行 pg_prewarm 预热表 pgbench_* tables
16) 执行pgbench on NUMA node 1 30分钟(numactl -N 1 -m 1 -- pgbench -r -M prepared -T 1800 -c __ -j __)
- 执行默认的 tpcb-like事务
执行3次并取平均值。环境变量:
export PGHOST=/tmp
export PGPORT=5432
export PGDATABASE="$USER"
export PGUSER="$USER"
export PGDATA=/mnt/nvme0n1/pgdata
环境:
- System: HPE ProLiant DL380 Gen10
- CPU: Intel Xeon Gold 6240M x2 sockets (18 cores per socket; HT disabled by BIOS)
- DRAM: DDR4 2933MHz 192GiB/socket x2 sockets (32 GiB per channel x 6 channels per socket)
- Optane PMem: Apache Pass, AppDirect Mode, DDR4 2666MHz 1.5TiB/socket x2 sockets (256 GiB per channel x 6 channels per socket; interleaving enabled)
- PCIe SSD: DC P4800X Series SSDPED1K750GA
- Distro: Ubuntu 20.04.1
- C compiler: gcc 9.3.0
- libc: glibc 2.31
- Linux kernel: 5.7 (vanilla)
- Filesystem: ext4 (DAX enabled when using Optane PMem)
- PMDK: 1.9
- PostgreSQL (Original): 14devel (200f610: Jul 26, 2020)
- PostgreSQL (Non-volatile WAL buffer): 14devel (200f610: Jul 26, 2020) + non-volatile WAL buffer patchset v4
作者按照Gang的环境配置,得到原始PG比NVM WAL BUFFER性能好的结果。需要进一步分析。NVM WAL BUFFER的XLogInsert花费的时间较长。这个拖累了XLogFlush带来的提升,整体上性能衰减了。VTune显示NVM WAL BUFFER的XLogInsert => XLogInsertRecord => CopyXLogRecordsToWAL =>中memcpy花费的时间较长,XLogFlush时间较短。整体结果和Gang一致。PM上的WAL BUFFER相对于DRAM来说,memcpy WAL记录时间长,因为现阶段PM延迟比DRAM大。作为回报,NVM WAL BUFFER减小了让记录命中设备的时间,因为不需要将他们从缓冲区写到其他地方,只需要将CPU cache中内容持久化到NVM。会继续跟踪。
原文
https://www.postgresql-archive.org/PoC-Non-volatile-WAL-buffer-td6120484.html