74
5.6 以前の InnoDB Flushing 瀬島 貴則 revision 2

5.6 以前の InnoDB Flushing

Embed Size (px)

Citation preview

Page 1: 5.6 以前の InnoDB Flushing

5.6 以前のInnoDB Flushing瀬島 貴則

revision 2

Page 2: 5.6 以前の InnoDB Flushing

免責事項

- 本資料は個人の見解であり、私が所属する組織の見解とは必ずしも一致しません。

- 内容の一部に偏ったものがあるかもしれませんが、各自オトナの判断でよろしくお願いします。

Page 3: 5.6 以前の InnoDB Flushing

自己紹介

- わりとMySQLでごはんたべてます- 3.23.58 あたりから使ってます

- 一時期は Resource Monitoring もよくやってました

- Twitter: @ts4th

Page 4: 5.6 以前の InnoDB Flushing

今日のお題

- SSDはたくさん書いたら壊れます- checkpoint や InnoDB Adaptive Flushing や

double write buffer によって発生する書き込み処理を踏まえ

- 要件を整理し- 減らせるところは書き込みを減らして- SSDの寿命を延ばせないか考えます

Page 5: 5.6 以前の InnoDB Flushing

InnoDB Adaptive Flushing

- かつてInnoDB Plugin 1.0.4 で追加されました- MySQL5.5 でInnoDB本体に組み込まれました- 当時としては、わりと画期的だったんですが- 5.6 でかなり良くなりました- 次の資料から図を拝借すると、現状こうです

- MySQL Performance: Demystified Tuning and Best Practices [CON5097]

Page 6: 5.6 以前の InnoDB Flushing
Page 7: 5.6 以前の InnoDB Flushing

一つ一つ

見て行きましょう

Page 8: 5.6 以前の InnoDB Flushing

InnoDBのファイル群

- ibdata1- internal data dictionary・・・tableやindexのメタデータ- double write buffer・・・耐障害性上げるためのバッファ- undo log・・・rollback segment

- ib_log_file*- transaction log・・・ redo logs、 write ahead log

- *.ibd- table space・・・ table のdataや index

Page 9: 5.6 以前の InnoDB Flushing

redo log(write ahead log)

- 更新内容は先ず log に書いて、 table space にはあとでゆっくり反映

- 他のRDBMS でも用いられる手法- logへの書き込みは sequential write になるの

で、HDDでも速い- table space に反映するとき更新処理がまとめ

られることも。 write combining

Page 10: 5.6 以前の InnoDB Flushing

Log Sequence Number(LSN)

- transaction log(ib_log_file*) のサイズは InnoDB の起動時に innodb_log_file_size で指定して決める(固定長)

- transaction log は ring buffer というか cyclic というか circular fashion

- InnoDB が transaction log を初期化して以降、 log の buffer に書いてきたバイト数が Log Sequence Number。

Page 11: 5.6 以前の InnoDB Flushing

oldest_modification

- page には oldest_modification というメンバ変数がある

- その page が dirty になった最初のイベントが書かれている log の position(LSN) が oldest_modification

- dirty page をflushしてdiskに同期させると、その page の oldest_modification は 0 でリセット

Page 12: 5.6 以前の InnoDB Flushing

Last checkpoint at

- buffer pool 上にある更新内容を、LSN 的に *.ibd にどこまで書きだしたか示すものが Last checkpoint at。

- SHOW ENGINE INNODB STATUS で見えるやつ

Page 13: 5.6 以前の InnoDB Flushing

write combining

- Last checkpoint at の進み方と disk I/O は必ずしも比例しない

- 同一の page への更新は まとめて disk に flush できる- 同一の page に格納されている複数の row

への更新を、一回で disk に書けることも- log に溜めこみ、まとめて flush できるとお得

Page 14: 5.6 以前の InnoDB Flushing

図に描くとこう

Page 15: 5.6 以前の InnoDB Flushing

InnoDB Adaptive Flushing

- log は有限なので使いきってはいけない- 具体的に言うと、(LSN - Last checkpoint at) が

max_modified_age_sync(75%くらい)を超えると強制的に dirty page の flush 走る- 75%というのは定数ではなく、 log/log0log.cc の

log_calc_max_ages() で計算してる

- なるべく強制 flush 走らないように、 adaptive に flushing する機能

Page 16: 5.6 以前の InnoDB Flushing

