Upgrade to Pro — share decks privately, control downloads, hide ads and more …

さいきんの InnoDB Adaptive Flushing (仮)

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

さいきんの InnoDB Adaptive Flushing (仮)

さいきんの InnoDB のお話です。

Avatar for Takanori Sejima

Takanori Sejima

September 07, 2017

More Decks by Takanori Sejima

Other Decks in Technology

Transcript

  1. 自己紹介 - わりとMySQLでごはんたべてます - 3.23.58 あたりから使ってます - 一時期は Resource Monitoring

    もよくやってま した - さいきんはハードウェアの評価したり、 Linux の TCPプロトコルスタックまわり調べたりもして ました - Twitter: @ts4th 3
  2. 今日のお題 - MySQL 5.7 に至るまでの InnoDB Adaptive Flushing を振り返りつつ -

    InnoDB Adaptive Flushing によって発生する 書き込み処理について整理して - うまいこと書き込み処理を最適化できないか考 えます 4
  3. InnoDB Adaptive Flushing とは - MySQL5.1の時代に、InnoDB Plugin 1.0.4 で 追加されました。

    - 5.5 でInnoDB本体に組み込まれ、5.6 、5.7でさ らに良くなりました。 - ただ、公式ドキュメントで解説しきれてない部分 もあると思うので、そのあたりを振り返ってみま す 7
  4. InnoDBの基本的なファイル群 - ibdata1 - internal data dictionary, undo log, change

    buffer, double write buffer などが含まれます(今回は紙幅の都 合でそのあたり割愛します) - ib_log_file* - transaction log・・・REDO Logs(Write Ahead Log) - *.ibd - table space・・・table の data や index 9
  5. ざっくりいうと、 InnoDB は - Transaction で発生したデータの更新は ib_log_file* に溜め込んで - 裏でチマチマ

    *.ibd に flush - ib_log_file* は固定長の(InnoDB起動時にサイ ズが決まる)ログなので、使い切ると、 Transaction の更新内容をログに書けなくなり ます。そうならないよう、裏で *.ibd に反映してい るわけです 10
  6. REDO Logs(Write Ahead Log) - 更新内容は先ず log に書いて、 table space

    に は裏でゆっくり反映 - WALはMySQL以外のRDBMS でも用いられる手法 - log への書き込みは sequential write になるの で、HDDみたいな random I/O 遅い block device でもそれなりに速い - table space に反映するとき、更新処理をまとめ ることもできる。 (write combining) 11
  7. 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。 12
  8. Last checkpoint at - buffer pool 上にある更新内容を、LSN 的に *.ibd にどこまで反映したか示すものが

    Last checkpoint at。 - SHOW ENGINE INNODB STATUS で見える やつ - 可能であれば、 (LSN - Last checkpoint at) は監視項 目に加えておくのがオススメ - 実はかなり重要な情報です 13
  9. oldest_modification - ibdata1 や *.ibd は、page (default 16KB)という単位で管 理されており -

    page の構造体には、 oldest_modification というメンバ変 数がある - その page が dirty になった最初のイベントが書かれている log の position(LSN) 、それが oldest_modification - dirty page をflushしてdiskに同期させると、その page の oldest_modification は 0 でリセット 14
  10. write combining - I/O が page 単位なので、同一の page への更 新は、まとめて

    disk に flush できる。 - 同一の page に格納されている複数の row への更新を、一回で disk に書けることも - log に溜めこみ、まとめて flush できるとお得 - Last checkpoint at の進み方と disk I/O は、必 ずしも比例しない。 15
  11. InnoDB Adaptive Flushing の必要性 - log は有限なので使いきってはいけない - 具体的に言うと、(LSN -

    Last checkpoint at) が 75%くらいになると、強制的に dirty page の flush 走る。そうなると刺さりがち。 - 75%というのは定数ではなく、 log/log0log.cc の log_calc_max_ages() で計算してる - なるべく強制 flush 走らないよう、 adaptive に flushing する機能が必要だったのです 17
  12. InnoDB Adaptive Flushing ない時代 - InnoDB Adaptive Flushing がない時代、(具体 的には

    InnoDB Plugin 1.0.4 より前の時代)い ずれかの状況にならないと flush されにくかった - buffer pool 上で dirty page の比率が innodb_max_dirty_pages_pct(昔はdefault90、いまは default 75)を超える - REDO Logs の使用率が約75%超える 18
  13. 原初の InnoDB Adaptive Flushing - 5.1や5.5 の InnoDB Adaptive flushing

    は、 master_thread という background で動いてる thread が、 innodb_io_capacity に応じて 他の タスクの合間に flush してた - むかしの master_thread は、単一の thread で複数の background task を goto でこなしていた。 - InnoDB がどういう task を background でやってるかわ かりやすいので、一度、 5.5 の master_thread 読んで みるのオススメです。 19
  14. 5.5以前の innodb_io_capacity - io_capacity は正確にいうと iops ではない - master thread

    が一秒間にどれくらい page の I/O してもいいか、という値 - (SELECT が多いなどで) disk read が多いと きは、 dirty page の flush を加減してた - 例えば、ピークタイムに dirty page をあまり flush しない ことがあったとしたら、それは read が多いことが原因 だったりした 20
  15. - 5.5 以前の InnoDB Adaptive Flushing は、 innodb_io_capacity の範囲内で flush

    してた ので - 高速な block device だからといって innodb_io_capacity 上げすぎると、 write combining が効かない - REDO Logs を溜めずにつどつどflushしてしまう 21
  16. 5.6以降の InnoDB Adaptive Flushing - で、5.6 や5.7でかなり良くなったんですが - MySQL Performance

    Architect のDimitriK の 資料 から図を拝借すると、現状こうです 23
  17. 24

  18. 5.6 以降で何が変わったのか・その一 - 公式ドキュメントとソースコードの乖離が激しく なった - ただ、先日久々に https://dev.mysql.com/ みたら、だい ぶ充実してきているように見えました

    - 最近は https://github.com/mysql/mysql-server でも change log が読めるし、change log から対応する WorkLog を辿りやすくなった - WorkLog にコードの修正方針などがまとめられている ので、とても調査しやすい OSS ですね 26
  19. 5.6 以降で何が変わったのか・その二 - back ground thread の種類が劇的に増えた - 5.5以前だと master_thread

    でこなしていたタスクが複 数の thread で分散 - 特定のタスク(purge や dirty page の flush など)が重 い場合、他のタスクがそれに引っ張られてしまう場合が あったが、それが軽減された 27
  20. 5.6 以降で何が変わったのか・その三 - 関連する parameter 増えた&意味変わった - 重要なのはこのあたり - innodb_adaptive_flushing_lwm(lwmは

    low water mark の略) - innodb_io_capacity - innodb_io_capacity_max - innodb_max_dirty_pages_pct_lwm - innodb_lru_scan_depth 28
  21. page cleaner thread - MySQL5.5までは、 InnoDB で master thread と呼ばれる

    thread が色々やってた - dirty page の flush - change buffering - purge - 5.6 でこれらの機能が複数の thread に分割 - 5.6 で追加された thread の一つが page cleaner thread 29
  22. flush list と LRU list - buffer pool 上の page

    は、昔から二種類の list で管理されている - flush list は更新された順(oldest_modification 順)に dirty page を管理 - LRU list は、すべての page を対象に、参照さ れた順に管理 - 5.6 以降では page cleaner thread がこれらに 対して back ground でタスクをこなす 30
  23. 5.5以前の LRU list - 5.5 以前で LRU list が参照されるケースの一 つは、

    buffer pool の page があふれたとき - disk 上のデータを読み込むとき、空き page なかった ら、最も参照されていない page を破棄する。 - もし、その page が dirty だったら、 disk に flush してか ら破棄 - foreground の thread は flush 終わるまで待たされる - flush 終わって page を破棄するまで、 disk から データが読めない 31
  24. page cleaner thread のタスク・その一 - 5.5以前は buffer pool をぜんぶ使い切る仕様 だったが、

    5.6 からは innodb_lru_scan_depth で指定されただけ、 予め buffer pool の free page を確保しておくようになった - 関数的には - MySQL5.6 では buf_flush_LRU_tail() 32
  25. buf_flush_LRU_tail() - page cleaner thread は一定間隔でLRU listを 参照し、 innodb_lru_scan_depth で指定され

    ただけ free page を確保しようとする。その際、 参照されてない page から破棄する。 - page を破棄する際、 その page が dirty であ れば disk に flush する。複数あればまとめて flush する 33
  26. - 常時 free page 確保してあるので、他の thread は free page 確保するために

    dirty page の flush を待たなくて良くなった - innodb_lru_scan_depth は、 free page 枯渇し ないなら、そんなに上げなくてもよいのでは? - buffer pool の miss hit が多くて free page 減りがちな ら、増やすことを検討してもよいかも 34
  27. page cleaner thread のタスク・その二 - flush list を見て dirty page

    を flush する - 5.6でここのアルゴリズムが賢くなった - 関数的には - MySQL5.6 だと page_cleaner_flush_pages_if_needed() 36
  28. page_cleaner_flush_pages_if_needed() - MySQL 5.6から、 一秒間に flush する page の数を REDO

    Logs の使用率に合わせてコント ロールするようになった - REDO Logs の使用率が innodb_adaptive_flushing_lwm を超えない限りは積極 的に flush しない - innodb_io_capacity_max 重要 - REDO Logs の使用率が(とてもざっくり) 60% くらいに なると io_capacity_max で指定しただけ flush 37
  29. - REDO Logs の使用率に比例して flush が激し くなる == innodb_io_capacity_max に近づくの

    で、書き込みの多いサーバに高性能な block device を割り当てると、 default の設定でもそこ そこ flush するようになった - 逆に、書き込みの少ないDBだと、 REDO Logs の使用 率低いので、 dirty page はちょっとずつしか flush され ない。安価なハードウェアでもそこそこ動く 38
  30. (これは私見ですが) - 設計を見る限り、 5.5 以前の InnoDB Adaptive Flushing は、 HDD

    のような低速な block device をメインターゲットにしてたのでは? - 新しい高性能なハードウェアを活かすために は、新しい MySQL へ移行していくのが無難。 39
  31. innodb_max_dirty_pages_pct_lwm - この parameter が default の 0 じゃないなら -

    REDO Logs の使用率 と buffer pool の dirty page の比率 を比較して、flush の頻度が変わ る - transaction log より buffer pool の方がサイズ 大きいケースがほとんどだと思うので、そこまで 意識しなくてもいい気がする 42
  32. しかし5.6の page cleaner は未完成 - page_cleaner: aggressive background flushing というバグレポートがあります

    - idle 気味なとき、 innodb_io_capacity(maxで はない)全開まで flush してしまうケースがあり える - もしこれが困るなら、 innodb_io_capacity 下げ ても良いのではないかと 44
  33. MySQL 5.7 での大きな変更点 - 5.7から、 flush する page の数を決めるとき、 REDO

    logs の使用率以 外も考慮するようになった。詳しくは こちらのWorkLog に - 改善点はいくつかあるのだが、 InnoDB Adaptive Flushing 的にポイン トは二つ。 - REDO Logs の使用率だけではなく、更新頻度も考慮 - もう一つは、buffer pool instance ごとの dirty page の偏りを考慮 - 関数的には、 5.6の page_cleaner_flush_pages_if_needed() が大幅 にリファクタリングされ、5.7では page_cleaner_flush_pages_recommendation() になった 46
  34. page_cleaner_flush_pages_recommendation() 47 - REDO Logs の使用率だけではなく、更新頻度も考慮。 - 「高頻度で REDO Logs

    に書いてるんだけど、 REDO Logs の使 用率が低いので innodb_io_capacity_max で指定しただけ flush してくれない」という事態が回避できるようになる。 - REDO Logs の使用率が約75% くらいになると強制的に dirty page が flush されるので、「REDO Logs 使い切りそうなペースで 更新されてるならば、innodb_io_capacity_max で指定されてるま で flush して、 log 使い切るのを防ごう」という理由かな?
  35. page_cleaner_flush_pages_recommendation() - buffer pool instance ごとの dirty page の偏りを考慮。 -

    InnoDB の buffer pool は複数の instance に分割できるのだが、 5.7 か ら、古い dirty page の多い instance は flush を多く、少ない instance は flush を少なくするようになった。5.6以前は instance ごとに dirty page の 偏りができていた可能性があったけど、改善された。 - 5.6 は 複数の buffer pool instance があった場合、必ずしも「より古い dirty page を flush する」という挙動になるとは限らなかった。「instance ご とに dirty page を N page ずつ flush する」という挙動になっていた。5.7 から、「古い dirty page をたくさん持ってる buffer pool instance はたくさん dirty page をflushする」という挙動になったので、 write combining 的に有 利なはず。 48
  36. さらに MySQL5.7 では - page cleaner thread が複数スレッドに対応し た。 -

    buffer pool instance が複数あれば、最大で instance の数だけ page cleaner thread が並列して flush できる - innodb_page_cleaners というパラメータで、 dirty page を flush するスレッドの数をコント ロールできる。 49
  37. とりあえず、 5.6 以降では - 5.5 以前から 5.6 に移行するだけで、 InnoDB Adaptive

    Flushing が賢くなる - 5.7 ならより賢い - 高速な block device 使ってるなら、MySQLの バージョン新しくした方がよい - InnoDB Adaptive Flushing が賢くなってるので、 MySQLのバージョン新しくするだけで、 dirty page の flush 周りが改善する 50
  38. InnoDB Adaptive Flushing の勘所 - 使い方にあわせてチューニングできるパラメー タが、たくさんあるのですが - 基本はやはり、 innodb_log_file_size

    ですね。 必要なら、 innodb_log_file_size は GB単位で 指定してよいのでは。 - あとはinnodb_io_capacity_max ですかね。 51
  39. まずは innodb_log_file_size - REDO Logs を使い切ると、強制的に dirty page が flush

    されまくって重いので、ピークタイ ムに枯渇しない程度は欲しい - ただ、REDO Logs 多すぎるとクラッシュリカバ リに時間が掛かるし、ファイルシステムのキャッ シュ(Linuxだとpage cache)にメモリをたくさん 使われてしまう 52
  40. ピークタイムの REDO Logs の使用率 - (個人的見解ですが)ピークタイムでも使用率 は、 50~60% 以下でよいのでは? -

    (LSN - Last checkpoint at) が REDO Logs の総量の 75% くらいまで行くと、 Transaction がブロックされる可 能性が出てくる。(5.6以降は)使用率60% くらいになる と、 innodb_io_capacity_max まで flush するケースが ある。余裕残してもよいのでは - REDO Logs の残量を適度に残せるようチュー ニングするのがオススメ。 53
  41. 5.6 の方が 75% くらいになりやすい? - 5.6 は REDO Logs の使用率が

    60% くらいに なるまで、 io_capacity_max まで flush しにく かったので、 REDO Logs の使用率が 75% く らいになりやすかったのでは? - 5.7 は更新頻度高ければ、使用率低くても innodb_io_capacity_max まで flush するの で、 REDO Logs のサイズが 5.6 より少なくて も、使用率上がりにくいのでは。 54
  42. 5.7 と innodb_io_capacity_max - 5.7 は REDO logs の使用率だけでなく、更新 頻度もみて

    dirty page をいくら flush するか決 める。故に、REDO logs の使用率低くても、 innodb_io_capacity_max まで flush する可能 性がある。上げすぎない方がよい - ただ、innodb_flushing_avg_loops であるてい ど調整可能。 55
  43. innodb_flushing_avg_loops - REDO logs の更新頻度も見て flush の頻度を 決めるのだが、(ざっくりいうと)そのサンプリン グ周期は、innodb_flushing_avg_loops で調整

    可能 - innodb_flushing_avg_loops をうんと大きくすれば、一 瞬だけ更新がバーストするDBでも、 innodb_io_capacity_max まで flush しにくいはず 56
  44. MySQLはハードウェアの進化に追随している - Dimitri という人は(たぶん)十数年以上にわ たって MySQL の benchmark を取り続け、新 しいハードウェアの性能を活かせるよう、ボトル

    ネックを調査し続けてます - MySQLの新しいバージョンが出るたびにその 成果が反映されているので、 MySQL はハード ウェアの進化に追随し続けてる OSS と言って 良いでしょう 58
  45. おまけ - 次期メジャーバージョンの MySQL 8.0でも、 InnoDB はいろいろ改善されるのですが - Facebook からの

    feature request や、 Percona の patch が取り込まれるようです。 - 今後も引き続き InnoDB が進化していくのは、 ありがたい限りですね。 59
  46. まとめ - 最近の InnoDB Adaptive Flushing はかなり 賢いです。オススメです。 - 最近のハードウェア使ってる人は、少なくとも5.6以降、

    できれば5.7を使うのが良いでしょう。 - (LSN - Last checkpoint at) を意識しましょう - 高性能なハードウェア使ってるなら innodb_io_capacity_max 上げてもOKですが、上げす ぎないよう注意しましょう。特に 5.7 では気をつけましょ う。 60
  47. 参考 - Configuring InnoDB for MySQL 5.6: innodb_io_capacity, innodb_lru_scan_depth -

    MySQL 5.6: IO-bound, update-only workloads - InnoDB adaptive flushing in MySQL 5.6: checkpoint age and io capacity - InnoDB Deep Talk #2 (仮) に引っ張りだされま した。 61