瞭解機制。
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)
https://software.intel.com/en-us/articles/how-to-emulate-persistent-memory-on-an-intel-architecture-server
[2] how_to_choose_the_correct_memmap_kernel_parameter_for_pmem_on_your_system
https://nvdimm.wiki.kernel.org/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 Attachmentv2-0002-Non-volatile-WAL-buffer.patch (53K) Download Attachmentv2-0003-README-for-non-volatile-WAL-buffer.patch (7K) Download Attachmentnvwal-performance-s50.png (39K) Download Attachmentnvwal-performance-s1000.png (40K) Download Attachmentpostgresql.conf (1K) Download Attachment
6、增加支援流複製v4-0001-Support-GUCs-for-external-WAL-buffer.patch (42K) Download Attachmentv4-0002-Non-volatile-WAL-buffer.patch (73K) Download Attachmentv4-0003-walreceiver-supports-non-volatile-WAL-buffer.patch (7K) Download Attachmentv4-0004-pg_basebackup-supports-non-volatile-WAL-buffer.patch (25K) Download Attachmentv4-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
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