問合せ最適化インサイド
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

問合せ最適化インサイド

on

  • 5,906 views

「とことんわかるPostgreSQLインサイド」(2006年)にて講演。PostgreSQLのSQL最適化機構について解説する。

「とことんわかるPostgreSQLインサイド」(2006年)にて講演。PostgreSQLのSQL最適化機構について解説する。

Statistics

Views

Total Views
5,906
Views on SlideShare
5,872
Embed Views
34

Actions

Likes
9
Downloads
76
Comments
0

1 Embed 34

https://twitter.com 34

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • 具体的なチューニングパラメータの内容がこの表である。 IO と CPU のコストとソート用バッファサイズは設定ファイルで調整し、統計情報は VACUUM ANALYZE 時に再計算される。いくつかのデフォルト値は、コンパイル時の定数になっている。 コスト : Cost シーケンシャルアクセス 1 ページが単位 選択度 : Selectivity WHERE, JOIN ON の条件を満たすタプルの割合 Distinct ある列の値が取る種類。重複したものは 1 種類と数える MCV(Most Common Value) 重複した値の多いもの順 Compressed Histogram 全体から MCV を除いた残りのヒストグラム。 初めに帯数を決定。 同じ数だけ分配されるように帯幅を調節。 OidFunctionCall( function-oid ) カタログに登録されている関数を間接的に呼ぶ。 関数追加が自由にでき、拡張性に富む。
  • 具体的な SQL が処理される流れとして見ていこう。 左側が SQL 、右側全体の構造が RelOptInfo tree である。 SQL は 3 つのテーブルを参照しているため、基底関連 ( テーブル ) は 3 つである。テーブルを組み合わせる場合は、常に 2 つずつ行われるため、どの 2 つを先に処理するかによって、 3 種類の方法がある。(これが右側) ただし、 WHERE で関係が指定されていないテーブル同士の結合は候補から落とす。なぜなら、単純に直積 (CROSS JOIN) を計算することになり、確実に悪いプランになるからだ。(というわけで、右側一番下は考えない)
  • この図は、 AB を先に、その後 AB と C を処理する流れである。ただし、同時並行で、 BC が先の場合についても行われている。 2 つのテーブルを参照して、それを組み合わせる場合、 2 つのことを考慮しなければならない。一つは、それぞれのテーブルに対する Scan 方法、もう一つは、その 2 つを連結させる Join 方法である。 あるテーブルが今回のクエリに関係のある、 N 個のインデックスを持っている場合、それらのインデックスに対する IndexScan と SeqScan を加えた、全部で N+1 個の Scan 方法がある。 また、 Join 方法は、 NestLoop, MergeJoin, HashJoin の 3 種類の方法がある。 Planner は、それぞれのすべての組み合わせについて、ボトムアップで(テーブルから上に向かって)コストを計算していく。 基本的には、既にそれよりも安い Path がある場合、新たな Path はすぐに捨てられる。しかし、それが Join 後で何かしらのカラムでソートされている場合、コスト的には効果でも、将来的な使えるかもしれないので、保持しておく。
  • VACUUM ANALYZE コマンドにて、 3 種類の統計情報を集計する。このとき、統計情報を収集するかいなかは、そのカラムの型が‘ =’ ‘<’ 演算子をサポートしているか否かで決定する。そのカラムに対して張られているインデックスは考慮しない。 一つ目は、 MCV: Most Common Values である。これは、重複のある値を、その重複数の多いものから順にならべたランキングである。 二つ目は、ヒストグラムである。 PostgreSQL では、 Compressed Histogram という工夫されたヒストグラムを使用している。 三つ目は、インデックス順と物理的なディスク上の配置順の相関である。これは、 B-tree のコストを計算する際に使用される。人工的ではあるもの、相関が 0 と 1 の場合について、どれくらいの選択率で B-tree IndexScan から SeqScan に切り替わるかを調べてみた。実は、予想以上に相関の影響は大きい。 物理的な並び順は、 CLUSTER コマンドにより整列できる。 CLUSTER コマンドは、実装的に VACUUM FULL の効果も兼ね備えているような?
  • 統計情報が利用できない場合は、選択度を見積もる

