Linuxページキャッシュの設定を変更してWrite I/Oをチューニングしたメモ

環境

以下のCentOS7環境で検証しています。

$ uname -r
3.10.0-327.13.1.el7.x86_64

設定パラメータ

Linuxのページキャッシュの書き出しを設定する主なパラメータは以下です。

バッファをバイパスするI/O(O_DIRECTなど)例外を除いて、通常の書き出し処理は一旦ダーティーページに書かれて後から遅延書き込みされます。

$ sysctl -a | grep dirty

vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 40
vm.dirty_writeback_centisecs = 500
パラメータ名 内容
vm.dirty_background_ratio 単位はパーセンテージ。ダーティーなページの割合がこの値に達すると優先度の低いバックグラウンドでpdflushが書き出し動作する。
vm.dirty_ratio 単位はパーセンテージ。ダーティーなページの割合がこの値に達すると優先度の高いフォアグランドでpdflushが書き出し動作する。
vm.dirty_bytes vm.dirty_ratioとほぼ同じ。バイト単位で細かくチューニングしたい場合使う。0は無効化。
vm.dirty_background_bytes vm.dirty_background_ratioとほぼ同じ。バイト単位で細かくチューニングしたい場合使う。0は無効化。
vm.dirty_writeback_centisecs pdflushスレッドの起動間隔。単位は1/100秒。0にするとpdflushの定期実行は無効化される。
vm.dirty_expire_centisecs ダーティーページの保持期間。この期間を超えると次回のpdflushで書き出される。単位は1/100秒。

CentOS7などtunedが有効化されている環境は単純にsysctlで変更するだけでは設定値が反映されないので注意です。

CentOS7再起動でsysctlで設定したカーネルパラメータが反映されないのはTunedが原因だった - YOMON8.NET

計測ツール

1 ダーティーページ状況の計測ツール

ダーティーページの状況や、書き出し状況は以下のワンライナーで計測しています。

while true; do sleep 1; cat /proc/{vmstat,meminfo}  | egrep "^nr_dirty|^nr_writeback" | awk 'BEGIN{print strftime("%H:%M:%S\t")}{print $2 "\t"}' | tr -d "\n"; echo ""; done
出力項目

タブ区切りで以下の項目を出力します。

列番 項目 内容
1 date 時間
2 nr_dirty ダーティページの数
3 nr_writeback ディスクにライトバック(書き戻された/書き出された)ページの数
4 nr_writeback_temp writeback時に使われることがある一時的なバッファ。ほとんど計測値に表れないと思います。
5 nr_dirty_threshold ダーティーページ数がこの値を超えるとpdflushによる書き込みがフォアグラウンドで走る。vm.dirty_ratioまたはvm.dirty_bytesで調整可能。
6 nr_dirty_background_threshold ダーティーページ数がこの値を超えるとpdflushによる書き込みがバックグラウンドで走る。vm.dirty_background_ratioまたはvm.dirty_background_bytesで調整可能。
出力フォーマットと出力例

フォーマット

date nr_dirty nr_writeback nr_writeback_temp nr_dirty_threshold nr_dirty_background_threshold

出力例

16:19:36    37  0   0   147729  49243
16:19:36    38  0   0   147897  49299
16:19:37    40  0   0   148050  49350
16:19:38    41  0   0   148062  49354
16:19:39    41  0   0   148052  49350

2 時間付きvmstat

説明は省略 dstat -tv とかでもOK。特に注目するのは bowa の項目。

vmstat 1 | awk '{print strftime("%H:%M:%S\t"),$0}'

3 fio(I/O負荷発生ツール)

I/O負荷をかけるにはfioが便利です。こちらも詳細は省きますが、コマンド例です。 direct=1 とか fsync=1 のオプションを付けて動きの違いを見るとわかりやすいかもしれないです。

fioが入っていない環境の場合はepelからインストールしてください。dd使っても同じような検証は可能です。

fio -filename=/your/file/path -rw=randwrite -bs=4k -size=3G -numjobs=15  -group_reporting -name=job01 -engine=libaio

また上記に合わせて同時に iostat -txm 1 とかも取得すると動きが良くわかります。

観測

fioを実行しながら、上で挙げたダーティーページ状況の計測ツールを使って状況を観測してみます。

