InnoDB のパフォーマンスチューニング - MySQLカンファレンス2007


(「InnoDB」は「いんのでーびー」と言うらしい...今まで「いのでーびー」と言ってました)

InnoDBをなぜ使うか

クラッシュリカバリ

  • MyISAMはデータ量の増大とともに時間がかかる
  • InnoDBはデータ量の増大との相関がない

InnoDBチューニングの王道的アプローチ

  • クエリを改善して全体的に処理効率を上げる
  • データサイズをできるだけ小さく
  • メモリをできるだけ多く積む

コミット性能(同期書き込み)

  • innodb_flush_log_at_trx_commit=1,0,2
    • 0 1秒ごと
    • デフォルト:1 コミットのたびに同期書き込み
    • 2 1秒またコミットのたび
  • マスターは1を推奨

同期書き込みのパフォーマンス

  • fsync() (あるいはO_SYNC + write, fdatasync()) の速度に依存
  • ディスクに対する同期書き込み1回の所要時間
    • シーク待ち時間 4ms
    • 回転待ち 4ms
    • 転送時間 1ms
  • 合計 9ms

同期書き込みのパフォーマンス向上

  • ライトキャッシュの使用
  • トランザクション間隔を適切にとる
    • 「1件ごとにコミット」は避ける
  • グループコミット機能
    • DBの機能
    • ある時間内に同時にきたコミットをいっぺんに同期書き込み
    • 5.0でのInnoDBは、バイナリログと併用するとグループコミットがきかない

テーブル設計やSQLの記述はきわめて重要

  • SQL文の実行計画には注意する
    • インデックスの効果的な利用
    • 複雑なSQL文をシンプルにする
    • EXPLANの結果に気を配る
  • サマリーテーブルの導入などは効果的

よくある問題

  • 主キーは重複しないようにUUIDにした
  • とりあえずインデックスつけた
  • ...

クラスタインデックスと主キー

  • クラスタインデックス
    • 主キー値をキーに、残りの列値を全て取得
  • セカンダリインデックス
    • インデックス値をキーに主キー値を取得
    • クラスタインデックスから...
  • このため、主キー検索は高速になり、それ以外のインデックスによる検索の性能は落ちる

クラスタインデックスの特性を生かす

  • 主キーは必ず定義する
  • 可能な限り主キーでLookupする
  • INSERT系処理は、主キー値を昇順に割り当てる
  • 主キーそのものの更新はコストがかかる
    • 設計上もよくない
  • 主キーのサイズは小さくする
    • セカンダリインデックスのサイズに影響する
    • UUIDよりも整数型
    • AUTO_INCREMENTについては注意が必要
  • セカンダリインデックスだけで完結するクエリを書く

AUTO_INCREMENTとスケーラビリティ

  • InnoDBのAUTO_INCREMENTの実装
    • テーブルロックを一時的にかける
    • 同時INSERTが増えれば競合により性能が落ちる
    • 明示的に値を割り当てる場合でも同様
  • MySQL5.1で実装の変更

巨大な列の扱いには注意

  • InnoDB Buffer Poolには全ての列が読み込まれる
  • ので、Text型は分けたほうがよいときがある

メモリ領域を有効に使う(ヒット率を上げる)

  • SELECTで列名指定しなくてもInnoDBバッファプールには読み込まれる
    • MySQLサーバからクライアントには返されない
  • 1対1関連が効果的な場合がある

データ型と文字エンコーディング

  • 内部的にMEMORY(またはMyISAM)のテンポラリテーブルを作ることがある
    • EXPLAINで"Using temporary"と出る場合
    • 主にグループ関数を使う場合にこのパターンになる
    • TEXT/BLOGを含む場合はMyISAMになる
    • tmp_table_size, max_heap_table_size(デフォルト16MB)
  • MEMORYは実際の列値ではなく、定義したデータがたぶんだけメモリを確保する
  • 文字コード変換はCPUを使うのでできるだけ避ける

RDBMSとメモリ領域に関する一般論

  • 全体論(特に読み取り)に関して
    • OSキャッシュは汎用的である一方、RDBMSのキャッシュはRDBMS用に最適化されている
    • RDBMS cache <-> OS cacheのやりとりはコストがかかるので避けられるなら避けた方がよい
    • だからRDMBS cacheを活用した方が速いはず
  • 更新に関して
    • RDBMSのキャッシュサイズと、チェックポイントの頻度には強い相関がある
    • 高頻度のチェックポイント...

innodb_buffer_pool_size

  • パラメータの意味
    • InnoDBバッファプールサイズ
    • デフォルト8MB
    • Dirtyな(更新された)領域の合計がこの値に近づくとチェックポイント
    • Dirtyでない領域は、サイズが圧迫されるとたんに退避される
  • 設計指針
    • デフォルト値は少なすぎる
    • InnoDB専用構成であればRAMの7〜8割を割り当てて良い