InnoDB Adaptive Flushing ない時代

- InnoDB Adaptive Flushing がない頃、いずれかの状況にならないと flush されにくかった- buffer pool 上で dirty page の比率が

innodb_max_dirty_pages_pct(昔はdefault90、いまは default 75)を超える

- redo log の使用率が max_modified_age_sync 超える

Page 17: 5.6 以前の InnoDB Flushing

原初の InnoDB Adaptive Flushing

- 5.1や5.5 の InnoDB Adaptive flushing は、 master_thread という background で動いてる thread が、 innodb_io_capacity に応じて 他のタスクの合間に flush してた- むかしの master_thread は goto たのしいのでいちどよ

んでみるとよいです

Page 18: 5.6 以前の InnoDB Flushing

5.5以前のinnodb_io_capacity

- io_capacity は正確にいうと iops ではない- master thread が一秒間にいくつ page の i/o

してもいいかという閾値- 昔は、(select が多いなどして) disk read が多

いときは、 dirty page の flush を手加減してた- 例えば、ピークタイムに dirty page をあまり flush しない

ことがあったとしたら、それは read が多いことが原因

だったり

Page 19: 5.6 以前の InnoDB Flushing

- 5.5 以前の InnoDB Adaptive Flushing は、 io_capacity の範囲内で flush してたので- 高速なストレージだからといって io_capacity 上げすぎ

ると、 write combining が効かない- redo logを溜めずにつどつどflushしてしまう

Page 20: 5.6 以前の InnoDB Flushing

もったいない

Page 21: 5.6 以前の InnoDB Flushing

ここでもう一度、 Dimitri さんの図を

思い出してみましょう

Page 22: 5.6 以前の InnoDB Flushing
Page 23: 5.6 以前の InnoDB Flushing

5.6 で何が変わったのか

- 公式ドキュメントとコードの乖離が激しくなった- コード読もう- InnoDB team blog には意外と説明が書いてある

- back ground thread の種類が劇的に増えた- 5.5以前だと master_thread でこなしていたタスクが複

数の thread で分散

- purge や dirty page の flush など特定のタスクが重

い場合、他のタスクがそれに引っ張られてしまうケー

スがあったが、それが軽減された

Page 24: 5.6 以前の InnoDB Flushing

- 関連する parameter が増えた- 重要なのはこのへん

- innodb_adaptive_flushing_lwm(lwmは low water mark の略)

- innodb_io_capacity- innodb_io_capacity_max

- MySQL5.6 の公式ドキュメントに書いてある io_capacity 関係の記述は説明不足感

- innodb_max_dirty_pages_pct_lwm

Page 25: 5.6 以前の InnoDB Flushing

page cleaner thread

- MySQL5.5までは、 InnoDB で master thread と呼ばれる thread がいろいろやってた- dirty page の flush- change buffering- purge

- 5.6 でこれらの機能が複数の thread に分割- 5.6 で追加された thread の一つが page

cleaner thread blogだと このへん

Page 26: 5.6 以前の InnoDB Flushing

flush list と LRU list

- buffer pool 上の page は、昔から二種類の list で管理されている

- flush list は更新された順(oldest_modification順)に dirty page を管理

- LRU list はすべてのpage を対象に、参照された順に管理

- 5.6 ではpage cleaner thread がこれらに対してback ground でタスクをこなす

Page 27: 5.6 以前の InnoDB Flushing

5.5以前の LRU list

- 5.5 以前で LRU list が参照されるケースの一つは、 buffer pool の page があふれたとき- disk 上のデータを読み込むとき、最も参照されていない

page を破棄

- もし、そのpageがdirtyだったら、diskにflushしてから破

棄- foreground の thread でも flush 終わるまで待たされる

Page 28: 5.6 以前の InnoDB Flushing

page cleaner thread のタスク・その一

- 5.5以前は buffer pool をぜんぶ使い切る仕様だったが、 5.6 からは innodb_lru_scan_depth で指定されただけ、 buffer pool の free page を残すようになった

- 関数的には buf_flush_LRU_tail()

Page 29: 5.6 以前の InnoDB Flushing

buf_flush_LRU_tail()

- page cleaner thread は一定間隔でLRU listを参照し、 innodb_lru_scan_depth で指定されただけ free page を確保しようとする。その際、参照されてない page から破棄する。

- page を破棄する際、 その page が dirty であれば disk に flush する。複数あればまとめて flush する

