Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

当社サービスの 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

Slide 6

Slide 6 text

当社サービスの 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 提供開始

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

共有ストレージ方式

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

ある日実際に起きたフェイルオーバー時のログ 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.

Slide 12

Slide 12 text

ある日実際に起きたフェイルオーバー時のログ 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. ここで起動完了

Slide 13

Slide 13 text

ある日実際に起きたフェイルオーバー時のログ 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秒くらい

Slide 14

Slide 14 text

ある日実際に起きたフェイルオーバー時のログ 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. この時間は何??

Slide 15

Slide 15 text

ある日実際に起きたフェイルオーバー時のログ 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. この時間は何?? このダウンタイムが私たちを苦しめていました

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

この発表で共有したいこと 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.

Slide 21

Slide 21 text

スタックトレースの調査 #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

Slide 22

Slide 22 text

スタックトレースの調査 #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 起動時に呼ばれそうな 関数から

Slide 23

Slide 23 text

スタックトレースの調査 #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 といった 関数が呼ばれている

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

ページキャッシュ? ▌ファイルアクセス高速化のためのカーネルのキャッシュ ▌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)

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

参考情報 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/