MySQL基礎まとめ
エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド
- 作者: 奥野幹也
- 出版社/メーカー: 技術評論社
- 発売日: 2010/06/12
- メディア: 大型本
- 購入: 16人 クリック: 204回
- この商品を含むブログ (33件) を見る
- 作者: Baron Schwartz,Peter Zaitsev,Vadim Tkachenko,菊池研自(監訳),株式会社クイープ
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/11/25
- メディア: 大型本
- この商品を含むブログ (6件) を見る
インデックステーブル
B+Tree
- 1つのノードがm個 (m>=2) の子ノードを持つことができる平衡木構造
- ソートした結果をルートノードに格納して、いくつかの範囲に分ける
- ルートノードの各範囲について子ノードが存在するように配置する
- 子ノードが親ノードに従属するように、全てのノードを構成する
- リーフにのみデータ(ポインタ)が存在する
- ルートノードから各ノードを辿っていくことで目的のノード(リーフ)を探し出すことができる(計算量はO(logN/logM))
InnoDBのインデックステーブル
- Primary Indexはクラスタインデックステーブルに格納される
- クラスタインデックステーブルのリーフには行データそのものが格納される
- リーフの行データはPrimary Indexをソートした結果で順番に並んでディスク上に保存される
Primary Indexが連続している複数の行を取り出す場合は、シーケンシャルアクセスとなり非常に高速となる
Primary Index以外のSecondary Indexはセカンダリインデックステーブルに格納される
- セカンダリインデックステーブルのリーフにはPrimary Indexが格納される
Secondary Indexが連続していても、Primary Indexが連続していなければ、行データを取り出す場合にランダムアクセスとなり遅くなる
Primary IndexをAUTO_INCREMENTにすることで、必ずPrimary Indexの値が既存の値よりも大きくなり、B-Treeへの追加が高速になる
InnoDBはよく使うクエリに対して、アダプティブハッシュインデックステーブルというバッファ上で利用できるテーブルを構築する(ハッシュをキーとしてテーブル検索するので計算量はO(1))
- MyISAMの場合は、インデックステーブルのリーフには行データを指すポインタが格納される
Covering Index
- Secondary IndexもPrimary Indexのようにクラスタインデックステーブルに格納できないだろうか
- 複合Index(インデックスのタプル)を使うと、複合Indexのインデックステーブルのリーフには、複合Indexに含まれるデータ(と完全な行データへのポインタ)が格納される
- ただし、取得するデータと条件句で指定するデータが、全て複合Indexに含まれないといけない
- また、複合IndexのTop Index(一番左に指定したインデックス)を条件句で指定しないといけない
- 上記の条件を満たさない場合は結局ランダムアクセスが発生し遅くなる
WAL (Write Ahead Log)
- まず、更新内容はメモリ上のバッファに書き込まれる
- 同時に、ディスク上のログファイルにも書き込まれる
- ログファイルは常にシーケンシャルアクセスが可能なため、ディスク上でも高速に書き込める(永続的なメディアへの同期が実現できているといえる)
- ログファイルはリングバッファとなっており、その最大容量を指定できる
- ログファイルの最大容量を使い切った時点で、あるいは、バッファ上のダーティページがある割合を超えた時点で、バッファの内容がディスク上のテーブルスペースに書き込まれる
- 障害発生時はバッファ上に書き込まれた内容は失うが、ログファイルには残っているため、テーブルスペースの内容にログファイルの内容を追加することで復旧が可能
- サーバ起動時に、テーブルの内容とログファイルの内容を比較して、ロールフォワードによる復旧が自動的に行われる
DoubleWrite
- バッファプールのテーブルの内容は、ログファイルを使い切った場合、Dirty-page(更新されたページ)が多くなった場合、にディスク上のテーブルスペースに書き込まれる
- テーブルスペースに書き込む際、まずダブルライトバッファと呼ばれるディスク上のスペースに書き込む
- その後、テーブルスペースの該当のページに書き込む
- これにより、テーブルスペースへ書き込んでいる最中のクラッシュによりデータを失っても、ダブルライトバッファから復旧できる
MVCC (Multi Version Concurrency Control)
- InnoDBはRead/Writeにおいて、テーブルをロックせず、行をロックする
- InnoDBのデフォルトの分離レベルはREPEATABLE_READである
- MySQLでトランザクションの4つの分離レベルを試す - FAT47の底辺インフラ議事録
- REPEATABLE_READでは、あるトランザクションがある行を更新しても、それより以前に開始したトランザクションには常に更新前の値を見せる
- あるトランザクションがある行を更新したとき、その行の新しい値と共に新しいトランザクション番号が割り振られる
- その行の古い値(+古いトランザクション番号)は退避され、その行には退避されたデータへのポインタが与えられる
- トランザクションが行を読み取るとき、自分より古いトランザクション番号を見つけるまでポインタを辿り、退避されたデータを読み取る
- 退避されたデータは、そのデータを必要とするトランザクションが終了する(ロールバックするかコミットする)まで、消去されない
レプリケーション
- データベースに対する全ての変更は、SQLクエリの形(Statement Based Replication)か行データそのものの形(Row Based -)、あるいはその両方の形(Mixed Based -)で、バイナリログというログファイルに保存されている
- マスターのマスタースレッドがバイナリログを読み込む
- スレーブのI/Oスレッドがマスタースレッドに接続し、バイナリログを取得する
- I/Oスレッドがバイナリログを、スレーブ上のリレーログに保存する
- スレーブのSQLスレッドはリレーログを読み込み各テーブルを更新する
- 負荷分散として、参照系のクエリは複数のスレーブで処理する
クエリ最適化
- MySQLのオプティマイザはユーザからのクエリを、最適なクエリに変更して実行する
- 例えば、INを使っていても、内部的にはEXISTSを使ったクエリに変更される
- MySQLでは、結果がベクトルになるようなサブクエリを扱うことができない
- MySQLでは、クエリは、JOIN/UNION/サブクエリ、に分類できる
- JOINは各テーブルの行をfor文でネストしたような形で各行がアクセスされる(Nested Loop Joinアルゴリズム)ため、インデックスを使用しないと遅くなる
- EXPLAINの出力を見ることで、インデックスが使われているか、ソートが実行されているか、一時テーブルが作成されているか、などを判別できる
チューニング
セッション
- セッション(クライアントからの接続)ごとにスレッドが起動される(epollのようにノンブロッキングではない)
- max_connections
- innodb_thread_concurrency
- セッション同時接続数を考慮して、スレッドの消費可能メモリ量を決める必要がある
- sort_buffer_size, read_buffer_size, read_rd_buffer_size, join_buffer_size, tmp_table_size
バッファ
- バッファ上のクエリキャッシュやテーブルキャッシュを最大限活用することで、ディスクI/Oが減り、レスポンス性能が向上する
- query_cache_size
- query_cache_type
- innodb_buffer_pool_size
同期
- クラッシュリスクと性能のトレードオフを考えて、バッファの情報をディスクに同期する間隔を指定する
カウンタテーブル
- 別のテーブルに同じカウントを付ける場合、カウンタ値のみを持つテーブルを用意してカウンタを払い出してテーブル間で共有する
- ただし、そのカウンタテーブルにカウンタ値のみ(つまり行がひとつ)を持たせると、テーブルロックはもとより行ロックによっても並行性を落とすことになる
- 行を複数用意してランダムに更新していき、SELECT SUM(cnt) FROM counter_table でカウンタを読み取る、などの対策が必要になる
Explain
- スロークエリログなどから時間のかかるクエリを取得し、Explainコマンドでその実行計画を分析してクエリを修正する
- type=ALLのテーブルフルスキャンや、type=indexのIndexフルスキャン、などはクエリ改善の余地がある
準同期レプリケーション
- MySQL 5.5から導入された、Fast failoverを実現する機能
- MySQL Semi-Synchronous Replication in Facebook - YJのダイアリー