Page 30: 5.6 以前の InnoDB Flushing

- 他のthreadは free page 確保するために dirty page の flush を待たなくて良くなった

- innodb_lru_scan_depth は、 free page 枯渇しないなら、そんなに上げなくてもよいのでは?

- buffer pool の miss hit が多い場合は検討してもよいかも

Page 31: 5.6 以前の InnoDB Flushing

page cleaner thread のタスク・その二

- flush list を見て dirty page を flush する- 5.6でここのアルゴリズムが賢くなった

- 関数的には page_cleaner_flush_pages_if_needed()

Page 32: 5.6 以前の InnoDB Flushing

page_cleaner_flush_pages_if_needed()

- 一秒間にflush する page の数を redo log の残量に合わせてコントロールするようになった- redo log の使用率がinnodb_adaptive_flushing_lwm

を超えない限りは積極的に flush しないので、write combining 狙いやすい

- innodb_io_capacity_max 重要- redo log の使用率が 60% くらいいくと

io_capacity_max で指定しただけ flush する

Page 33: 5.6 以前の InnoDB Flushing

- redo log の残量に比例して flush が激しくなる == io_capacity_max に近づくので、書き込みの多いサーバに高性能なストレージを割り当てると、 default の設定でもそこそこ flush するようになった

Page 34: 5.6 以前の InnoDB Flushing

innodb_max_dirty_pages_pct_lwm

- default の 0 じゃないなら- これ と これ の比較をしてflush の頻度が変わ

る- transaction log より buffer poolの方がサイズ

大きいケースがほとんどだと思うので、そこまで意識しなくてもいい気がする

Page 35: 5.6 以前の InnoDB Flushing

しかし5.6の page cleaner は未完成

- page_cleaner: aggressive background flushing

- いやーしょーじきビミョーだけど- idle気味なとき、 io_capacity(maxではない)全

開まで flush してしまうケースがありえるそうな

Page 36: 5.6 以前の InnoDB Flushing

- make dirty page flushing more adaptive- すごいざっくりいうと- いまの Adaptive Flushing は flush list 優先の

仕組みで、 ほとんどのケースはうまくいくんだけど、 状況に応じて LRU list の flush をもっと優先できるといいよね

Page 37: 5.6 以前の InnoDB Flushing

なるほど

Page 38: 5.6 以前の InnoDB Flushing

中の人がドキュメントで

説明したくない気持ちが

わかった

Page 39: 5.6 以前の InnoDB Flushing

(めんどくさい)

Page 40: 5.6 以前の InnoDB Flushing

しょうじき言いたい

かもしれない

Page 41: 5.6 以前の InnoDB Flushing

           ///)

          /,.=゙''"/

   /     i f ,.r='"-‐'つ____  こまけぇこたぁいいんだよ!!

  /      /   _,.-‐'~/⌒  ⌒\

    /   ,i   ,二ニ⊃( ●). (●)\

   /    ノ    il゙フ::::::⌒(__人__)⌒::::: \

      ,イ「ト、  ,!,!|     |r┬-|     |

     / iトヾヽ_/ィ"\      `ー'´     /

Page 42: 5.6 以前の InnoDB Flushing

これらを踏まえてもう一度

Page 43: 5.6 以前の InnoDB Flushing
Page 44: 5.6 以前の InnoDB Flushing

とりあえず、 5.6 では

- 5.5 以前から 5.6 に移行するだけで、 Adaptive Flushing が賢くなる

- SSD使ってるなら 5.6 にした方がよい- innodb_log_file_size を増やしたり、

innodb_adaptive_flushing_lwm を上げたりするのは、SSDの書き込み寿命を伸ばすのに効果的ですねきっと

Page 45: 5.6 以前の InnoDB Flushing

ただ、やりすぎは禁物で

- innodb_log_file_size や、innodb_adaptive_flushing_lwm を増やすと、クラッシュリカバリの時間ふえます。

- log_file_size がデカすぎると page cache 持って行かれます

- innodb_io_capacity_max はうまく使いましょう

Page 46: 5.6 以前の InnoDB Flushing

あるいは、平滑化したいのであれば

- innodb_flushing_avg_loops- innodb_adaptive_flushing_lwm 上げ過ぎない

ほうがいいかも- 実際に検証するのが一番なんだけど- page_cleaner_flush_pages_if_needed() 読む

