Save 37% off PRO during our Black Friday Sale! »

MySQL のクラッシュからの復旧を ちょっとだけ速くする裏技/Techniques to make mysql crash recovery a bit faster

MySQL のクラッシュからの復旧を ちょっとだけ速くする裏技/Techniques to make mysql crash recovery a bit faster

A97eee01397705443a72a48ce29d3e19?s=128

Cybozu
PRO

April 17, 2020
Tweet

Transcript

  1. MySQL のクラッシュからの復旧を ちょっとだけ速くする裏技 特殊な環境ではかなり速くなる サイボウズ株式会社 飯塚 翔

  2. 自己紹介 ▌名前:飯塚 翔 ▌所属:サイボウズ株式会社 クラウド運用チーム(3年目) ▌最近の取り組み:MySQL 8.0 の検証 ▌@yoku0825 さんに技術顧問をしていただいています

  3. 背景 少し長いですがお付き合いください

  4. 背景 300 台を超える数の MySQL インスタンスを運用

  5. 当社サービスの MySQL の構成 2010.12 MySQL 5.5 GA 2013.02 MySQL 5.6

    GA (GTID) 2015.10 MySQL 5.7 GA (loss-less semi-sync) 2018.4 MySQL 8.0 GA
  6. 当社サービスの MySQL の構成 2010.12 MySQL 5.5 GA 2013.02 MySQL 5.6

    GA (GTID) 2015.10 MySQL 5.7 GA (loss-less semi-sync) 2018.4 MySQL 8.0 GA 2011.11 cybozu.com 提供開始
  7. 当社サービスの MySQL の構成 2010.12 MySQL 5.5 GA 2011.11 cybozu.com 提供開始

    2013.02 MySQL 5.6 GA (GTID) 2015.10 MySQL 5.7 GA (loss-less semi-sync) 2018.4 MySQL 8.0 GA 当時は GTID も loss-less semi-sync レプリケーションもなく レプリケーションを使わない 共有ストレージ方式 で運用を始めました
  8. 共有ストレージ方式

  9. 共有ストレージ方式 https://www.slideshare.net/satoshimitani71/osc-2017-osaka-mysql-db

  10. 共有ストレージ方式 ▌ここがうれしい ◼ データロストのリスクがない! (準同期レプリケーションが無い時代はこの方法しかなかった) ▌ここがだめ ◼ 起動時に特別な処理が走り 復旧に時間がかかる おそれ

  11. ある日実際に起きたフェイルオーバー時のログ 04:04:34 [Note] InnoDB: Starting crash recovery. … 04:04:45 [Note]

    InnoDB: Apply batch completed 04:13:19 [Note] InnoDB: Starting in background the rollback of uncommitted transactions … 04:13:21 [Note] /opt/cybozu/mysql/bin/mysqld: ready for connections.
  12. ある日実際に起きたフェイルオーバー時のログ 04:04:34 [Note] InnoDB: Starting crash recovery. … 04:04:45 [Note]

    InnoDB: Apply batch completed 04:13:19 [Note] InnoDB: Starting in background the rollback of uncommitted transactions … 04:13:21 [Note] /opt/cybozu/mysql/bin/mysqld: ready for connections. ここで起動完了
  13. ある日実際に起きたフェイルオーバー時のログ 04:04:34 [Note] InnoDB: Starting crash recovery. … 04:04:45 [Note]

    InnoDB: Apply batch completed 04:13:19 [Note] InnoDB: Starting in background the rollback of uncommitted transactions … 04:13:21 [Note] /opt/cybozu/mysql/bin/mysqld: ready for connections. ここまで11秒くらい
  14. ある日実際に起きたフェイルオーバー時のログ 04:04:34 [Note] InnoDB: Starting crash recovery. … 04:04:45 [Note]

    InnoDB: Apply batch completed 04:13:19 [Note] InnoDB: Starting in background the rollback of uncommitted transactions … 04:13:21 [Note] /opt/cybozu/mysql/bin/mysqld: ready for connections. この時間は何??
  15. ある日実際に起きたフェイルオーバー時のログ 04:04:34 [Note] InnoDB: Starting crash recovery. … 04:04:45 [Note]

    InnoDB: Apply batch completed 04:13:19 [Note] InnoDB: Starting in background the rollback of uncommitted transactions … 04:13:21 [Note] /opt/cybozu/mysql/bin/mysqld: ready for connections. この時間は何?? このダウンタイムが私たちを苦しめていました
  16. クラッシュからの復旧時の処理 1. クラッシュリカバリ ◼ redo log の内容をテーブルスペースに反映する ◼ 「innodb_log_file_size を大きくしすぎると

    復旧に時間がかかるので注意」 とよく言われるもの 2. ???
  17. クラッシュからの復旧時の処理 1. クラッシュリカバリ ◼ redo log の内容をテーブルスペースに反映する ◼ 「innodb_log_file_size を大きくしすぎると

    復旧に時間がかかるので注意」 とよく言われるもの 2. ??? クラッシュリカバリが 遅いのだと思っていました
  18. 技術顧問に聞いてみた クラッシュリカバリって これくらいかかるものなんですか?

  19. 技術顧問に聞いてみた クラッシュリカバリって これくらいかかるものなんですか? いや、それはおかしい

  20. この発表で共有したいこと 1. 私たちを苦しめていた 謎のダウンタイム の正体 2. 復旧を高速化するためのワークアラウンド 04:04:34 [Note] InnoDB:

    Starting crash recovery. … 04:04:45 [Note] InnoDB: Apply batch completed 04:13:19 [Note] InnoDB: Starting in background the rollback of uncommitted transactions … 04:13:21 [Note] /opt/cybozu/mysql/bin/mysqld: ready for connections.
  21. スタックトレースの調査 #6 Datafile::read_first_page at storage/innobase/fsp/fsp0file.cc:338 #7 Datafile::validate_first_page at storage/innobase/fsp/fsp0file.cc:552 #8

    Datafile::validate_to_dd at storage/innobase/fsp/fsp0file.cc:404 #9 fil_ibd_open at storage/innobase/fil/fil0fil.cc:3968 #10 dict_check_sys_tables at storage/innobase/dict/dict0load.cc:1465 #11 dict_check_tablespaces_and_store_max_id at storage/innobase/dict/dict0load.cc:1525 #12 innobase_start_or_create_for_mysql at storage/innobase/srv/srv0start.cc:2329 #13 innobase_init at storage/innobase/handler/ha_innodb.cc:4048
  22. スタックトレースの調査 #6 Datafile::read_first_page at storage/innobase/fsp/fsp0file.cc:338 #7 Datafile::validate_first_page at storage/innobase/fsp/fsp0file.cc:552 #8

    Datafile::validate_to_dd at storage/innobase/fsp/fsp0file.cc:404 #9 fil_ibd_open at storage/innobase/fil/fil0fil.cc:3968 #10 dict_check_sys_tables at storage/innobase/dict/dict0load.cc:1465 #11 dict_check_tablespaces_and_store_max_id at storage/innobase/dict/dict0load.cc:1525 #12 innobase_start_or_create_for_mysql at storage/innobase/srv/srv0start.cc:2329 #13 innobase_init at storage/innobase/handler/ha_innodb.cc:4048 起動時に呼ばれそうな 関数から
  23. スタックトレースの調査 #6 Datafile::read_first_page at storage/innobase/fsp/fsp0file.cc:338 #7 Datafile::validate_first_page at storage/innobase/fsp/fsp0file.cc:552 #8

    Datafile::validate_to_dd at storage/innobase/fsp/fsp0file.cc:404 #9 fil_ibd_open at storage/innobase/fil/fil0fil.cc:3968 #10 dict_check_sys_tables at storage/innobase/dict/dict0load.cc:1465 #11 dict_check_tablespaces_and_store_max_id at storage/innobase/dict/dict0load.cc:1525 #12 innobase_start_or_create_for_mysql at storage/innobase/srv/srv0start.cc:2329 #13 innobase_init at storage/innobase/handler/ha_innodb.cc:4048 validate や check といった 関数が呼ばれている
  24. コードを追ってみると https://github.com/mysql/mysql-server/blob/ bd9c260cf7026a54d9bf91ce6782cdb4e6a25c71/ storage/innobase/srv/srv0start.cc#L2327

  25. コードを追ってみると https://github.com/mysql/mysql-server/blob/ bd9c260cf7026a54d9bf91ce6782cdb4e6a25c71/ storage/innobase/srv/srv0start.cc#L2327 クラッシュから復旧するときには テーブルのヘッダの検査も行う

  26. コードを追ってみると https://github.com/mysql/mysql-server/blob/ bd9c260cf7026a54d9bf91ce6782cdb4e6a25c71/ storage/innobase/srv/srv0start.cc#L2327 ibd ファイルが多いときには 時間がかかるので注意

  27. コードを追ってみると 1. 存在するはずの ibd ファイルを列挙 2. open(3) で開く 3. pread(3)

    で先頭 64 KiB を読み込む (64KiB は innodb_page_size の最大値) 4. 不整合が発生していないか検証 innodb_file_per_table = ON なら テーブルの数だけ存在する
  28. テーブルの数ってせいぜい数百程度だし 問題になる環境なんてないのでは?

  29. テーブルの数ってせいぜい数百程度だし 問題になる環境なんてないのでは? あるんだな、それが

  30. サイボウズのサービスは マルチテナント で提供

  31. マルチテナント ▌契約ごとに閉じた環境が作られる ▌課金もアカウント管理もセキュリティポリシーの設定も環境ごとに別々 A社環境 (aaaaa.cybozu.com) B社環境 (bbbbb.cybozu.com)

  32. マルチテナント サイボウズでの実現方法 ➡ テナントごとに MySQL の DATABASE を作成して テナントごとにテーブルを作る A社環境

    (aaaaa.cybozu.com) B社環境 (bbbbb.cybozu.com) • users • schedule • comments • … • users • schedule • comments • …
  33. テーブル数はどうなる? ▌N : テナントあたりのテーブル数 ◼ 作りこんだアプリケーションなら 100 を超える ことはよくある ◼

    パーティショニングを駆使しているともっと増える ▌M : テナント数 ◼ 高性能なインスタンスに高密度に集約すると 数百 程度になることも
  34. テーブル数はどうなる? ▌N : テナントあたりのテーブル数 ◼ 作りこんだアプリケーションなら 100 を超える ことはよくある ◼

    パーティショニングを駆使しているともっと増える ▌M : テナント数 ◼ 高性能なインスタンスに高密度に集約すると 数百 程度になることも N × M ≈ 数万
  35. テーブル数と復旧時間の関係 ▌innodb_fast_shutdown = 2 で強制的にクラッシュリカバリを走らせる ▌起動前に echo 3 > /proc/sys/vm/drop_caches

    でキャッシュを飛ばしておく
  36. テーブル数と復旧時間の関係

  37. クラッシュからの復旧時の処理 1. クラッシュリカバリ ◼ redo log の内容をテーブルスペースに反映する ◼ 「innodb_log_file_size を大きくしすぎると

    復旧に時間がかかるので注意」 とよく言われるもの 2. ibd ファイルのベリファイ ◼ ファイルの先頭 64 KiB を読む ◼ テーブルが多い環境ではとても遅い 遅い原因は こっちだった
  38. ボトルネックはどこ? 1. 存在するはずの ibd ファイルを列挙 2. open(3) で開く 3. pread(3)

    で先頭 64 KiB を読み込む (64KiB は innodb_page_size の最大値) 4. 不整合が発生していないか検証
  39. ボトルネックはどこ? 1. 存在するはずの ibd ファイルを列挙 2. open(3) で開く 3. pread(3)

    で先頭 64 KiB を読み込む (64KiB は innodb_page_size の最大値) 4. 不整合が発生していないか検証 直列に
  40. ボトルネックはどこ? 1. 存在するはずの ibd ファイルを列挙 2. open(3) で開く 3. pread(3)

    で先頭 64 KiB を読み込む (64KiB は innodb_page_size の最大値) 4. 不整合が発生していないか検証 同期読み込み
  41. ボトルネックはどこ? 多数のランダムリードを直列に同期読み込みするペナルティ ▌コマンドをまとめて処理できるストレージ(RAID, NVMe)を 使っていても活かせない ▌OS の IO スケジューラの恩恵を受けることもできない ➡

    並列に非同期読み込みするように変えたい
  42. 実装方法 a. MySQL のソースコードをがんばって書き換える ◼ upstream に貢献出来てかっこいいが 実装難易度を考えて断念 b. ヘッダを事前にページキャッシュに乗せるだけのプログラムを作る

    ◼ メモリに乗せてしまえば直列に読むペナルティを減らせそう ◼ MySQL に手を加えなくても済む。採用
  43. ページキャッシュ? ▌ファイルアクセス高速化のためのカーネルのキャッシュ ▌innodb_flush_method = O_DIRECT の説明でよく出てくる ▌「free ではなく available を見ましょう」という説明でもおなじみ

    $ free -w -h total used free shared buffers cache available Mem: 11G 5.8G 627M 3.5M 1.1M 5.3G 5.6G Swap: 232M 0B 232M ページキャッシュ(と Slab)
  44. ページキャッシュに乗せるためのプログラム ▌Go で書きました ▌ibd ファイルを Open して先頭 64KiB を Read

    するような goroutine(軽量スレッド)をたくさん立てるだけ ▌systemd の設定を書いて MySQL の前に起動するようにした
  45. 実験結果 オレンジ は前述のプログラムの実行時間 + MySQL の復旧時間

  46. まとめ ▌MySQL にはクラッシュからの復旧時に「ibd ファイルのヘッダを読む」 という処理があり、以下のような環境で復旧に時間がかかることがある ◼ レプリケーションを使っていない(共有ストレージ方式など) ◼ テーブル数が多い(マルチテナントのサービスを提供しているなど) ▌「事前にページキャッシュに乗せておく」というワークアラウンドで

    復旧を高速化できる場合がある
  47. 参考情報 Percona のブログに「テーブルが増えすぎるとロクなことがないので注意」という記事あり ▌MySQL 8.0 General Tablespaces: File per Database

    (and no FRM files) https://www.percona.com/blog/2016/10/03/mysql-8-0-general- tablespaces-file-per-database-no-frm-files/ ▌One Million Tables in MySQL 8.0 https://www.percona.com/blog/2017/10/01/one-million-tables- mysql-8-0/