MySQL 5.7 のパフォーマンスチューニングについて、調べてたのでまとめる。
// 結構な文量になってしまった…
大きく、2つのアプローチがある。
- DBチューニング
- システム変数 (my.cnf) のチューニング
- 全体最適
- アプリ (SQL) チューニング
- 個別最適
まあ、地道に、計測→問題点の特定→修正→計測… のサイクルを回すしかない。
1. DBチューニング
ディスク構成関連
- ログファイルとデータファイル (たとえば、システム表領域データファイル) を別の物理ディスクに配置することでI/O性能が向上する
- InnoDBデータファイルをRawデバイスに置くことで、I/O性能が向上する
- OSマウントオプション noatime を無効化する
- Linuxのファイルシステムには、ファイル読み込み時刻が「atime」として保存される。1ファイルアクセス当たりのオーバーヘッドはささやかだが、大量のファイルに対して頻繁にアクセスするシステムでは、atimeの更新にかかる時間も無視できなくなる。atimeが不要なシステムであれば、atimeの更新を無効化することでディスクのパフォーマンスが向上する可能性がある。
ストレージエンジン関連
バッファプールサイズ
innodb_buffer_pool_size
- バッファプールサイズ
innodb_buffer_pool_size
は InnoDB のデータとインデックスをキャッシュするメモリ上の領域。可能な限り大きくすることが基本方針。 - MySQL (InnoDB) のみを利用していれば、バッファプールサイズ
innodb_buffer_pool_size
はメインメモリの 70-80% 程度を割り当てる。データとインデックスの両方をキャッシュする。(デフォルトは128MB) - ログファイル全体をOSがキャッシュできる程度の余裕を持たせると更新系の性能に利点が大きい。
- バッファプールサイズ
バッファプールインスタンス
innodb_buffer_pool_instances
- バッファプールをいくつのインスタンスに分けるか指定する。デフォルト8
- 十分なメモリがある場合、複数のバッファプールインスタンスに分割することで、グローバル Mutex の衝突を減らすことができ同時並行性を改善可能。キャッシュされたページに対して、異なるスレッドがI/Oを行うときの競合が減るため並列性が増す。
innodb_buffer_pool_size
が大きく、mutex競合がオーバヘッドとなっている場合は、2以上に設定innodb_buffer_pool_size
はN * innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances
に等しいサイズにする必要あり。- オンラインでもサイズ変更可能。
- MySQL :: MySQL 5.7 Reference Manual :: 15.6.1 InnoDB Startup Configuration
- mutex 競合確認方法の例は以下。
1 2 3 4 5 6 7 8 9 |
|
innoDB_page_size
- デフォルト16k。16KBは、幅広いワークロードに適しているサイズ。特にテーブルスキャンを含むクエリだったり、バルクアップデートを含むDML操作など。
- 環境によって調整する。
データファイルアクセスに OSキャッシュを無駄に消費しないよう
innodb_flush_method
をO_DIRECT
にする。O_DIRECT
にすると、OSのDirect I/O機能を利用し、OSのキャッシュをバイパスする。MySQL は自前のキャッシュ機構があるので、OSキャッシュは冗長なので、バイパスしたほうがよい。- Linuxでは、多くの場合
O_DIRECT
にしたほうがオーバヘッドが下がる
innodb_log_file_size
- ログファイル(WAL: Write Ahead Log)はトランザクション更新ログ。ログファイルがどの程度頻繁に切り替わっているかをチェックしておく。
- 大きくしすぎるとOSキャッシュに載らず、性能劣化する場合あり。また、クラッシュ後のリカバリ時間が長くなる。
innodb_buffer_pool_size
の25%~100%。デフォルト45MB。最大リカバリ時間に影響するので考慮しつつ大きく設定する。
innodb_log_buffer_size
- InnoDB は、変更されたデータのレコードを、メモリ上のバッファに書き込み、トランザクションがコミットする前にログがディスクに書き込まれないようにすることで、大きなトランザクション のI/Oを節約する。
- 大量・巨大な blob を書き込む必要がなければ、4MB-8MBが適切。
innodb_flush_log_at_trx_commit
innodb_flush_log_log_at_trx_commit
が 1 の時、ログバッファはトランザクションがコミットされるたびにディスク上のログファイルに書き出され、データの整合性を最大限保つ。しかし、これはパフォーマンスへの影響がある。この値を2にすると、ログバッファはトランザクションのコミットごとにOSのファイルキャッシュへ書き出される。これにより、ACIDを意識しないのであればパフ ォーマンスを最適化・高速化できるが、OSのクラッシュ時には数秒分のトランザクションが消えてしまう可能
innodb_file_per_table
- テーブル単位でOS上のデータファイルを分ける設定
- ディスクI/O分散やibdata ファイルの肥大化を防ぐためONを推奨
- pros / cons: MySQL :: MySQL 5.7 Reference Manual :: 15.7.4 InnoDB File-Per-Table Tablespaces
- ibdataファイル肥大化時の運用を検討する必要あり: 漢(オトコ)のコンピュータ道: InnoDBのファイルサイズ管理
Double Write Buffer
- クラッシュ時のpartial write (書き込み途中状態での終了) によるデータページ破壊への対策機能で、InnoDBで使われているファイルFlush技術。
- InnoDBデータファイルにページを書き出す前に、隣接領域 (これをdoublewrite bufferと呼ぶ)にまず書き出す。double write bufferへの書き込みおよびFlushが完了したら、InnoDBはデータをデータファイルの適切な位置に書き込む。これによりページファイルへの書き込み途中にクラッシュしても (OSやストレージサブシステム、mysqldプロセス)、doublewrite buffer からクラッシュリカバリできる。ただし、これはI/Oオーバヘッドが2倍以上かかるため、性能とのトレードオフ。
- 障害復旧可能性を高めるため、
innodb_doublewrite = ON
(デフォルト設定) が推奨
- クラッシュ時のpartial write (書き込み途中状態での終了) によるデータページ破壊への対策機能で、InnoDBで使われているファイルFlush技術。
innodb_thread_concurrency
で同時実行スレッド数を調整する。- 処理を並列に行うとスループットの向上が期待出来るが、同時に実行中のスレッドがあまりに多すぎるとロックの競合が多発してしまって返って性能を落とす原因になってしまう。
- 5.5 以上であればリミットをかける必要はない。現代的なOSであれば、マルチコア・プロセッサのコンテキストスイッチを効果的に実施してくれる。0は上限なし。デフォルトでOK
- 並列度で問題が出た場合は、値を調整する。この場合の推奨値は、CPU 数の 2 倍にディスクの数を足したもの。動的変数なのでオンラインで変更可能。
InnoDB compression (圧縮機能)
- 以下のメリットがある
- DBデータファイルサイズを縮小化
- I/O削減
- スループットの改善
- データ量削減
- 条件
innodb_file_per_table
を有効化することinnodb_file_format
が Barracuda であること
CREATE TABLE
もしくはALTER TABLE
でROW_FORMAT=COMPRESSED
かKEY_BLOCK_SIZE
を指定する
- 以下のメリットがある
innodb_io_capacity
- InnoDB のバックグラウンドタスクに使用するI/Oキャパシティ(IOPS)の上限を設定する。デフォルト200。
- 高速なストレージを使用している場合は拡大する。
- デフォルト値 (200) は、ストライプされた2本のディスクを目安にした値
innodb_read_io_threads
,innodb_write_io_threads
- 高速なストレージを使用している場合は拡大する。
- デフォルト値 (4) は、一般的には十分な値
コネクションスレッドプール関連
max_connections
- サーバが許容可能なコネクション数。デフォルト151。
- 多すぎるとメモリを消費し切る可能性あり
thread_cache_size
- スレッドをコネクションの切断後にもキャッシュしておく数。
- 一般的には
max_connections / 3
- 計算式
8 + (max_connections / 100)
により自動計算。
sort_buffer_size
- ソート用のメモリサイズ。このサイズを超えるとディスクを利用する。OLTPでは256KB〜1MBを割り当てる。
クエリキャッシュ関連
query_cache_size
- クエリキャッシュに割り当てるメモリサイズ
- 一般的には32MでOK。デフォルト1MB。
query_cache_type
- 最悪のケースでは、パフォーマンスのオーバヘッドが約15%
- select 比率が高いサーバで有効
- DEMAND に設定すると、クエリ実行時にSQL_CACHE句をつけたクエリだけキャッシュ可能
その他
autocommit
は高負荷なのでOFFが推奨。- MySQL :: MySQL 5.7 Reference Manual :: 15.1.2 Best Practices for InnoDB Tables
- トランザクション文で明確にコミットすること
Read-Ahead
- read-aheadリクエストは、バッファプールへ近い将来必要になりそうな複数のページを先読みするI/Oリクエスト。1エクステントの全ページに対してリクエストする。I/O性能の改善効果がある。Linear read-head(デフォルト)とRandom read-headの2つのアルゴリズムがある
- この関連パラメータはチューニングする余地あり
再起動時のウォームアップ運用
- バッファプールの中身を保存・リストアする機能がある。この機能を利用して、再起動後、データをキャッシュに載せ高速化する。
- MySQL :: MySQL 5.7 Reference Manual :: 15.6.3.8 Saving and Restoring the Buffer Pool State
2. アプリ (SQL) チューニング
適切なSQLを使う (クエリ最適化)
- まずはスロークエリの分析
SHOW FULL PROCESSLIST
で現在実行中の時間がかかっているクエリを特定する- EXPLAIN で実行計画の評価
- たとえば、全件フェッチしてからアプリケーション側で行を絞り込むというようなロジックはNG
- フェッチする行数を減らすようなクエリを組み立てる。
- サブクエリをJOINに書き換えたり、インデックスを適切に貼ったり。
テーブルの最適化
- 適切なデータタイプを選択する
- 数値をVARCHAR(桁数)はNG。INT (もしくはBIGINT) にすることで高速かつデータサイズが小さい
- インデックスを適切に作成する
- 付けすぎると更新時のオーバヘッドが増える
- 無駄に付けない。たとえば、性別などの二値にインデックスを張っても意味がない。
- 適切に正規化する
- JOIN処理により性能が出ない場合は非正規化も検討する
- 適切なデータタイプを選択する
適切な粒度でcommitする
- グループコミットの利用
レプリケーションを利用した負荷分散
- 参照処理はSlave側を向ける、など
コネクションプーリングする
Innodb compression
- テーブル単位に圧縮オプションを選択可能
- DML操作の実行時に compresson failure を引き起こす可能性あり。パラメータをチューニングする。
- 繰り返しの値や一般的に文字列 (CHAR, VARCHAR, TEXT or BLOB列) は圧縮効果が大きい
- ほとんどバイナリデータ (integer or floating point number) や事前に圧縮されているデータ (たとえば JPEG た PNG) は圧縮効果が少ない
- 画像データが入っていないテーブルに適用すると効果あり
性能測定
推測するな、計測せよ。
設定を変えたり、プログラムを変えたりしても、効果を測れないと意味がない。
MySQL のステータス
- MySQL :: MySQL 5.7 Reference Manual :: 15.6.3.9 Monitoring the Buffer Pool Using the InnoDB Standard Monitor
INFORMATION_SCHEMA
INNODB_CMP*
: 圧縮の状況INNODB_TRX
,INNODB_LOCKS*
: ロック状況INNODB_SYS_*
: システムテーブル (メタデータ)INNODB_BUFFER_*
: バッファプール
- PERFORMANCE_SCHEMA, sys スキーマ
- InnoDB 監視
- Pending file IOリクエスト
- バッファプール統計
- ロック、mutex
SHOW ENGINE INNODB STATUS
性能測定ツール
- innotop
top
風に MySQL のステータスを表示するツール- innotop/innotop
- dstat, sar
- OSレイヤの性能値を取得する
ベンチマークツール
- DBT2
- SysBench
- mysqlslap
- 標準の負荷エミュレーションクライアントプログラム
- MySQL :: MySQL 5.7 Reference Manual :: 5.5.9 mysqlslap — Load Emulation Client
- flexAsynch
- MySQL Cluster用のテストツール