11:04:54 20 0  0  418745 139581
11:04:55 106072 0  0  401404 133801
11:04:56 190426 1193   0  400360 133453 #<- nr_dirtyがnr_dirty_thresholdまたはnr_dirty_background_thresholdを超過し書き込みが始まる
11:04:57 245946 8425   0  400152 133384
11:04:58 248485 16430  0  400097 133365
11:05:00 244314 28352  0  400123 133374
11:05:01 241633 34827  0  400224 133408 #<- この辺りでfioによる書き込みは終わっている
11:05:02 165506 29078  0  400153 133384
11:05:03 119912 2008   0  418430 139476 #<- nr_dirtyがnr_dirty_background_thresholdを下回り書き込みが終了
11:05:04 119914 0  0  418336 139445 #<-nr_writebackが0(書き込み無い状態)が続く
11:05:05 119915 0  0  418353 139451
11:05:06 119916 0  0  418360 139453
11:05:07 119917 0  0  418347 139449
11:05:08 29023  738    0  418304 139434 #<-dirty_writeback_centisecsの5秒が経過してpdflushが再度起床されて書き込みが動く
11:05:09 17717  420    0  418124 139374
11:05:10 7021   577    0  418113 139371
11:05:11 30 79 0  418156 139385 #<-dirty_pageが少なくなりdirty_bytesやdirty_expire_centisecsに沿って処理される
11:05:12 13 0  0  418176 139392
11:05:13 15 0  0  418171 139390

実験

理解を深めるために例えばこんな極端な設定にしてみます。

$ sysctl -a | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 89
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 90
vm.dirty_writeback_centisecs = 0

ダーティーページが溜まっても dirty_ratiodirty_background_ratio で設定した閾値に届かないのでpdflushが起床されません。

dirty_expire_centisecs によって30秒経ったダーティーページは書き出し対象となりますが、dirty_writeback_centisecs が0と設定されているのでpdflush定期実行もされず、ずっとメモリ上に残った状態となります。

13:17:00 786481 0  0  1018808    1007488
13:17:01 786484 0  0  1018476    1007159
13:17:02 786488 0  0  1014818    1003542
13:17:03 786490 0  0  1015298    1004017
13:17:04 786490 0  0  1014252    1002982
13:17:05 786492 0  0  1015057    1003779
13:17:06 786494 0  0  1015271    1003990
13:17:07 786496 0  0  1014851    1003575

当然vmstatで確認してみてもboの部分の値見ても書き込み行われてないです。(下の例はmysqlがページキャッシュをスルーしてDirect IOしている値が少し出てしまってますが。。)

             procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
              r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
13:16:59      0  0      0 394360    948 5473796    0    0     0     6  613  718  8  7 86  0  0
13:17:00      0  0      0 391196    948 5473776    0    0     0     6  325  516  4  1 96  0  0
13:17:01      1  0      0 386808    948 5473708    0    0     4    15 1559 1751 32  5 64  0  0
13:17:02      1  0      0 381108    948 5473748    0    0     0    14 2093 1623 55  9 37  0  0
13:17:03      1  0      0 380324    948 5473756    0    0     4    11 2032 1550 55  7 38  0  0
13:17:04      2  0      0 375196    948 5473740    0    0     0     7 2193 2022 50 10 40  0  0
13:17:05      1  0      0 377568    948 5473744    0    0     0     9 2244 2184 48 12 40  0  0
13:17:06      0  0      0 380456    948 5473752    0    0     0     9 1737 2062 30  7 64  0  0
13:17:07      1  0      0 377244    948 5473756    0    0     0     7 1672 1786 32  7 62  0  0

sync コマンド売ってみると一気に書き込みが始まります。

$ sync

残ってたダーティーページが一掃されました。

13:25:32 787079 0  0  1018037    1006726
13:25:33 660539 41994  0  1018018    1006707
13:25:34 617478 43034  0  1017894    1006584
13:25:35 486411 44049  0  1017872    1006562
13:25:36 351217 46082  0  1017941    1006630
13:25:37 208905 48130  0  1017923    1006613
13:25:38 58387  51199  0  1017256    1005953
13:25:39 2  0  0  1019179    1007855

vmstatでもboの列にディスクに書き出した値が出ています。

             procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
              r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
