これはなに
MySQLサーバでswapが出てレプリケーション遅延が起こっていた。その原因を考えた時のメモ(結構昔だけど、掘り起こして少し整理)。
トラブル発生時の状況
swap 全食いつぶしされていた(swapは8Gの設定)。swap が単に発生したというよりも、swap in/out が沢山あったのが問題。3列目がページイン(swapin)、4列目が ページアウト(swapout) 。単位は kbyte/s。
$ sar -f /var/log/sa/sa16 -B | grep '11:20' -A 10 11:20:01 AM 19.96 1528.86 227.95 0.23 306.29 0.00 0.00 0.00 0.00 11:30:01 AM 32.78 1060.79 284.00 0.41 418.23 57.04 2.05 53.95 91.29 11:40:01 AM 35.41 918.87 288.44 0.32 613.35 156.52 4.00 154.15 96.03 11:50:01 AM 30.93 931.70 337.14 0.23 635.15 129.01 3.78 125.92 94.82 12:00:08 PM 1090.31 66591.60 1161.96 38.31 18141.11 264214.86 998791.30 17389.23 1.38 12:10:04 PM 16094.34 73548.50 1853.48 65.37 23471.29 248716.54 1527936.86 22704.65 1.28 12:20:05 PM 25803.10 25574.29 2219.17 183.17 13001.47 45104.96 88174.19 12253.92 9.19 12:30:07 PM 6338.98 27665.28 1886.63 201.32 8700.89 42764.03 85559.13 7997.71 6.23 12:40:05 PM 6489.96 22360.17 1541.27 204.05 7111.80 38805.93 31339.39 6642.17 9.47 12:50:01 PM 7012.49 16050.09 730.82 80.87 14578.10 23819.42 35602.05 3570.47 6.01 01:00:01 PM 0.03 1.32 43.08 0.01 28.28 0.00 0.00 0.00 0.00
cpu 使用率 system 30% と I/o wait 20 % くらいでていた。User は数%程度でそこまで大きく変わらず。
対策
swappiness 設定
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Performance_Tuning_Guide/s-memory-tunables.html anonymous memory か page cache かどちらを優先するのかの度合。値が低ければ スワップアウトを避ける(anonymous memoryをスワップに出さずに、page cacheをディスクに書き戻すのを優先する)。 DBだと低めの値を設定するのが推奨のよう。 RHEL 6.4 からは swappiness = 0 はさらに強くswapout しないような仕様になっているそうなので注意が必要。Oracle Databaseで10が推奨とのこと。
anonyamous memory って何? page cacheとの関係は?
http://nopipi.hatenablog.com/entry/2015/09/13/181026 無名ページ —>メモリが足りない時スワップアウト(スワップへ退避)され、物理メモリから解放されるページ ファイルページ —> メモリが足りない時ディスク同期し解放されるページ スワップする/しない、の差。 どういうこっちゃ? もうちょっと調べる。
http://enakai00.hatenablog.com/entry/20110906/1315315488
http://mkosaki.blog46.fc2.com/blog-entry-884.html
MemTotal = MemFree + File-backedなメモリ + Anonymousなメモリ + カーネル空間が使うメモリ
File-backed(file pageやpage cacheとも)というのは、ディスクからメモリに読み込んだファイルなど、メモリを開放したくなったら、その内容をディスクに書き戻せば開放できるタイプのメモリ。ファイルキャッシュ、バッファ、プロセスのテキスト領域(プログラム本体)
Anonymousというのはそれ以外のメモリで、メモリを開放したくなったら、Swap領域に書き出さないと開放できないタイプのメモリ。プロセスのメモリ(データ/スタック領域)、共有メモリ、tmpfsとか。
maria DBでのswappiness 推奨設定
https://mariadb.com/kb/en/mariadb/configuring-swappiness/ swappiness = 1 がよい。 そもそも メモリがswapであることを想定したアルゴリズムではない、とのこと。mysql でも同様か? 値の参考には出来そう。
結局どのような設定にすべきか?
innodb buffer pool 内に書かれているものは、ディスクにそのまま書き戻せるものではなくて、検索結果や更新途中の処理のデータが乗っている。つまり、mysqldによって作られたanonymous memoryとして持たれていると思われる。anonymous memory は以下のように確認する。18G ちょっとある(データはトラブルの起こっていない他のDB)。
$ grep anon /proc/meminfo -A 2 Active(anon): 19021848 kB Inactive(anon): 1724228 kB Active(file): 1416160 kB Inactive(file): 1705648 kB
バイナリログ/その他ログなどはfile cacheへ置かれていると考えられ、これらがメモリ内に共存することになる。
ここで swappiness が高い(デフォルト60)だと、file cache とanonymous memory のどちらも物理メモリ上に残そうとするため、物理メモリがあふれるとfile cacheの一部がディスクに戻ると共に、anonymous memoryの一部がswapに書きだされる。kernel の mm/vmscan.c を読むと、fille_prio = 200 - anon_prio (デフォルトなら file:anon = 140 :60)となっており、LRU(使ってないものから追い出す)に基づいて追い出す模様(深くは追えていない)。
一方 swappiness が低い(例えば設定値1の)場合には、anonymous memoryを物理メモリ上に置いておく優先順位が高くなり、file cacheが優先的にディスクに戻される。すなわり、swapは起こりにくくなる。
DBにおいてswap が発生するのと、file cacheが揮発するのとどちらがサーバ全体に有利かという点を考える。innodb buffer pool が swap に書きだされるか、主にその他ログファイルがディスクに戻るかとの違い。
file cache に乗る可能性のあるもの。ログファイルの大きさは innodb_log_file_size = 128M であり、そこまで大きくない。slow query log は(肥大化しているとはいえ)300M 以下。DBサーバにおいてメモリに必ず乗っていて欲しい大きなファイルなどは他にない。他に数G単位のがあるならbuffer pool sizeチューニングの話のところで出てくるはずだし、Active(file)あたりにも現れるはず。テーブルスペースから buffer pool にのせる前にfile cache にのせることがある。この buffer pool にのせる前のfile cache については innodb_flush_method 設定で抑制できそう。
結論: 単に各種ログファイル等が開かれることにより乗った古い file cache がディスクに書き戻されても差支えないと考えれば、swap が出るよりは file cacheに乗っているデータをファイルに戻してもらった方がよい。
innodb_flush_method = O_DIRECT設定
設定の意味は? https://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_flush_method データファイルを開くときには O_DIRECT を用いて、flushするときにはfsync() を用いるというオプション。ただし、fsyncするときもOSはデータキャッシュを行わない。 デフォルトでは innodb データファイルから、一回ファイルシステムキャッシュを経由して innodb buffer pool にデータを読みだす。 O_DIRECT設定をいれると、データファイルから直接buffer pool に読み込むことで、システムによるキャッシュとRDBMSによるキャッシュの重複を無くすという感じ。buffer pool にキャッシュされているデータは、まったく内容が同じというわけではないが役割がかぶるので、それだけを使う。
デメリットはあるのか?
http://d.hatena.ne.jp/sh2/20101205 性能を上げるというよりも、メモリ使用量を制限するというためのものだと考えた方がよさそう。 どういうときに入れてもいいのかという話は 「High Performance MySQL」に書かれていた。https://books.google.co.jp/books?id=JXFuCQAAQBAJ&pg=PA363&lpg=PA363&dq=O_DIRECT+raid+write+back&source=bl&ots=8oVbZYwCdj&sig=4vpbTujup-BfTM0bRACIsnOLcA0&hl=ja&sa=X&ved=0ahUKEwi6qdrEseDMAhVBpJQKHVK9D-4Q6AEIHjAA#v=onepage&q=O_DIRECT%20raid%20write%20back&f=false この設定はOSによるキャッシュは無効化するが、RAIDカードによる read-ahead は無効化しない。O_DIRECT を有効化してかつパフォーマンスを落ちないようにする方法は、RAIDカードによる write-back設定をいれたwrite cache が必要。InnoDBと実際のストレージとの間にバッファがなければ、パフォーマンスは劇的に悪くなる。 弊社で使用しているサーバは write-back 設定を入れてあるはずなので大丈夫(構築時に一応確認するが、大体デフォルトで入っている)。 また、設定を入れるとMysqlを起動した直後のパフォーマンスが悪くなりそう。OSがキャッシュしていれば起動後にdiskから直接読みださなくてよいので早くなる。これは、OS再起動後とかだとどちらにせよOSによるファイルキャッシュも消えていると思うので気にしてもしょうがなさそう。
設定手順
vm.swappiness = 1の設定を入れる
sudo su - sysctl vm.swappiness # 確認 sudo cp /etc/sysctl.conf /var/tmp/sysctl.conf sudo vim /etc/sysctl.conf # 末尾に vm.swappiness=1 を追記 diff /var/tmp/sysctl.conf /etc/sysctl.conf sudo sysctl -p # 設定を反映 sysctl vm.swappiness # 反映を確認
innodb_flush_method = O_DIRECT の設定をいれる
[mysqld]ブロックの最後にでも以下の記述を追加する
innodb_flush_method=O_DIRECT
mysql restart する
$ sudo service mysql restart
設定確認する
mysql -uroot -p -e "show global variables ;" | grep innodb_flush_method