より良い実行計画を生成するために、PolarDB for PostgreSQL は、ステートメントアウトライン機能 (OUTLINE) を提供します。この機能は、特定のクラスの SQL 文に指定されたヒントを自動的に追加することで、ビジネスアプリケーションに影響を与えることなく SQL の実行計画を正確に制御します。
背景情報
本番データベース環境において、低速 SQL 文は、運用保守 (O&M) チームや開発者にとって一般的な課題です。これらの低速な文は、不均一なデータ分布や不正確な統計情報など、さまざまな要因によって引き起こされる可能性があります。
ヒントを使用してオティマイザーの動作に影響を与えることは、より良い実行計画を生成するための一般的で効果的な方法です。しかし、ビジネス SQL 文に直接ヒントを追加することには、いくつかの課題があります。
ヒント追加の複雑さ:多くのアプリケーションはミドルウェアを使用して SQL を自動生成するため、手動でヒントを追加することは非現実的で面倒です。
リリースプロセスの複雑さ:アプリケーションで手動でヒントを追加するには、ヒントを調整するたびに新しいアプリケーションバージョンをリリースする必要があります。このプロセスは時間と手間がかかり、追加のリスクを伴う可能性があります。
ヒント管理の複雑さ:アプリケーションに追加されるヒントが増えるにつれて、管理が困難になります。データベース管理者がシステムで使用されているすべてのヒントを追跡することが困難になります。
これらの課題に対処するため、PolarDB for PostgreSQL は、特定のクラスの SQL 文にヒントを自動的に追加する機能を導入しました。これがステートメントアウトライン機能、すなわち OUTLINE です。この機能により、ビジネスアプリケーションに影響を与えることなく、SQL の実行計画を正確に制御できます。
機能紹介
OUTLINE 機能を使用すると、特定の SQL 文の実行計画を手動で固定し、このクラスの SQL のヒントを追加または変更できます。OUTLINE には次の利点があります。
アプリケーションからの独立性:アプリケーションコードを変更したり、アプリケーションを再デプロイしたりすることなく、データベースレイヤーで OUTLINE を設定できます。
迅速な応答と調整:実行計画を迅速に調整して、低速 SQL 文の問題を効果的に解決できます。これにより、システムの安定性とパフォーマンスが向上します。
きめ細かな制御と管理:さまざまな SQL 文に対応する説明を提供できます。これにより、システム内のすべての説明と、ヒット数や有効かどうかなどのステータスが明確に表示されます。
ビジネスアプリケーションに影響を与えることなく、OUTLINE 機能を使用して SQL の実行計画を正確に制御し、低速 SQL 文の問題を効率的に解決できます。
用語
HINT:オプティマイザヒント。SQL コメントに埋め込むことで、オプティマイザーに影響を与え、異なる実行計画を生成させます。
OUTLINE:特定のクラスの SQL 文に追加するヒントを指定する定義。
OUTLINE DDL:OUTLINE の変更。作成、削除、ステータスの変更が含まれます。
SQL_ID:SQL 文の識別子。データベースシステムによって自動的に生成され、異なる SQL 文を区別します。OUTLINE は SQL_ID を使用して、ターゲットの SQL 文を識別し、マッチングします。
適用範囲
PolarDB for PostgreSQL のサポート対象バージョンは次のとおりです。
PostgreSQL 16 (マイナーエンジンバージョン 2.0.16.9.9.0 以降)。
PostgreSQL 14 (マイナーエンジンバージョン 2.0.14.13.28.0 以降)。
説明マイナーエンジンバージョン番号は、コンソールで表示するか、
SHOW polardb_version;文を実行して確認できます。ご利用のマイナーエンジンバージョンがサポートされていない場合は、マイナーエンジンバージョンをアップグレードしてください。pg_hint_plan 拡張機能がインストールされており、そのバージョンが 1.4.1 以降である必要があります。
適用範囲
この機能は、 で、マイナーエンジンバージョンが 2.0.14.13.28.0 以降の でサポートされています。
説明マイナーエンジンバージョン番号は、コンソールで表示するか、
SHOW polardb_version;文を実行して確認できます。ご利用のマイナーエンジンバージョンがサポートされていない場合は、マイナーエンジンバージョンをアップグレードしてください。拡張機能がインストールされており、そのバージョンが 1.4.1 以降である必要があります。
注意事項
SQL_ID のマッチング基準:データベースカーネルは、特定のクラスの SQL 文を正規化し、OUTLINE をマッチングする際に次の要素を無視します。
スペース、改行、コメント。
SQL 内のパラメーター (定数や変数を含む)。たとえば、
a = 1、a = $1、a = 2は正規化されます。キーワードの大文字と小文字。たとえば、
SELECT a、select a、Select aは正規化されます。複数のパラメーターを持つリストの場合、パラメーターの数は無視されます。たとえば、
a IN (1,2,3)とa IN (1,2)は正規化されます。ただし、IN (1)には複数のパラメーターが含まれていないため、a IN (1,2,3)とa IN (1)は正規化されません。
1 つの SQL クラスに対して複数の OUTLINE を作成できます。これらは積み重ねて適用され、複数の HINT が SQL 文に影響を与えます。
OUTLINE の優先度:OUTLINE は、SQL 文に埋め込まれたヒントよりも高い優先度を持ちます。SQL 文に対して OUTLINE を作成すると、その文に含まれる元のヒントはすべて無視されます。その文は、OUTLINE 内のヒントによってのみ影響を受けます。
hint_table との相互排他性:OUTLINE 機能と pg_hint_plan の `hint_table` 機能は相互排他的です。`hint_table` 機能は OUTLINE に似ています。OUTLINE 機能を有効にすると、`hint_table` 機能はデフォルトで無効になります。
パフォーマンスへの影響
OUTLINE 機能は、パフォーマンスへの影響を最小限に抑えるために、高同時実行キャッシュモジュールを使用して設計されています。標準的な Sysbench ストレステストによると、機能を有効にして OUTLINE を追加した後の 1 秒あたりのトランザクション数 (TPS) と 1 秒あたりのクエリ数 (QPS) の低下は、約 1%~2% にとどまります。
OUTLINE 機能の有効化
OUTLINE 機能を使用するデータベースで、pg_hint_plan 拡張機能がインストールされており、そのバージョンが 1.4.1 以降であることを確認します。
SELECT extname, extversion >= '1.4.1' AS outline_version_ok FROM pg_extension WHERE extname = 'pg_hint_plan';結果が次のようになっている場合、拡張機能は正常にインストールされており、OUTLINE 機能のバージョン要件を満たしています。
extname | outline_version_ok --------------+-------------------- pg_hint_plan | t (1 row)結果が異なる場合は、次のいずれかの問題が存在する可能性があります。次のように解決してください。
説明適切な権限を持つアカウントを使用して、ターゲットデータベースで次の文を実行してください。
エクステンションが存在しません:
CREATE EXTENSION IF NOT EXISTS pg_hint_plan;拡張機能はインストールされているが、そのバージョンが要件を満たしていない場合:
ALTER EXTENSION pg_hint_plan UPDATE;
OUTLINE 機能を有効にします。
pg_hint_plan.polar_enable_outlineパラメーターを `on` に設定します。このパラメーターを変更しても、クラスターは再起動しません。コンソールでクラスターパラメーターを設定する方法の詳細については、「クラスターパラメーターの設定」をご参照ください。
OUTLINE 機能の使用
OUTLINE の作成
PolarDB は、OUTLINE を作成するための hint_plan.create_outline 関数を提供します。この関数は、安定させたい SQL 文から Hints を自動的に抽出し、OUTLINE に追加します。
例
CALL hint_plan.create_outline($$ SELECT /*+ Set(enable_bitmapscan off) */ * FROM t WHERE a = 1 $$);OUTLINE の表示
PolarDB for PostgreSQL は、OUTLINE を表示するための hint_plan.outlines_status ビューを提供します。
次の文を使用して、データベース内の OUTLINE をクエリできます。
SELECT * FROM hint_plan.outlines_status;返されるフィールドは次のとおりです。
列 | データ型 | 説明 |
id | BIGINT | プライマリキー。システムによって自動的に生成され、アウトラインを区別するための ID です。 |
sql_id | BIGINT | OUTLINE に対応する SQL_ID。 |
hints | TEXT | OUTLINE に対応するヒント。 |
state | CHARACTER(1) | OUTLINE の現在の状態。有効な値:
|
depends_rels | TEXT[] | 現在の OUTLINE を使用して生成されたプランが依存するすべての |
query_string | TEXT | OUTLINE の作成に使用された SQL 文。 |
create_user | TEXT | OUTLINE を作成したユーザー。 |
create_time | TIMESTAMP WITHOUT TIME ZONE | OUTLINE が作成された時刻。 |
total_hints | TEXT | 現在の SQL_ID に追加されるヒント情報。複数の説明 (describe) が同じ SQL_ID を持つ場合、ヒントは ID 順に集約されます。 |
calls | BIGINT | 現在の OUTLINE がヒットして使用された回数。 |
ターゲット OUTLINE の有効化または無効化
OUTLINE を作成した後、次の関数を使用して有効または無効にできます。
hint_plan.enable_outline:OUTLINE を有効にします。対応する OUTLINE の
idを指定します。hint_plan.disable_outline:指定された
idの OUTLINE を無効にします。
例
--- データベース内の OUTLINE ID をクエリします
SELECT * FROM hint_plan.outlines_status;
--- OUTLINE を有効にします
CALL hint_plan.enable_outline(1);
--- OUTLINE を無効にします
CALL hint_plan.disable_outline(1);OUTLINE の削除
hint_plan.del_outline 関数を使用して、対応する ID を指定することで、未使用の OUTLINE を削除できます。
例
--- データベース内のアウトライン ID をクエリします。
SELECT * FROM hint_plan.outlines_status;
--- アウトラインを削除します。
CALL hint_plan.del_outline(1);完全な例
OUTLINE 機能を有効にします。詳細については、「OUTLINE 機能の有効化」をご参照ください。
基本的なテストデータを準備します。
CREATE TABLE t(a int,b int,PRIMARY KEY(a)); CREATE INDEX ON t(b); INSERT INTO t SELECT i,i FROM generate_series(1,100000)i; ANALYZE t;オプティマイザーは、主キーインデックスのコストが列 b のインデックスのコストに匹敵すると見なします。そのため、オプティマイザーが選択する実行計画は変動する可能性があります。
EXPLAIN (costs off) SELECT * FROM t WHERE b = 1 AND a = 1;次の結果が返されます。
QUERY PLAN ------------------------------- Index Scan using t_b_idx on t Index Cond: (b = 1) Filter: (a = 1) (3 rows)ヒントを使用してオプティマイザーに主キーインデックスを選択させます。次に、OUTLINE を作成して、プランが安定したままであることを保証します。
ヒントを使用して、オプティマイザーがプライマリキーインデックスを選択するように影響を与えます。
EXPLAIN (costs off) /*+IndexScan(t t_pkey) */ SELECT * FROM t WHERE b = 1 AND a = 1;次の結果が返されます。
QUERY PLAN ------------------------------ Index Scan using t_pkey on t Index Cond: (a = 1) Filter: (b = 1) (3 rows)この SQL 文に対して OUTLINE を作成します。SQL テキストはアプリケーションテンプレートと一致している必要があることに注意してください。パラメーター、定数、ヒント、スペース、コメントはマッチングに影響しません。ただし、余分な
::型キャスト、テーブルのスキーマ指定、テーブル名や列名の大文字/小文字の変更などの要因により、マッチングが妨げられる可能性があります。マッチング ルールの詳細については、「SQL_ID のマッチング基準」をご参照ください。CALL hint_plan.create_outline($$/*+IndexScan(t t_pkey) */ SELECT * FROM t WHERE b = 1 AND a = 1;$$);説明DMS を使用してクラスターに接続し、上記の文を実行したときに
ERROR: invalid transaction terminationのようなエラーが発生した場合は、psqlなどの別のクライアントを使用してください。詳細については、「データベースクラスターへの接続」をご参照ください。ターゲット SQL 文を再度検証します。期待されるプライマリキーインデックスが使用されます:
EXPLAIN (costs off) SELECT * FROM t WHERE b = 1 AND a = 1;次の結果が返されます。
QUERY PLAN ------------------------------ Index Scan using t_pkey on t Index Cond: (a = 1) Filter: (b = 1) (3 rows)OUTLINE のマッチングは、テンプレートに一致する SQL 文内の追加のパラメーター、スペース、またはコメントの影響を受けません。主キーインデックスが引き続き使用されます。
EXPLAIN (costs off) SELECT * -- comment FROM t WHERE b = 2 AND a = 4;結果は次のとおりです。
QUERY PLAN ------------------------------ Index Scan using t_pkey on t Index Cond: (a = 4) Filter: (b = 2) (3 rows)
hint_plan.outlines_status ビューですべての OUTLINE のステータスを表示できます。
SELECT * FROM hint_plan.outlines_status;次の結果が返されます。
id | sql_id | hints | state | depends_rels | query_string | create_user | create_time | total_hints | calls ----+----------------------+---------------------+-------+--------------+------------------------------------------------------------------+-------------+----------------------------+---------------------+------- 1 | -3220256307655713529 | IndexScan(t t_pkey) | Y | {public.t} | /*+IndexScan(t t_pkey) */ SELECT * FROM t WHERE b = 1 AND a = 1; | postgres | 2024-11-11 11:24:44.063143 | IndexScan(t t_pkey) | 2 (1 row)未使用の OUTLINE を無効化または削除できます。
idが 1 の OUTLINE を無効にします。CALL hint_plan.disable_outline(1);idが 1 の OUTLINE を削除します。CALL hint_plan.del_outline(1);
OUTLINE を無効化または削除すると、プランは元の状態に戻ります。
EXPLAIN (costs off) SELECT * FROM t WHERE b = 1 AND a = 1;次の結果が返されます。
QUERY PLAN ------------------------------- Index Scan using t_b_idx on t Index Cond: (b = 1) Filter: (a = 1) (3 rows)