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

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

問合せ最適化インサイド

5,388
views

Published on

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

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

Published in: Technology

0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
5,388
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
84
Comments
0
Likes
12
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • 具体的なチューニングパラメータの内容がこの表である。 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 の効果も兼ね備えているような?
  • 統計情報が利用できない場合は、選択度を見積もる
  • 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