ゼロから始めるDBチューニング

スピーカー

@tenn_25 さん

スライド

Googleスライド https://docs.google.com/presentation/d/1YyeNLabcYvgtWLgqqHhZC1mTPEHbMLl35HsE3RBcytQ/edit#slide=id.p 

#DBチューニング ##今回のゴール 達成目標

  • チューニングの観点を知る
  • データベースがどう動いているかを理解する
  • インデックス、統計情報、実行プランを理解する

#本回の注意事項

  • RDBMS(relational database management system)に限る。製品によっても細かいところが違います
  • Microsoft SQLServerを例とする

#目次

  1. チューニングのアプローチ
  2. データベースの構成要素
  3. データベースのデータ構造
  4. インデックスのデータ構造
    • クラスターインデックス
    • 非クラスターインデックス
    • 統計情報
  5. デモ

#チューニングのアプローチ 語録:DB 上から見るか?下から見るか?

  • SQLの見直し 
  • ループ処理、サブクエリ、JOINなどの見直し 
  • 論理設計の見直し 
  • テーブル設計見直し、非正規化など
    パフォーマンスだけならば、実は非正規化の方がよいことがある。(データ関係が複雑になっていないので)
  • DB内部の設定の見直し 
  • DB内の細かい設定変更、インデックス ← 今回の話はこれ
  • 物理設計の見直し 
  • 負荷分散、並列処理、RAIDやファイル構成の見直し、スケールアップ/アウト(札束で殴r

#効果が大きいところを見つけて対処していこう

  • ボトルネック以外をチューニングしても意味ない
  • ボトルネックの8割はディスクI/O(※個人調べ)
  • いかにディスクI/Oを減らすかが鍵!
  • 表面的な症状を見てるだけでは根本原因は分からない。
    リソース負荷や症状はあくまで「予想の裏付け」程度の参考にする。
    zabbixで監視して結果出ても、予想の裏付けぐらいだけ・・・となるので、過信しない。

そのため、チューニングをしっかり行うならば、
★データベースの内部の情報を調べる必要がある。
★データベースの仕組みを知っている必要がある。

  • データベースの内部情報を調べる

  • トレース(SQLServer Profiler)、パフォーマンスモニタ、動的管理ビュー(DMV)、クエリストア

  • とりあえずスロークエリ(時間のかかるSQL)を調べよう!

  • データベースの仕組みを知る

  • どうやってクエリが実行されるか

    • どうやってデータを検索しているか

データベースの構成要素

(ここからDB構成要素概念図を使用。資料見ましょう)

データベースの要素は大きく分けて2つ + 2つの物理

  • リレーショナルエンジン(クエリプロセッサ)
  • データベースエンジン(ストレージエンジン)
  • ストレージ
  • メモリ

リレーショナルエンジン

クエリ(ソフトウェアに対するデータの問い合わせや要求)が投げられると、クエリのSQLをコンパイルする。
コンパイルされたものを実行するために、統計情報(後述)を元に、どのようにデータを探すか計画を立てる。
過去に同じクエリを実行した履歴が残ってればそれを流用する。

  • プランキャッシュ
    その名の通り、計画の履歴のこと。
    *「クエリをどうやって情報取得してこようかな」という実行プランを立てるために、「キャッシュに残っていないかな?」*と探す。

データベースエンジン

実行プランに従ってクエリを実行する。
トランザクションやロックの制御、メモリ管理をしながらデータにアクセスする。
デファクトスタンダードはInnoDB。

ストレージ

データ操作のログはログファイルに格納されている。
実データや、その他諸々はデータファイルに格納されている。

メモリ

ストレージのデータを操作するには、 必ずメモリに必要なデータを持ってくる必要がある。

データベースのデータ構造

データを扱う最小単位をページと呼ぶ。
SQL Serverだと8KBのブロックに分かれている。
実データは行単位でページに格納される。

データベースのデータ構造

ヒープ…実データそのもののこと。ストレージ上でバラバラ。綺麗に並んでない。
インデックス…検索を高速化させるためにテーブルに付与する情報。索引。

インデックスのデータ構造

主にB-Treeという木構造から成るインデックスが採用されている

  • ルートノード:インデックスの始点
  • 中間ノード:次の階層への複数階層存在する
  • リーフノード:目当てのヒープページの場所が書いてある
  • B+Tree:リーフノードが次のリーフへのポインタを持つ

非クラスター化インデックス

(SQL server用語。他のRDBMSにない…かも?)

(ここからインデックスの図、各ノードの表を使用。資料見ましょう)
例えば以下のSQLを実行

SELECT * FROME UserTable WHERE UserID = '1202'

⇒実行されれば、まずルートノードから検索がいく。
ルートノードから中間ノードへ行く。
さらに、中間ノードからリーフノードに行く。
しかし、リーフノードには実際のデータへの参照先が含まれているだけ。
最終的に、実際のデータへ飛ぶ。

索引から目当てのページ(ヒープ)に飛ぶ必要がある(RID Lookup)
★技術書などの索引と同じ!
「何ページにあるか」までは書いてあるが、それが文書の中でたまたま使われた言葉なのか、
その言葉の説明がきっちり書いてあるのかまでは、そのページを見に行かないとわからない。
⇒実際のページ(ヒープ)に探しに行くのが多くなると、
索引を追うので大変になるので、遅くなる。

クラスター化インデックス

メインのインデックス。
テーブルの主キーにつけられる。

例えば以下のSQLを実行

SELECT * FROME UserTable WHERE UserID = '1202'

⇒実行されれば、まずルートノードから検索がいくのは非クラスター化インデックスと同じ。
ルートノードから中間ノードに行く。ここも同じ。
中間ノードからリーフノードに行く。 リーフノードには実際のデータが格納されており、インデックスのキーでソートされている。

五十音順に辿って行くと、目当ての言葉に直接辿りつく!
★辞書と同じ  データがソートされている。

RID Lookupが発生しないクラスター化インデックスの方が速い。

語録?:クラスター化インデックスの方が偉い!

(参考)MySQLのセカンダリインデックス

クラスター化インデックスはSQLServerもMySQLも同じ。
しかし、検索が、クラスター化インデックスのルートノードからいかない所が違う。

以下のSQL例

SELECT * FROME UserTable WHERE Name = 'Mary'

先に、セカンダリインデックスのルートノードから検索が走る。
セカンダリインデックスのリーフノードには主キーの値が格納されている。
主キーの値を使って、クラスター化インデックスを検索していく。

統計情報

統計情報は実行プラン(クエリプラン)を決めるために使われる
-サンプルが多すぎても良くない(時間がかかる)
DBへの負荷もかかる。
-サンプルが少なすぎても良くない(信憑性に欠ける)
-統計情報自体が古くなると、それもまた良くない(信憑性に欠ける)
-インデックス構築時や、ある程度データが更新された時に再作成される

デモ

@tenn_25さんの画面共有で、SQL Server Management Studioを実践
あるテーブルに対して、以下のように条件を変えて、SELECT文を実施。

  1. インデックスを何もつけない
  2. 主キーにクラスター化インデックスをつける
  3. インデックスを意図的に無効化
  4. 主キーとは別のものに、クラスター化インデックスを張る...?
  • (上記のクラスター化インデックスの定義に反してしまっているので、別の言葉か?)

終了後のいろんな質問タイム!

インデックスを頑張って張りまくるとどうなるの?

インデックスを付けると更新箇所が増えるので、更新処理[INSERT,UPDATE,DELETE]は遅くなる。張りすぎはトレードオフ。

DB製品での違い

[SQLServer/MySQL]クラスター化インデックス=[Oracle]索引構成表
⇒リーフノードに実データがソートされた状態で格納されている。

[SQLServer]非クラスターインデックス=[Oracle]インデックス?? (不明確)
⇒リーフノードヒープ(実データ)への参照が格納されている。

[MySQL]セカンダリインデックス
⇒リーフページに主キーが格納されており、それを使ってクラスター化インデックスを検索する。

索引構成表というのがクラスター化インデックスと同じっぽい。

ScanとSeekの違いって?

スキャンとシークを比較するとシークが早い。

  • スキャンは対象のデータを0から100までチェックする処理
  • シークはインデックスのツリーを辿っていき、欲しいデータにだけアクセスする処理

Index Scanはインデックスのリーフノードを順に調べて、目的のデータを探している
Index Seekは、Bツリーインデックスをルートから順にたどって、最短の手順でデータを探し出している

クエリ?がどこまで同じなら、クエリキャッシュにHITする?

一言一句同じ&データが更新されないことが条件
データが更新されたらクエリキャッシュは破棄される