XLOG_BLCKSZ のデフォルト値は 8KB (BLCKSZと同じ) なのですが、 PostgreSQL のトランザクションログのフラッシュコードを覗いてみると、この値はトランザクションログ書き出し時の最小単位として利用されます。
これは、たとえばトランザクションログに100バイト分のデータをコミットしたいときでも XLOG_BLCKSZ が 8KB がであれば 8KB 分の write() が発生しフラッシュされ、もし運悪くログが境界をまたいでしまうようなケースでは XLOG_BLCKSZ の n 倍の書き込み、つまり 16KB などの書き込みが発生することになります。
また、コードを見る感じだと PostgreSQL のトランザクションログ書き込みは、同じページを繰り返しフラッシュします。たとえば、とあるページに 100バイト、100バイト、100バイトのトランザクションを毎回フラッシュするということは、同じページ(ファイル内のオフセット)に対して3回 write() されフラッシュする実装になっているようです。
フラッシュベースのストレージを使っていると書き込み量はデバイスの利用期間に影響しますので、8KBは(シークコストが大きかったディスクの時代ならともかく)少なくとも、現時点のフラッシュの時代にはちょっと大きすぎるかなあと思って XLOG_BLCKSZ を小さくコンパイルしなおしてみました。
XLOG_BLCKSZ は src/include/pg_config.h に定義されていますので、その値を2のn乗(ただし1024以上)に設定します。今回はデフォルトの8192, 今頃のフラッシュデバイスでよくありがちな4096、そして512は指定できないけども1024にして試してみました。
なお評価は以下のようなスクリプトを作成して実行し、 diskstats からブロックデバイスへの書き込み量をモニタします。ついでにフラッシュへの物理書き込み量も同時にとっていますが、ここでは評価しません。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
su - postgres -c "pg_ctl stop" | |
rm -rf /pgdata/* | |
su - postgres -c "initdb" | |
cp ~/postgresql.conf /pgdata/ | |
su - postgres -c "pg_ctl start" | |
sleep 3 | |
createdb -U postgres test | |
sleep 1 | |
./pgbench -c 80 -T 100 -j 8 -U postgres -i test | |
sleep 1 | |
vacuumdb -U postgres test | |
sleep 1 | |
su - postgres -c "pg_ctl stop" | |
sleep 5 | |
cat /proc/diskstats | grep fioa | |
fio-status -a | grep "Physical bytes"| sed -e 's/,//g' | |
su - postgres -c "pg_ctl start" | |
sleep 3 | |
./pgbench -c 80 -T 100 -j 8 -U postgres test | |
su - postgres -c "pg_ctl stop" | |
sleep 5 | |
cat /proc/diskstats | grep fioa | |
fio-status -a | grep "Physical bytes"| sed -e 's/,//g' |
グラフを見ていただくと判るとおり XLOG_BLCKSZ を 8192 から 4096 に変更するとストレージへの書き込み量が24%ほど削減できました。この効果は実際のアプリケーションでどれぐらいの大きさのログが定常的に書き込まれるかによって変わると思いますが、一定の効果はありそうです。
XLOG_BLCKSZ = 1024 ではさらに書き込み量の減少は確認できましたが、しかし4096の場合からの差を見る限り、1024まで下げてもそれほどのインパクトはなさそうです。最近のファイルシステムの書き込み単位は1ページ4KBぐらいでしょうし、そもそも4KBを切るような書き込みは苦手なフラッシュストレージも多いので、とりあえず4096が落としどころな値ではないでしょうか。
さて、気軽に XLOG_BLCKSZ を変えてみたのですが、トランザクションログのフォーマットが変化するためデフォルトの設定のデータベースファイルが使えなくなります(トランザクションログだけ?それとも全体を?)データベースファイル群を initdb で初期化する必要があります。またトランザクションログを利用するレプリケーションなどの互換性にも影響があるのかもしれません。
追記
上記の内容はブロックサイズ 4096B でフォーマットされた(最小単位が 4K バウンダリな) ioMemory PX600 を利用して行っていました。 XLOG_BLCKSZ = 1024 を試すならフォーマットを 512B に変更すべきでした。すみません。ということで改めて XLOG_BLCKSZ = 1024 の値を調べてみたところ、 4096B フォーマット上の結果と比べると大幅に書き込み量が減りました。
先の結果との差は、ファイルを書き込むときに、 XFS がパディングしたものと考えられます。
結果として、 XLOG_BLCKSZ = 8192 (デフォルト)の状態、すなわちトランザクションログのブロックサイズ 8192B で特定のワークロードを流した場合と比べ、 XLOG_BLCKSZ = 1024 (最小値)でワークロードを流した場合では、書き込み量が約半分になることがわかりました。
先にも書きましたが 4096B 未満の I/O はフラッシュストレージの特性上性能が出しづらいケースも考えられる(仮に512BアクセスできたとしてもCopy-Modify-Writeを行っている)ほか、ホスト側からの書き込み量に対してデバイス内でより大きな Physical Write が発生しているケースもあるので Host write の値にとらわれるのは危険です。 ioMemory シリーズのような 512B 単位の I/O もネイティブに扱えるデバイスなら 1KB などのアグレッシブなトランザクションログ ブロックサイズを利用し、フラッシュの寿命を約2倍に伸ばせそうです。