のオススメ

Page 47: 5.6 以前の InnoDB Flushing

次に

Page 48: 5.6 以前の InnoDB Flushing

double write buffer

- 耐障害性を上げるための機能- 具体的には torn page 対策

- double write するからといって、 iops が倍になるというわけではない。ほぼバッファリングされる

- 書き込まれる量は倍くらいになる- 詳しくは buf/buf0dblwr.cc

Page 49: 5.6 以前の InnoDB Flushing

- double write buffer は 合計 64*2 page、 page cleaner が主に使うのはそのうちの 120 page

- page cleaner が LRU list や flush list から page を flush するとき、対象の page を buf_dblwr_add_to_batch() して double write buffer にためて、最後に ibdata1 と *.ibd に書き出す。

Page 50: 5.6 以前の InnoDB Flushing

- LRU list や flush list から buf_dblwr_add_to_batch() で double write buffer buffer にのせてる最中に 120 page 使いきったら、その時点で buffer から flush する

Page 51: 5.6 以前の InnoDB Flushing

やや細かく言うと

- 120 page を ibdata1 に書くときは 64*16KB と (64-8)*16KB の二回に分けて。

- メモリ上の double write buffer から ibdata1 に書きだしたら、次は *.ibd へ io_submit()- このへんから

- 最後に io_handler_thread が更新された *.ibd を fsync() して終わり

Page 53: 5.6 以前の InnoDB Flushing

残りの 8 page は?

- single page flush のために予約されている- 例えば、free page を取れなかったとき、LRU list から

dirty page を一つだけ flush するときなど

- double write buffer が溜まるのを待ってられないだろうから、これは即時に ibdata1 と *.ibd の fsync() までやり切るみたい

- page cleaner が LRU list から flush するときは、 120 page の方に書いてバッファリング

Page 54: 5.6 以前の InnoDB Flushing

つまるところ

- page cleaner が buffer pool から *.ibd に flush するところは、 buffering されている

- disk へ書き出すところは double write buffer によって serialize されてる

- single page flush で double write buffer を flush してるとき、 page cleaner は待たされる可能性がある。逆もある。

Page 55: 5.6 以前の InnoDB Flushing

補足

- 5.6 の page cleaner は buffer pool instance の数だけループを回しているところがあるんですが、 LRU list からの flush や flush list からの flush は、そのループの中で実行されていて、 double write buffer から ibdata1 への flush も、その中で行われるので、 instance 多いと fsync() ふえます。

Page 56: 5.6 以前の InnoDB Flushing

考えうる残念なシナリオ

- page cleaner が flush list から大量に flush しまくってるとき(120page*N回 flush してるとき)に single page flush がくると、 single page flush と mutex 取り合いになりそう

Page 57: 5.6 以前の InnoDB Flushing

performance killer?

- Dimitri さんが double write buffer がボトルネックだと言うのもわかる気がする

- buf_dblwr_add_to_batch() は 120 page 溜まると、それがはけるまで buf_dblwr->mutex とりあう可能性がある

- よっぽど write intensive なときだろうけど

Page 58: 5.6 以前の InnoDB Flushing

時代が変わった

- HDDだと double write buffer 良かったと思う- いまはPCI-e SSD、コアたくさんのCPUとかあ

るので、よもやまさかのボトルネックになる可能性

- ただ、性能面に関していうと、 Dimitri さん未来に生きてるから- 彼は未来に備えてボトルネックとなりうる要素を潰さない

といけないと思うから

Page 59: 5.6 以前の InnoDB Flushing

- Dimitri さんが懸念するような問題は、現状ではあまり普及してないハイエンドなサーバじゃないと発生しにくいと思うので

- 現時点では、ほんとにまずいかどうか、各自検証してから見なおせばいい

- そもそも、そんなに書き込み激しい master だったとしたら、 slave 追いつけないかもしれないし

Page 60: 5.6 以前の InnoDB Flushing

double write buffer でみるところ

- どれくらいの頻度で double write buffer から disk に flush されてるか気になるなら Innodb_dblwr_writes や Innodb_dblwr_pages_written を見ればOK

- single page flush でも +1 されるので、 free page たりなくって Innodb_dblwr_writes 増えてるなら、 innodb_lru_scan_depth 見なおすとか

Page 61: 5.6 以前の InnoDB Flushing

double write buffer 無効化する前に

