クエリ処理コンポーネントを識別する
クエリの実行には 4 つの個別のステージがあります。 これらのステージを実行順序で示すと次のとおりです。
- 解析
- 変換 (リライター)
- 計画
- 実行
パーサー
パーサーは、クエリ文字列の構文が有効かをチェックする役割を担います。 パーサーの主要部分は次の 2 つに分かれています。
- 文法規則と対応するアクションのセットで構成される gram.y。
- 識別子と SQL キー ワードを認識する "レクサー" である scan.1。 すべてのキーワードまたは識別子がトリガーとなってトークンが作成され、パーサーに渡されます。
パーサーによってクエリ ツリーが構築されます。クエリは識別可能な複数の部分に分割され、関係しているテーブルや、適用されたフィルターなどを把握できます。クエリ ツリーには次の部分があります。
- コマンドの種類 - SELECT、INSERT、UPDATE、DELETE のいずれか。
- 範囲テーブル エントリ (RTE) - リレーション、
ie
テーブル、サブクエリ、結合の結果などの一覧。SELECT ステートメントでは、これらの項目は FROM キー ワードの後に出現します。 - 結果の関係 - INSERT、UPDATE、DELETE コマンドの結果の関係は、変更が有効になるテーブルまたはビューです。
- ターゲット リスト - クエリの結果。キーワード SELECT と FROM の間で識別されます。 DELETE コマンドで結果は生成されないため、プランナーは、Executor が削除する行を見つけられるように特別なエントリを追加します。 INSERT コマンドは、結果の関係に含める必要がある新しい行を識別します。 UPDATE コマンドの場合、ターゲット リストには、古い行を置き換える必要がある新しい行が記述されます。
- 修飾 - 最終的な結果行の操作を実行するかどうかを指定するブール値。 これは、SQL ステートメントの WHERE 句に対応します。
- 結合ツリー - このツリーは FROM 項目のリストである可能性があります。 結合は、任意の順序で実行することも、外部結合などの特定の順序で実行することもできます。
- その他 - ORDER BY 句など、この段階では関連しない項目。
リライター
エラーが見つからない限り、パーサーの出力は変換またはリライター プロセスに渡されます。見つかった場合はエラー メッセージが返されます。
クエリ リライターは、ルールを適用することによってクエリ テキストを書き換えます。 リライターはルールに従い、変更したクエリをクエリ プランナーに渡します。 行レベルのセキュリティは、この段階で実装されます。
たとえば、SELECT に関するルールは、INSERT、UPDATE、DELETE クエリの場合を含め、常に最後の手順として適用されます。 また、ルールは、UPDATE クエリが既存の行を上書きするのではなく、新しい行を挿入し、古い行を非表示にすることを意味します。 トランザクションがコミットされた後、バキューム プロセスで非表示の行を削除できます。
Planner
プランナーのジョブは、クエリ ルールを取得し、クエリを実行できるさまざまな方法のうち最も高速な方法を理解することです。
プランナーは、データに対する物理的な操作を表すノードを含むプラン ツリーを作成します。
PostgreSQL では、コストベースのクエリ オプティマイザーを使用して、クエリに最適なプランを見つけます。 プランナーは、さまざまな実行プランを評価し、必要なリソース (CPU サイクル、I/O 操作など) の量を見積もります。この見積もりは、"プラン コスト" と呼ばれる単位に変換されます。 コストが最も低いプランが選択されます。
ただし、結合の数が増えると、可能なプランの数が指数関数的に増加します。 可能なすべてのプランを評価することは、比較的単純なクエリでも不可能になります。 使用可能なプランの数を制限するために、ヒューリスティックとアルゴリズムが使用されます。 その結果、選んだプランが最適なプランではない可能性があります。 ただし、それは最適に近いものであり、妥当な時間内に選択されます。
コストはプランナーの最良の見積もりです。 コスト見積もりの目的は、"同じ" クエリについて、"同じ" 条件でさまざまな実行プランを "比較" することです。 プランナーは、テーブルと行で収集された統計を使用して、クエリのコスト見積もりを生成します。 コスト見積もりを正確にするためには、統計が最新である必要があります。
最新の統計
クエリ オプティマイザーのプランナー コンポーネントは、テーブルと行に関する統計を使用して、正確なコスト見積もりを生成します。
ANALYZE は、データベース テーブルに関する統計を収集し、その結果を pg_statistic システム カタログに格納します。 次の場合は、ANALYZE を実行する必要があります。
- 自動バキューム (通常はこれによってテーブルが自動的に分析される) を無効にしました
- 自動バキュームを無効にしており、最近 ANALYZE を実行していません
- 上記のいずれかであり、かつ INSERTS、UPDATES、または DELETE ステートメントが多数あります。
コストの見積もりは最新の統計を利用していますが、統計が古い場合は非効率なプランが選択される可能性があります。 ANALYZE にパラメーターが渡されない場合、データベース内のすべてのテーブルが調べられます。
ANALYZE の構文は次のとおりです。
ANALYZE [ VERBOSE ] [ ***table*** [ ( ***column*** [, ...] ) ] ]
VERBOSE を使用すると、進行状況メッセージが表示され、分析中のテーブルと一部の統計情報が示されます。
毎日使用量が少ない時間に実行するように VACUUM と ANALYZE をスケジュールします。 ANALYZE は、ターゲット テーブルに対する読み取りロックのみを必要としているため、他のアクティビティと並行して実行できます。
実行者
このフェーズでは、プランナーによって作成されたプランを受け取り、必要な行セットを抽出するために再帰的に処理します。 プラン ノードが呼び出されるたびに、Executor は行を配信するか、完了したことを報告で知らせる必要があります。
Executor は、4 つの SQL クエリの種類をすべて評価します。
- SELECT
- INSERT
- UPDATE
- DELETE
SELECT の場合、Executor からクライアントに各行が結果セットとして返されます。
INSERT の場合、返される各行が指定されたテーブルに挿入されます。 このタスクは、ModifyTable と呼ばれる特別な最上位レベルのプラン ノードで行われます。
UPDATE の場合、各計算行には、更新されたすべての列値とターゲット行の行 ID が含まれます。 データが ModifyTable ノードに送信されると、更新された行が作成され、古い行が削除済みとしてマークされます。
DELETE の場合、プランによって返される列は行 ID のみです。 ModifyTable ノードは、行 ID を使用して行を削除済みとしてマークします。