$30 off During Our Annual Pro Sale. View Details »

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

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

Cybozu
PRO

April 17, 2020
Tweet

More Decks by Cybozu

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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 提供開始

    View Slide

  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 レプリケーションもなく
    レプリケーションを使わない 共有ストレージ方式 で運用を始めました

    View Slide

  8. 共有ストレージ方式

    View Slide

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

    View Slide

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

    View Slide

  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.

    View Slide

  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.
    ここで起動完了

    View Slide

  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秒くらい

    View Slide

  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.
    この時間は何??

    View Slide

  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.
    この時間は何??
    このダウンタイムが私たちを苦しめていました

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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.

    View Slide

  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

    View Slide

  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
    起動時に呼ばれそうな
    関数から

    View Slide

  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 といった
    関数が呼ばれている

    View Slide

  24. コードを追ってみると
    https://github.com/mysql/mysql-server/blob/
    bd9c260cf7026a54d9bf91ce6782cdb4e6a25c71/
    storage/innobase/srv/srv0start.cc#L2327

    View Slide

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

    View Slide

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

    View Slide

  27. コードを追ってみると
    1. 存在するはずの ibd ファイルを列挙
    2. open(3) で開く
    3. pread(3) で先頭 64 KiB を読み込む
    (64KiB は innodb_page_size の最大値)
    4. 不整合が発生していないか検証
    innodb_file_per_table = ON なら
    テーブルの数だけ存在する

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. マルチテナント
    サイボウズでの実現方法
    ➡ テナントごとに MySQL の DATABASE を作成して テナントごとにテーブルを作る
    A社環境 (aaaaa.cybozu.com) B社環境 (bbbbb.cybozu.com)
    • users
    • schedule
    • comments
    • …
    • users
    • schedule
    • comments
    • …

    View Slide

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

    View Slide

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

    View Slide

  35. テーブル数と復旧時間の関係
    ▌innodb_fast_shutdown = 2 で強制的にクラッシュリカバリを走らせる
    ▌起動前に echo 3 > /proc/sys/vm/drop_caches でキャッシュを飛ばしておく

    View Slide

  36. テーブル数と復旧時間の関係

    View Slide

  37. クラッシュからの復旧時の処理
    1. クラッシュリカバリ
    ◼ redo log の内容をテーブルスペースに反映する
    ◼ 「innodb_log_file_size を大きくしすぎると
    復旧に時間がかかるので注意」 とよく言われるもの
    2. ibd ファイルのベリファイ
    ◼ ファイルの先頭 64 KiB を読む
    ◼ テーブルが多い環境ではとても遅い
    遅い原因は
    こっちだった

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  41. ボトルネックはどこ?
    多数のランダムリードを直列に同期読み込みするペナルティ
    ▌コマンドをまとめて処理できるストレージ(RAID, NVMe)を
    使っていても活かせない
    ▌OS の IO スケジューラの恩恵を受けることもできない
    ➡ 並列に非同期読み込みするように変えたい

    View Slide

  42. 実装方法
    a. MySQL のソースコードをがんばって書き換える
    ◼ upstream に貢献出来てかっこいいが
    実装難易度を考えて断念
    b. ヘッダを事前にページキャッシュに乗せるだけのプログラムを作る
    ◼ メモリに乗せてしまえば直列に読むペナルティを減らせそう
    ◼ MySQL に手を加えなくても済む。採用

    View Slide

  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)

    View Slide

  44. ページキャッシュに乗せるためのプログラム
    ▌Go で書きました
    ▌ibd ファイルを Open して先頭 64KiB を Read するような
    goroutine(軽量スレッド)をたくさん立てるだけ
    ▌systemd の設定を書いて MySQL の前に起動するようにした

    View Slide

  45. 実験結果
    オレンジ は前述のプログラムの実行時間 + MySQL の復旧時間

    View Slide

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

    View Slide

  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/

    View Slide