問合せ最適化インサイド Presentation Transcript

  • 1. PostgreSQL http://www.sigmodj.org/Events/tokoton-db0604.html 問合せ最適化インサイド NTT オープンソースソフトウェアセンタ 板垣貴裕 2006.4.24 1
  • 2. 問合せ最適化インサイド:概要 はじめに : 最適化とは? Plannerインサイド Executor概要 :何ができるのか? 前処理 :ルールベースで式を簡略化 パス選択 :コストベースでアクセスパスを列挙 後処理 :実行計画の生成 最適化に必要な情報 統計情報 選択度 まとめ 2
  • 3. 問合せ最適化とは? 問合せが与えられた場合に、 問合せ 最も適切な実行計画を得ること 最適化機構の入出力 Join 入力:問合せ 構文解析済みのSQL(内部表現) Scan Scan どのような 行が欲しいか? 木構造の問合せ内部表現 “次の行が欲しい” 出力:実行計画 実際の処理のパイプライン 実行計画 どうやって 欲しい行を得るか? 1行 3
  • 4. 例:テーブル2つの結合 Join SELECT * FROM A, B WHERE A.i = B.i AND i x i y A.x = 10 AND B.y < 100; テーブル A テーブル B 内部形式 Joinの方式 (= 条件を使う順序) Join クエリ A.i = B.i •A.xで絞込→対応するBを取得 A.x = 10 A.i = B.i B.y < 100 Scan on A Scan on B •B.yで絞込→対応するAを取得 A.x = 10 B.y < 100 B.y < 100 A.i = B.i A.x = 10 •A.x, B.yでそれぞれ絞込→結合 Scanの方式 A.x = 10 行へのアクセスパス A.i = B.i B.y < 100 •シーケンシャル読み取り •インデックス経由読み取り 4
  • 5. “最適な”実行計画とは? クエリを処理する複数の方式 複数の 複数 最適化の 最適化の余地 選ぶべき実行計画 処理時間が最も短い実行計画 処理時間 代わりに ルール / コスト コスト ディスクアクセス (read, write, seek) CPU演算 (その他:通信時間, 並列度) PostgreSQLでは考慮の対象外 5
  • 6. 速度感 と 最適化の原則 一般的な速度 (2006/4 現在) PostgreSQLは CPU (メモリ帯域) 1page = 8KB 6.4GB/s → 800,000 page/s ストレージのシーケンシャル読み込み (1台当り) 160MB/s → 20,000 page/s ストレージのランダム読み込み (1台当り) 100回/s → 100 page/s 最適化の原則 処理の回数を減らす CPUを消費して、ストレージアクセスを減らす ストレージはシーケンシャルに読み込む 6
  • 7. Planner インサイド PostgreSQLの問合せ最適化機構 Executor概要 ルールベース コストベース 7
  • 8. Plannerとは? PostgreSQLの問合せ最適化機構 別名:オプティマイザ(optimizer) ソースコード:src/backend/optimizer 規模 Planner 関連 DBエンジン全体 45K行 420K行 ストレージ マネージャ 関連 90K行 個別型定義 パーサとコマンド 110K行 60K行 8
  • 9. Plannerの位置づけ SQL解析 SQL パース処理 値の分布 アナライズ処理 Parser 実データを リライト処理 近似した情報 内部問合せ 問合せ Planner 統計情報 最適化機構 実行計画 Executor Storage 本講では データI/O Manager Black Box ストレージ 結果 アクセス 9
  • 10. Plannerの内部処理の概要 Parser ルールベース 式の書き換えに 関するノウハウ 前処理 (/prep) パス探索 パス探索 コストベース アクセスパス列挙 DP DP GEQO Planner 動的計画法 GEQO (/path) (/geqo) 遺伝的最適化 後処理 (/plan) アクセスパス選択 と 実行計画の生成 Executor 10
  • 11. Executor 概要 Parser Planner 統計情報 Executor 11
  • 12. Executorとは? 実際のデータ処理 Plannerが作成した実行計画に基づき、 データを読み込み、処理する ソースコード:src/backend/executor “次の行が欲しい” Executorの動作 実行計画 行をひとつずつ取り出す 1行 内部的には粗粒度で処理する場合もある シングルスレッド (8.1現在) Sort 実行計画はツリー構造 Join ツリーの各ノードが処理単位 Scan Scan 12
  • 13. 処理の種類 Scan:テーブルへのアクセス SeqScan :シーケンシャル IndexScan :インデックス経由 BitmapScan :インデックスをビットマップ化 Join:2つのテーブルの結合 NestLoop :入れ子ループ MergeJoin :ソートマージ HashJoin :ハッシュ値マッチング その他 Sort :並び替え Hash :ハッシュ化 Aggregate :集約 13
  • 14. Scan処理:テーブルへのアクセス SeqScan テーブル テーブル全体に対して シーケンシャルにアクセス 候補行が多い場合に有効 IndexScan インデックスとテーブルを インデックス 交互にアクセス テーブル 候補行が少ない場合に有効 3 4 ランダムアクセス BitmapScan 1 2 インデックスから得られた 候補行をビットマップ化し、 テーブルの候補行のみを取得 インデックス テーブル 候補行が中程度の場合に有効 2 1 3 スキップしながら 0 シーケンシャルアクセス 0 4 1 1 複数のインデックスを併用可能 ビットマップ 14
  • 15. Join処理:2つのテーブルの結合 NestLoop 外 内 二重ループ 外側1行ごとに内側ループを1週 外側ループ回数が少ない場合に有効 内側ループはIndexScanが望ましい 外 内 MergeJoin ソート済みの2つのテーブルを 同時にスキャン 双方の行数が多い場合に有効 B-treeインデックスがあると望ましい 外 内 HashJoin 内側ループのハッシュを作成 外側ループの行と付き合わせる ハッシュがメモリに収まる場合に有効 ハッシュ 15
  • 16. その他の処理 Sort メモリに収まる場合はクイックソート 収まらない場合は外部ソート ORDER BY用 or MergeJoinの前処理 Hash ハッシュ関数によって型値を整数化 ハッシュ関数 HashJoin, Aggregateの前処理 “文字列” 46 Aggregate (整数) 複数の行を1行に集約 Unique GROUP BY, DISTINCT 1 1 複数のアルゴリズム 1 2 HashAggregate (Hashを使用) 2 3 GroupAggregate (Sortを使用) 3 4 ソート済みの Unique (Sortを使用) 3 テーブルに対する 4 重複除去 16
  • 17. Planner 前処理 式の簡略化 Planner 式の事前評価 前処理 パス探索 パス探索 DP GEQO 後処理 17
  • 18. 式の簡略化 コストベース最適化の効率を高めるための式変形 最適化の単位 = サブクエリ → サブクエリの個数を減らす Join SubQuery Scan Join Join Scan Scan Scan Scan Scan ルールベース = 開発者のノウハウの書き下し 副問合せの引き上げ(pull-up) IN, ANY, サブクエリ, 明示的JOINなどを親クエリに一体化 OUTER JOIN を INNER JOIN に変換 例:SELECT ... FROM A LEFT JOIN B ON (...) WHERE B.y = 42; 関数のインライン化 関数内外を跨った最適化 (SQLで書かれた関数のみ) 18
  • 19. 例:明示的なJOINをFROM句に変換 SQL SELECT * FROM A, B WHERE A.i = B.i AND A.x = 10 AND B.y < 100; SELECT * FROM A JOIN B ON A.i = B.i WHERE A.x = 10 AND B.y < 100; SELECT * FROM A, (SELECT * FROM B WHERE y < 100) B WHERE A.i = B.i AND A.x = 10; Join A.i = B.i 最適化しやすい 表現に変換 Scan on A Scan on B A.x = 10 B.y < 100 19
  • 20. 式の事前評価 事前評価 暗黙の条件の追加 / 冗長な条件の削除 定数式(処理の間、結果が変わらない式)の最適化 条件の追加と削除 暗黙の条件の明示化 例:A=B and B=C ⇒ A=C 冗長な条件の削除 例:10 < x AND 20 < x ⇒ 20 < x 例:X OR TRUE ⇒ TRUE 定数式の最適化 Plannerの時点で先に結果を求めておく 例:abs(-5) ⇒ 5 20
  • 21. 関数出力の安定性 関数の出力の安定性は、関数ごとの属性に持つ CREATE FUNCTION name(…) [Immutable | Stable | Volatile] Immutable ある入力に対し、常に出力が一定 数学系関数 +, -, *, /, abs() 文字列操作 || (連結), length(), position() Stable ある入力に対し、そのトランザクション内ならば出力が一定 トランザクション開始時刻 now() 現在の状態 current_user() Volatile 実行するたびに出力が変化する シーケンス nextval() ランダム random() 現在時刻 timeofday() 21
  • 22. Planner パス探索 Planner 前処理 パス探索 パス探索 DP GEQO 後処理 22
  • 23. コストベースの最適化 コストとは? ディスクアクセス時間 共有バッファ状態は考慮しない 常にディスクから読むことを仮定 CPU演算時間 部分コスト 初期コスト (startup cost) 最初の 最初の行を取得するまでのコスト 総合コスト (total cost) 最後の 最後の行を取得するまでのコスト 実行コスト (run cost) 総合コスト – 初期コスト:行取得中 行取得中のコスト 行取得中 23
  • 24. 部分コスト と 実質コスト 処理する行の割合を考慮 実質コスト = 初期コスト + 割合×実行コスト 初期コスト 実行コスト 実際のコスト 総合コスト 最後の行の取得 最初の行の取得 割合の見積もり 基本 : 100% 初期コスト ALL, ANY : 50% •ソート Cursor : 10% •ハッシュ作成 EXISTS : 1 / 行数 •BitmapScan初期化 LIMIT N : N / 行数 24
  • 25. コスト計算に使用されるパラメータ コスト 名前 説明 cost I/O (sequential_page_cost) シーケンシャル読込 (基準) 1 random_page_cost ランダム読込 4 CPU cpu_tuple_cost タプル処理 0.01 cpu_index_tuple_cost インデクス タプル処理 0.001 cpu_operator_cost 様々な計算処理 0.0025 メモリ work_mem 作業用バッファサイズ 1024KB effective_cache_size ページキャッシュ効果 1000 page 複雑な処理は cpu_operator_cost を基に計算 ソート:2 * cpu_operator_cost * N logN (N : 行数) Bitmap同士の結合:100 * cpu_operator_cost 25
  • 26. Scanのコスト インデックス テーブル SeqScan I/O – 全ページ数×S CPU – 全行数×T IndexScan I/O 必要ページ数×S 必要行数×RI CPU 必要行数×I 必要行数×T BitmapScan I/O 必要ページ数×S 必要ページ数×RB CPU (IndexScanと同じ) 初期コスト (他は実行コスト) S (sequential_page_cost) R random_page_cost 値の大小関係 T cpu_tuple_cost •全ページ数 > 必要行数 > 必要ページ数 I cpu_index_tuple_cost •R > { RI, RB } > S C cpu_operator_cost 26
  • 27. RI : IndexScan時のディスク読み込みコスト random_ ページあたりのランダムアクセスコスト page_cost [default:4] 等量前後で 急激に変化 ∞で random_page_cost と等しい 1 ランダムアクセスの遅さは (= sequential_ バッファキャッシュにより page_cost) 低減できることを表現 0 1 2 3 4 ランダムアクセスページ数/effective_cache_size (≒メモリの何倍のデータか) 27
  • 28. RB : BitmapScan時のディスク読み込みコスト random_ ページあたりのランダムアクセスコスト page_cost [default:4] 少ないときは 完全ランダムアクセス 全件に近い場合は SeqScanと等しい 1 必要ページが 運良く (= sequential_ 重なる or 並ぶ とコストが page_cost) 減少することを表現 0 20 40 60 80 100 % 予想読み込みページ数/全ページ数 (≒全体のどれだけの割合を読み込むか) 28
  • 29. Joinのコスト 計算量 Join NestLoop : O(NM) Merge Join : O(NlogN+MlogM) Outer Inner Hash Join : O(N+M) N M コスト NestLoop 初期 Outer(初期) 実行 Outer(実行) + Outerの行数×Inner(総合) MergeJoin 初期 Outer(総合+Sort) + Inner(総合+Sort) 実行 CPUコストのみ HashJoin 初期 Inner(総合+Hash) + Outer(初期) 実行 Outer(実行) 29
  • 30. 作業用メモリ制限 work_mem 作業用メモリ領域サイズ ソート, ハッシュ, マテリアライズなどで使用 自らメモリ消費量を管理 (OSではなく) 他のトランザクションに悪影響を与えないため ソート方式の切り替え オンメモリ上ではクイックソート メモリが不足するとディスクを使った外部ソート 外部ソートの場合はディスクアクセスコストを追加 ハッシュ処理は外部化できない ソートに切替 (HashAggregate → GroupAggregate) ソートを見かけたら、work_memを増やすと良い場合も 30
  • 31. パス探索 動的計画法 Planner 前処理 パス探索 パス探索 DP GEQO 後処理 31
  • 32. 動的計画法とは? 仮定:最適経路中の部分経路もまた最適経路である 経路ごとの最適な解以外は切り捨ててよい 切 異なる経路 JOINの順序 結合は常に2つのテーブル間 (3つ以上の結合では複数の候補が存在) ソート状態 どの列に対してソートされているか? 後のソートで役に立つかもしれないため、記憶しておく 同一の経路内での自由度 Scan方式 (SeqScan, IndexScan, BitmapScan) Join方式と方向 (NestLoop, MergeJoin, HashJoin) (重複除去するタイミング) PostgreSQLでは最適化しない 32
  • 33. Joinの順番 テーブル A 結合 AB テーブル B 結合 ABC SELECT * テーブル C FROM A, B, C WHERE テーブル A A.foo = B.foo テーブル B 結合 ABC AND 結合 BC B.bar = C.bar 2つずつ テーブル C AND 結合 …; テーブル A 結合 CA テーブル B 結合 ABC テーブル C 33 関係の指定が無いため
  • 34. Pathの列挙 A •SeqScan AB •IndexScan •NestLoop •BitmapScan •MergeJoin •HashJoin ABC B C Scan SeqScan 有用なインデックスを使ったIndexScan 有用なインデックスを組み合わせたBitmapScan Join NestLoop (内外入れ替え) MergeJoin (ソート済み or コスト最小+ソート) HashJoin (内外入れ替え) 34
  • 35. パス探索 遺伝的最適化 Planner 前処理 パス探索 パス探索 DP GEQO 後処理 35
  • 36. 遺伝的最適化(GEQO) 利点 Joinテーブル数が増えても最適化に必要な時間が短い 動的計画法では O(N!) の時間がかかる あまり出番は多くない 12回以上のテーブルのJoinでのみ使用されるため 比較的多くのJoinでも動的計画法で十分な速さ デフォルト値はバージョンアップで変更されている 6.0以前 = GEQOなし 6.1- (1997/6) =6 6.3- (1998/3) =8 •CPUの高速化 6.5- (1999/6) = 11 •Plannerの改良 8.0- (2005/1) = 12 を反映 36
  • 37. JOINテーブル数 と 計画生成時間 SELECT * FROM tbl t1, tbl t2, ... tbl tN; 100 動的計画法 10 DP 1 0 5 10 15 20 0.1 0.01 遺伝的最適化 0.001 GEQO 0.0001 geqo_threshold=12 環境 Pentium4 1.4GHz PostgreSQL8.1.3 37
  • 38. 後処理 Planner 前処理 パス探索 パス探索 DP GEQO 後処理 38
  • 39. パスの選択 と 実行計画への変換 パスの選択 基本的には、実質コストが最小のものを選ぶ ただし、1%までのコスト差はランダム性あり 実行計画への変換 パラレルな構造 + 暗黙の処理の明示化 Pathをそのまま使わず、Planとしてコピーする Pathを含む最適化用の作業用メモリを一括解放 Path Plan Planでは Sort 明示化 Join Join Sort Hash Hash Group Scan Scan Scan Scan Limit 39
  • 40. 実行計画生成の仕上げ MIN/MAX用の特別な最適化 MIN/MAX を以下の形に置き換え ORDER BY column ASC/DESC LIMIT 1 集約関数にもかかわらずインデックスが使える 集約関数は基本的にはインデックスを使わない (SUM, AVERAGE, …) 実行計画のトップノードとして追加 GROUP BY 用の集約ノード ORDER BY 用のソートノード DISTINCT 用の重複除去ノード (Sort + Unique) 40
  • 41. まとめ:Planner Plannerの動作の流れ 式を簡略化し、最適化しやすい式に変形 アクセスパスを列挙し、コストを計算 コストが最も低いパスを選択し、実行計画を作成 Plannerの最適化の方針 Planner 自分自身のトランザクションのみを考慮 前処理 他のプロセス状態は考慮しない データがキャッシュ上にないことを仮定 パス探索 パス探索 共有バッファ状態は考慮しない データ書き出しは考慮しない DP GEQO 書き出しは別のプロセスが行う 後処理 41
  • 42. 最適化に必要な情報 統計情報 選択度 42
  • 43. 値の内容による最適な実行計画の変化 候補となる行数が変化すると、 最適な実行計画が変わる場合がある 例:SELECT * FROM A, B WHERE A.x = X AND B.y = Y AND A.id = B.id A.x = X の絞込みが強い 絞込みの強さ NestLoop 外:A→内:B B.y = Y の絞込みが強い Join A.id = B.id NestLoop 外:B→内:A IndexScan + HashJoin Scan Scan SeqScan + HashJoin A.x = X B.y = Y IndexScan + MergeJoin 43
  • 44. 最適化のために必要な情報 情報 選択度 (Selectivity) 絞込みの強さ;ある条件を満たす行数 統計情報 (Statistics) データ本体にアクセスしないまま、 具体的なデータ値を考慮した最適化がしたい 情報収集 ANALYZEコマンド 保存先 pg_stat システムテーブル pg_statistic システムビュー 44
  • 45. 統計情報 最適化に必要な情報 統計情報 選択度 45
  • 46. ANALYZEコマンド:統計情報収集 稼動状況統計とは ANALYZE table 直接関係なし ページ数 ページ数 行数 ヒストグラム (Compressed Histogram) Common値 (MCV: Most Common Values) 重複のある値を、重複数の多い順に並べたもの Uncommon値 (MCV以外) のヒストグラム 各帯のサンプル数が均一化されるよう帯幅を調整 NULL率 関係代数では X = Y と X IS NULL は別に扱われるため カーディナリティ (Cardinality) 濃度 or 値の種類(互いに異なる値の数) 相関 値の大小とディスク上の順序の相関 順序つきスキャンのコストに影響 行サイズ平均 ヒストグラム 46
  • 47. Compressed Histogram 個数の多い値を分離したヒストグラム ヒストグラムの精度を高めるため 参考:ジップの法則 (Zipf’s Law) N番目に多い要素が全体に占める割合は1/Nに比例 経験則 (値, 個数)のペア ヒストグラム(大小順) で保持 で保持 47
  • 48. サンプリング数 サンプリング係数(S) 範囲 : 1~1000 [default=10] 列ごとに指定可能 テーブル中最も大きな係数を使用 収集情報数 MCV = 10S MCV用作業領域 = 20S サンプリング行数 = 300S サンプリング数が足りない → 統計情報の精度が劣化 特にカーディナリティの精度が劣化しやすい なぜか上限は1000 最大サンプリング数 300K行 10M行以上のテーブルだと1000でも不足? 48
  • 49. 統計情報の 保存先 と 稼働中の補正 システムカタログ pg_class (テーブルのメタ情報) ページ数, 行数 pg_attribute (列のメタ情報) サンプリング数の係数 pg_statistic (テーブルの統計情報) NULL率, 平均行サイズ, カーディナリティ, ヒストグラム, 相関 統計情報はANALYZE時にしか更新されない VACUUMと共に1日1回程度が推奨されている 稼働中の変動に対する補正 現在のファイルサイズ 推定行数 = ANALYZE時の行数 × ANALYZE時の ファイルサイズ 49
  • 50. 選択度 最適化に必要な情報 統計情報 選択度 50
  • 51. 統計情報を使った選択度の算出 一致検索:x = K 赤字 統計情報 K ∈ Common値 MCVペアの値そのもの K ∈ Uncommon値 行数 – NULL数 数 MCVを除いた行数で計算 カーディナリティ 範囲検索:x < K Common値, Uncommon値を 併せたヒストグラム K ヒストグラム 51
  • 52. Joinの選択度:列1 = 列2 Common値, Uncommon値に分けてマッチング 1. 両辺のCommon値同士 2. 左辺の1でマッチしなかったCommon値と 右辺のUncommon値 3. 2の辺を入れ替えたもの 4. 両辺のUncommon値同士 左辺 右辺 Common値 Common値 Uncommon値 Uncommon値 52
  • 53. 複数の選択条件の認識 複数の選択条件 独立とみなす Selectivity(A and B) = Selectivity(A) × Selectivity(B) 範囲選択条件 Selectivity(A < x < B) = Selectivity(A < x) + Selectivity(x < B) - 1 最小値 A<x x<B 最大値 53
  • 54. 統計情報を利用できない場合 定数名 値 説明 DEFAULT_EQ_SEL 0.5% 等号 A = b DEFAULT_INEQ_SEL 33.3% 不等号 A < b DEFAULT_RANGE_INEQ_SEL 0.5% 範囲 b < A < c DEFAULT_MATCH_SEL 0.5% LIKE DEFAULT_UNK_SEL 0.5% IS NULL DEFAULT_NUM_DISTINCT 200 = 1/EQ_SEL (未知のテーブルの行数) ページ数/行サイズ or 1000 工夫されている点 行/ページ = ~100 程度を仮定 EQ は 1%(ページごとひとつずつ)より低い RANGE (0.5%) ≠ INEQ2 (11.1%) 54
  • 55. 最適化のために必要な情報:まとめ 動的な補正 SQL 一部処理にて ストレージ情報へ Parser 直接アクセス データ分布 内部問合せ ANALYZEコマンドによる データ値統計情報の取得 Planner 統計情報 実行計画 Executor Storage Manager ANALYZEコマンド 結果 実際のデータから 統計情報を計算 55
  • 56. まとめ SQL Parser 前処理 内部問合せ パス探索 パス探索 DP GEQO Planner 統計情報 実行計画 後処理 Executor Storage Manager 結果 56
  • 57. まとめ Planner : PostgreSQLの問合せ最適化機構 前処理 :ルールベースの式の簡略化と事前評価 パス探索:コスト計算とアクセスパスの列挙 後処理 :実行計画への変換 最適化に必要な情報 統計情報:データ分布のヒストグラム 選択度 :候補行数の推定 Plannerにうまく働いてもらうために… ANALYZEコマンドで統計情報を最新に保ちましょう EXPLAINコマンドで実行計画を確認しましょう 57
  • 58. 問合せ最適化インサイド Appendix 応用 Prepared Query テーブルパーティショニング Plannerへの不満 重複除去が苦手 問合せ最適化に対する拡張 OracleにあってPostgreSQLにない トラブルシュート EXPLAINコマンド 例:SQLの書き方とインデックスの使用可否 例:統計情報の精度不足 58
  • 59. 応用:Prepared Query 2系統のPrepared Query 最適化を 最適化を行う場所 簡易プロトコル: 簡易プロトコル:PREPARE + EXECUTE プロトコル SQLのPREPARE, EXECUTEを使った場合 (名前付き) 引数:“変数”として実行計画を生成 具体値ではないので、統計情報が使いづらい 拡張プロトコル: 拡張プロトコル:PARSE + BIND + EXECUTE プロトコル JDBCのPreparedStatementなどを使った場合 (無名) V3プロトコル (8.0以降) •libpq (C) 引数:“初回BIND時の定数”として実行計画を生成 •JDBC (java) 2回目以降のBINDでも初回の実行計画を使用 •PDO (php) 引数によって異なる実行計画を取るべき場合に不都合 •DBD (perl) 不満 再最適化の指示ができない (8.1現在) 無名Prepared Queryはただ一つしか保持できない パース済み構文や実行計画を、複数プロセス間で共有できない JDBCのPreparedStatementオブジェクトをプールする旨みが無い 59
  • 60. 応用:テーブルパーティショニング Constraint Exclusion 制約を利用してより強い最適化を行う CHECK制約と問合せ条件を比較し、矛盾する表を除外 例:レンジ・パーティショニング CREATE TABLE parent (key integer, ...); parent CREATE TABLE child1000 ( CHECK (key between 1000 and 1999) ) INHERITS (parent) ; child1000 CREATE TABLE child2000 ( CHECK (key between 2000 and 2999) ) INHERITS (parent) ; child2000 SELECT * FROM parent WHERE key = 2400; 不満 検索を展開 “パーティショニング機能”としては使いづらい (UNION ALL) Oracle 7.3 (1996) のパーティション ビューと同等 Prepared Queryと同時に使用できない 60
  • 61. 不満:重複除去(DISTINCT)が苦手 既にUniqueであることを認識できない SELECT DISTINCT i FROM (SELECT DISTINCT i FROM t) t; Unique → Unique → Sort → SeqScan Uniqueの前倒し最適化ができない SELECT DISTINCT t1.i FROM t1, t2 WHERE t1.i = t2.i; Unique → Join → 2×(Sort + Scan) SELECT t1.i FROM (SELECT DISTINCT i FROM t1) t1, (SELECT DISTINCT i FROM t2) t2 WHERE t1.i = t2.i; Join → 2×(Unique + Sort + Scan) 61
  • 62. 不満:拡張性の不足 ユーザ定義拡張に対する拡張性 ユーザ定義 関数 選択度の指定は不可 コスト (CPU, IO)の指定は不可 ユーザ定義 テーブル関数 返す行数の指定は不可 (常に1000) 例:dblink (複数サーバへの分散問合せ) SELECT * FROM dblink(server1, ‘SELECT…') AS t1(…), dblink(server2, ‘SELECT…') AS t2(…) WHERE t1.foo = t2.foo; 1000行だと 仮定して最適化 62
  • 63. 不満:OracleにあってPostgreSQLにない 基本的な動作は同等 コストベース最適化 (全探索) 統計情報 (ヒストグラムなど) Oracle限定機能 ヒント機能 強制的に特定のアクセスパスを使わせる パラレル化 一つのクエリを複数のスレッドで並行処理 アドバイザと自動化 SQLを収集し、索引の追加を提案 統計情報収集用のパラメータの自動調整 63
  • 64. EXPLAINコマンド EXPLAIN : 実行計画を見るためのコマンド 例 EXPLAIN SELECT i FROM tbl WHERE i = 10; 出力 Index Scan using idx on tbl ① (cost=0.00..3.07 rows=5 width=4) Index Cond: (i = 10) ② ③ 解釈 ① テーブル tbl を インデックス idx を使って読み取り ② 初期コストは0.00, 総合コストは3.07 ③ 5行が出力されると推定 64
  • 65. 例:SQLの書き方とインデックスの使用可否 1. min, max (8.0以前) SELECT min(x) FROM …; SELECT x FROM … ORDER BY x LIMIT 1; 2. キャスト (7.4以前) WHERE int8column = 4; WHERE int8column = 4::int8; ※8.0以降も int4→OID→int8 のキャストには注意 3. 計算 WHERE lower(文字列) = ‘abc’; CREATE INDEX index ON table ( lower(文字列) ); WHERE floor(整数/10) = 16; WHERE 整数 BETWEEN 10*16 AND 10*(16+1)-1; 65
  • 66. 例:統計情報の精度不足 サンプリング数の設定 ALTER TABLE table ALTER column SET STATISTICS 1-1000 ; EXPLAIN ANALYZE SELECT … FROM order_line, item WHERE ol_o_id = N AND ol_i_id = i_id ORDER BY ol_i_id; STATISTICS = 10 Merge Join (cost=2779.93 rows=1832) (actual time=116.244 rows=3) Merge Cond: ("outer".ol_i_id = "inner".i_id) → Sort (rows=1834) (rows=3) Sort Key: ol_i_id → Index Scan using idx_ol_o_id on order_line (rows=1834) (rows=3) Index Cond: (ol_o_id = N::numeric) → Sort (rows=10010) (rows=7924) Sort Key: i_id → Seq Scan on item i (rows=10010) (rows=10000) STATISTICS = 1000 Sort (cost=53.69 rows=16) (actual time=0.125 rows=3) Sort Key: ol_i_id → Nested Loop (rows=16) (rows=3) → Index Scan using idx_ol_o_id on order_line (rows=16) (rows=3) Index Cond: (ol_o_id = N::numeric) → Index Scan using pk_i on item (rows=1) (rows=1) Index Cond: ("outer".ol_i_id = i_id) 66
  • 67. 例:統計情報の精度不足 Join ol_o_id = i_id 候補の実行計画 NestLoop order_line item order_line NestLoop item ol_o_id = N 40M行 10000行 MergeJoin (or HashJoin) order_line sort MergeJoin item sort 不正確な統計情報でのコスト 1834回のIndexScan > (1834+10010)件のソート 正確な統計情報でのコスト 16回のIndexScan < (16+10010)件のソート 67