- 以下の対応で性能改善できるかも- single page flush を抑制するために、 free page が枯

渇しないよう、 innodb_lru_scan_depth みなおす

- flush list からの flush を最適化(write combining)する

ために、 innodb_log_file_size を大きくする。- innodb_adaptive_flushing_lwm 見直す- SSD なら innodb_flush_neighbors = 0

- 古くない page なら double write buffer に書き込む

のを先送りしても良い感

Page 62: 5.6 以前の InnoDB Flushing

5.7 の Atomic Write

- Fusion-io 使うなら、MySQL5.7から Atomic Write が使えるようになる

- すごいざっくり言うと、 double write しなくても書き込みの完全性が保証される

- double write buffer の代わりに atomic write 使えば、Fusion-io への書き込み減らせるし性能も改善されるようだし良いコトずくめ

Page 63: 5.6 以前の InnoDB Flushing

ext4 の data=journal

- double write buffer の代わりに file system に頑張ってもらう案

- Dimitri さんオススメで、 percona の人も検証してたのですが

- percona の人も言ってるけど、環境に依存するから、ちゃんとテストしてから使ったほうがいい- filesystem からだって bug は出ることがある

Page 64: 5.6 以前の InnoDB Flushing

- あと、 data=journal にしちゃうと O_DIRECT 使えないそうな。これはツライ

- https://www.kernel.org/doc/Documentation/filesystems/ext4.txt- Enabling this mode will disable delayed

allocation and O_DIRECT support.

Page 65: 5.6 以前の InnoDB Flushing

あと、5.7ではこれが入る

- innodb_log_write_ahead_size- 詳しくは (4) Avoiding the ‘read-on-write’ during

transaction log writing- facebook も percona もやってた件ですね

- これで page cache に載り切らないくらい redo log を大きくしたときの性能が改善するので

- とにかくでかくして flush 減らすのもありかも

Page 66: 5.6 以前の InnoDB Flushing

ここから先は、

人によって見解が分かれる(はずなので)

自己責任でお願いします

Page 67: 5.6 以前の InnoDB Flushing

skip-innodb-doublewrite

- innodb_flush_log_at_trx_commit={0,2} なら- そもそも、 commit 時に disk への sync が保証

されてないので、 skip しても良いのでわ- これで書き込む量ほぼ半分- master がHAクラスタなら、slaveは検討しても

よいのでわ- あるいはMHA使ってるなら

Page 68: 5.6 以前の InnoDB Flushing

innodb_log_group_home_dir

- slave 壊れたとき、データ復旧しないなら- redo log は disk 上になくてもいいんじゃない?- innodb_log_group_home_dir=/dev/shm- log_file_size は InnoDB 起動時にサイズが固

定されるので、 tmpfs 向き- もともと redo log は O_DIRECT で open され

ないので、 page cache でメモリ持っていくし

Page 69: 5.6 以前の InnoDB Flushing

innodb_undo_directory

- 5.6 から undo_log_file を ibdata1 の外に出せるようになった

- slave 壊れたとき、データ復旧しないなら- これも tmpfs におけるんじゃない?- redo log と違って固定長じゃないので、先ずは

disk 上で別ファイルにして、 tmpfs 上におけるサイズなのか確認したほうがよい

Page 70: 5.6 以前の InnoDB Flushing

- undo log を ibdata1 の外に出そうとすると ibdata1 作りなおしなので、試すのがめんどくさいけど

- skip-innodb-doublewrite や redo log を tmpfs に移動するだけなら、 mysqld 再起動だけで済むので、運用上試しやすい

Page 71: 5.6 以前の InnoDB Flushing

おまけ

- 今後気になることは、このblogに書かれてる- http://mysqlserverteam.com/mysql-5-7-improves-

dml-oriented-workloads/- Future improvements

- Implementing improvements to the adaptive flushing algorithm (suggestion by Dimitri Kravtchuk)

- これは実装されたらコード読みたい

Page 73: 5.6 以前の InnoDB Flushing

まとめ

- 5.6 の Adaptive Flushing オススメです- SSD使ってる人は5.6以降にあげましょう- log_file_size や adaptive_flushing_lvm 意識しましょう

- double write buffer 切る前に- I/O減らせる要素はあります- Dimitri さんは未来に生きてます(きっと)

- ともあれ、じっさいに検証しましょう

Page 74: 5.6 以前の InnoDB Flushing

おわり