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

さいきんの InnoDB Adaptive Flushing (仮)

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

さいきんの InnoDB Adaptive Flushing (仮)

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

Avatar for Takanori Sejima

Takanori Sejima PRO

September 07, 2017
Tweet

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