13:25:32      r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
13:25:32      1  0      0 386516    948 5476288    0    0     0     6  323  510  3  1 97  0  0
13:25:33      1  1      0 386116    948 5476544    0    0     0 481326 1345  716  5  5 62 28  0
13:25:34      0  2      0 386164    948 5476680    0    0     0 179720 1100  593  5  2  0 93  0
13:25:35      0  2      0 386256    948 5476684    0    0     0 489996 2308  608  3  4  0 93  0
13:25:36      0  2      0 385728    948 5476688    0    0     0 526860 1762  696  7  5 42 46  0
13:25:37      0  2      0 385820    948 5476664    0    0     0 560140 1453  547  4  5 48 43  0
13:25:38      0  2      0 383104    948 5476668    0    0     0 505868 1510  723  6  5 46 44  0
13:25:39      0  0      0 391212    948 5476668    0    0     0 404499 1366  642  4  3 53 40  0
13:25:40      0  0      0 389420    948 5476588    0    0     4    10  437  674  6  1 93  0  0
13:25:41      0  0      0 389348    948 5476560    0    0     0     7  313  524  3  0 97  0  0

このようにパラメータを動かしながら実験繰り返すと動きが理解しやすいと思います。

チューニング例

デフォルト値

vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 40
vm.dirty_writeback_centisecs = 500

いくつかあるパラメータの中でも、特にポイントとなるのはvm.dirty_ratiovm.dirty_background_ratio です。

また、チューニングの前にI/Oネックになっているアプリケーションの挙動はperfなどのツールを使ってチェックしておく必要があります。O_DIRECTでアクセスしていないかや、fsyncのタイミングなどはチェックしておかないとそもそもの動きが把握できなかったりします。

ダーティーページの最小化

調べて見ると、これが一番よく見られるチューニングです。

pdflush起動までの閾値( dirty_ratio , dirty_background_ratio )を両方下げます。これによりダーティーページが積極的にディスクに書き出されて、OS上に残ることが少なくなります。

例えば以下のようなメリットがあると考えられます。 ・電源障害時などの被害を最小化 ・ページキャッシュの解放

ストレージ側にも大きなキャッシュがある場合などは、より有効な可能性があります。

設定例

vm.dirty_ratio = 20                     #default : 40
vm.dirty_background_ratio = 5  #default : 10

他にも vm.dirty_writeback_centisecs を短くすることも検討の余地ありです。

キャッシュ有効活用メモリ上に乗せたまま高速処理

上とは逆にpdflush起動までの閾値( dirty_ratio , dirty_background_ratio )を両方上げます。

こうすることでメモリ上にダーティーなページを溜め込む形になります。その分一回の書き出しは大きくなります。メモリ上置いたまま高速に処理を回すことができるアプリケーションなら検討の余地あるかもしれません。

設定例

vm.dirty_ratio = 60                        #default : 40
vm.dirty_background_ratio = 20  #default : 10

他にも dirty_writeback_centisecsdirty_expire_centisecs を上げたりも検討可能です。

電源障害時などはより多くのデータがロストすることに注意です。

局所的なI/O負荷を受け流したい

普段はメモリだけで処理して、ほとんどI/O発生しないが、局所的にはI/O負荷が高くなるシステムの場合など。 dirty_ratio を上げて、 dirty_background_ratio を下げる方法があります。

これにより、ダーティーなページは裏側バックグラウンドで書き出されることになります。局所性によるI/Oの待ち行列を慣らしてあげることが可能です。

dirty_background_ratio で起床したバックグラウンド処理の場合はデータ書き出しに想像以上の時間がかかる場合があるので、念入りに検証が必要です。

大きな書き込みがあると一気に dirty_ratio閾値に到達してしまい意図した動作をしない場合があります。その場合は、 dirty_ratio の値をさらに上げてみたり、 dirty_writeback_centisecs を下げて処理書き出し間隔を短くしたり、ファイルなどの書き出し帯域を絞ってバックグラウンドが書き出す時間を確保してみたり調整が必要です。

vm.dirty_ratio = 60                        #default : 40
vm.dirty_background_ratio = 5    #default : 10

この手法も電源障害時などはより多くのデータがロストすることに注意です。

参考URL

linux/vm.txt at 6e5c8381d1db4c1cdd4b4e49d5f0d1255c2246fd · torvalds/linux · GitHub

Better Linux Disk Caching & Performance with vm.dirty_ratio