チェックポイントに影響のあるパラメータ

  • innodb_log_file_size(5MB), innodb_log_files_in_group(2)
    • デフォルト少なすぎ
  • innodb_max_dirty_pages_pct(90)
    • パーセント単位
    • この値を上回ると強制的にチェックポイント
    • 通常デフォルトでok
  • innodb_doublewrite(ON)
    • 保険でディスクに2回書き込む方式
    • リカバリの確実性をあげる実装

InnoDBとロック生業

  • 行レベルロック
  • SELECTはロックをかけない
  • ロックエスカレーションは発生しない
  • Next-Key Lockingによる不整合防止

Next-Key Locking

  • InnoDBログファイルとバイナリログの不整合を防ぐために必要な実装

Next-Key Lockingの無効か

  • innodb_locks_unsafe_for_binlog
    • Next Key Lockingを無効化する
    • Statementベースのバイナリログだと、不整合を起こす危険がある
    • ...
  • MySQL 5.1
    • 分離レベルをREAD COMMITTEDにすることと、バイナリログを「Row Based」にすることで無効化できる

その他のパラメータ

  • innodb_thread_concurrency
    • innodb内部でのスレッド数
    • デフォルト8
    • マルチコアCPUのスケーラビリティ問題は5.0.30で大幅に改善
    • 0にすることで最大のスループットを得られることが多い
    • 接続の増加によって急激に落ちるようであれば、4〜20程度で調整すると良い
  • innodb_support_xa
    • バイナリログとInnoDBログファイルの同期をとる(2相コミット)
    • 5.0から
    • デフォルトON
  • innodb_autextend_increment
    • 単位時間あたりのデータほげほげ
    • デフォルト8MB すくない
  • innodb_file_per_table
    • テーブルごとにファイルを作成
    • デフォルトoff
  • table_cache
    • 同時実効性をあげるための重要なパラメータ
    • 目安は 同時接続数 x テーブル数
    • 1024〜2048が一般的
  • thread_cache
    • 切断した後に、再接続を軽いコストでできるようにするためのキャッシュ
    • コネクションプールを使わない場合に効果的
    • 数百程度にすることも珍しくない

5.1での変更内容

  • AUTO_INCREMENTの同時実効性の向上
  • 透過的なテーブルの圧縮(オプション)
    • row_format=COMPRESSED engine=InnoDB
    • CPU使用率が若干増加す
  • インデックスの追加/削除が高速になる
    • 5.0までは1行1行処理してた
    • 5.1からはインデックス領域だけの再作成ができるようになった
    • 6.0ではオンラインで再作成の予定
  • Next Key Lockignの無効か
  • ...

シャットダウン時間の短縮

  • シャットダウン時に行われる処理
    • InnoDBバッファプール全領域のチェックポイント
  • シャットダウン時間の短縮方法
    • SET GLOBAL innodb_max_dirty_pages_pct=0;
    • しばらくまつ
    • SHOW GLOBAL STATUS like 'innodb_buffer_pool_pages_dirty';
      • この値がゼロに近づく(十分に小さくなる)のを待つ
    • mysqladmin shutdown

ALTER TABLE, LOAD DATAの高速化

  • SET GLOBAL ...trx_commit=0
  • 処理
  • 1

大量の更新処理

  • InnoDBはDELETEやUPDATEが行われてもすぐには削除しない
  • Purge用のスレッドが物理的な削除を行う
  • 更新処理の負荷が非常に高いと、Purgeが追いつかないことがある
    • SHOW INNODB STATUSの,"TRANSACTIONS"を見る
  • 多少性能を落として安定させることもできる
    • innodb_maxDirty_pages_pctを低い値にする
    • ...

マスターでは耐障害性、スレーブでは性能を追求

  • innodb_flush_log_at_trx_commit
    • マスター1
    • スレーブはほとんどのケースで0でおk
  • innodb_log_file_size
    • チェックポイント時間に影響
    • 大きめにとる
    • ...

マスターInnoDB、スレーブMyISAM/MEMORYの是非

  • スレーブが途中で止まったときの対処に留意する
  • SQLスレッドからの更新があるので、実際には参照オンリーではない
    • ALTER TABLEや巨大なUPDATEは、MyISAMだと参照をブロックしてしまう

ハードウェア選択

  • メモリ
    • 最も重要
    • 64bitがほとんど
    • 16GBめずらしくない
  • ディスク
    • RAID 1+0
      • 5は書き込み主体のアプリケーションに向かない
    • RAIDチャンクサイズ(ストライピングサイズ)
      • 最小I/O単位である、InnoDBのブロックサイズは16KB
      • 小さいチャンクサイズだと、1回の読み書きで複数のディスクにまたがった処理が必要
      • →トータルのランダムI/Oサイズが増え、スループット低下
      • 256KB程度が典型的
    • ライトキャッシュ+バッテリーバックアップ