PolarDB-X は、パラメータ化された SQL 文の実行計画をキャッシュすることで、バージョンアップデート後も複雑なクエリのパフォーマンスを安定させます。このメカニズムは、プランキャッシュと SQL プラン管理 (SPM) の 2 つのレイヤーで構成されています。
仕組み
PolarDB-X が SQL ステートメントを受信すると、次の順序でステートメントを処理します。
パラメータ化:ステートメント内のすべての定数値をプレースホルダー文字 (
?) に置き換えます。プランキャッシュの確認:パラメータ化された SQL をキーとして使用し、キャッシュされた実行計画を検索します。キャッシュされたプランが存在しない場合は、オプティマイザーを呼び出して生成します。
単純なステートメントのルーティング:単純なステートメントは直接実行します。実行計画管理は、複雑なステートメントにのみ適用されます。
複雑なステートメントに最適なプランの選択:ベースラインに保存されている固定実行計画を使用してステートメントを実行します。ベースラインに複数のプランが存在する場合、プランエニュメレーターはコストが最も低いプランを選択します。
プランキャッシュ
プランキャッシュは、PolarDB-X でデフォルトで有効になっています。パラメータ化の後、PolarDB-X は SQL ステートメント内のすべての定数を ? に置き換え、パラメーターリストを構築します。実行計画では、LogicalView 演算子内の SQL にこれらの ? プレースホルダーが含まれます。
任意のステートメントで EXPLAIN を実行して、プランキャッシュにヒットしたかどうかを確認します。出力の HitCache フィールドが結果を示します。
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';出力には HitCache:true または HitCache:false のインジケーターが含まれます。
実行計画管理
複雑な SQL ステートメントがプランキャッシュを通過した後、SQL プラン管理 (SPM) がそのステートメントに実行計画管理を適用します。
プランキャッシュと実行計画管理はどちらも、パラメータ化された SQL ステートメントをルックアップキーとして使用します。プランキャッシュはすべての SQL ステートメントを対象としますが、実行計画管理は複雑なクエリにのみ適用されます。
各 SQL ステートメントは、正確に 1 つのベースラインにマッピングされます。ベースラインには、1 つ以上の実行計画が保持されます。SPM は現在のパラメーター値に基づいて各プランのコストを評価し、最もコストの低いプランを選択します。プランキャッシュからの実行計画が SPM に入ると、次のようになります。
プランが既知の場合、SPM はそのプランのコストが最も低いかどうかを確認します。
プランが未知の場合、SPM はそれを評価して最適化が必要かどうかを判断します。
自動プラン展開中、SPM はプランエニュメレーターによって発見されたより良いプランを自動的にベースラインに追加します。
BASELINE コマンド
PolarDB-X は、実行計画を管理するために BASELINE コマンドファミリーを提供します。
構文:
BASELINE (LOAD|PERSIST|CLEAR|VALIDATE|LIST|DELETE) [Signed Integer, Signed Integer, ...]
BASELINE (ADD|FIX) SQL (HINT Select Statement)コマンドリファレンス:
| コマンド | 説明 |
|---|---|
BASELINE ADD SQL <hint> <select> | ヒントを使用して生成された実行計画をベースラインに追加します。新しいプランと既存のプランの両方が利用可能なままであり、ランタイム時にプランエニュメレーターが最もコストの低いものを選択します。 |
BASELINE FIX SQL <hint> <select> | 実行計画を必須プランとして固定します。PolarDB-X は、パラメーター値に関係なく、常にこのプランを使用します。 |
BASELINE LIST | すべてのベースラインとその実行計画をリスト表示します。 |
BASELINE LOAD | 指定されたベースラインをシステムテーブルからメモリにロードします。 |
BASELINE LOAD_PLAN | 指定された実行計画をシステムテーブルからメモリにロードします。 |
BASELINE PERSIST | 指定されたベースラインをディスクにフラッシュします。 |
BASELINE PERSIST_PLAN | 指定された実行計画をディスクにフラッシュします。 |
BASELINE CLEAR | 指定されたベースラインをメモリから削除します。 |
BASELINE CLEAR_PLAN | 指定された実行計画をメモリから削除します。 |
BASELINE DELETE | 指定されたベースラインをディスクから削除します。 |
BASELINE DELETE_PLAN | 指定された実行計画をディスクから削除します。 |
`BASELINE LIST` の出力列:
| 列 | 説明 |
|---|---|
BASELINE_ID | ベースラインの一意の識別子。パラメータ化された SQL から派生します。 |
PARAMETERIZED_SQL | すべての定数が ? に置き換えられた後の SQL ステートメント。 |
PLAN_ID | このベースライン内での実行計画の一意の識別子。 |
EXTERNALIZED_PLAN | シリアル化された論理実行計画ツリー。 |
FIXED | BASELINE FIX によって固定され、コストに関係なく常に選択される場合は 1。プランがコストベースの選択に参加する場合は 0。 |
ACCEPTED | 1 は、このプランが Plan Enumerator によって承認され、選択対象であることを示します。 0 は、プランが検証保留中であることを示します。 |
ベースラインには、ACCEPTED=1のプランを複数含めることができます。FIXED=1のプランがない場合、Plan Enumerator は実行時に最もコストの低いプランを選択します。FIXED=1のプランがある場合、PolarDB-X はコストを無視して常にそのプランを使用します。
実行計画の最適化
データ変更や PolarDB-X オプティマイザーのアップグレード後、クエリに対してより良い実行計画が存在する可能性があります。BASELINE ADD と BASELINE FIX を使用して、より良いプランを導入し、固定します。
次の例では、ハッシュ結合クエリをバッチキーアクセス結合 (BKA 結合) に切り替えて、そのプランを固定する方法を示します。
ステップ 1:現在の実行計画の確認
EXPLAIN を実行して現在のプランを確認し、プランキャッシュにヒットしたことを検証します。
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';出力:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| LOGICAL PLAN |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Gather(parallel=true) |
| ParallelHashJoin(condition="l_partkey = p_partkey", type="inner") |
| LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true) |
| LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)", parallel=true) |
| HitCache:true |
| |
| |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
7 rows in set (0.06 sec)BASELINE LIST を実行してベースラインを確認します。これには 1 つのプラン (ParallelHashJoin) が含まれており、FIXED=0 および ACCEPTED=1 となっています。
BASELINE LIST;出力:
+-------------+--------------------------------------------------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| BASELINE_ID | PARAMETERIZED_SQL | PLAN_ID | EXTERNALIZED_PLAN | FIXED | ACCEPTED |
+-------------+--------------------------------------------------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| -399023558 | SELECT *
FROM lineitem
JOIN part ON l_partkey = p_partkey
WHERE p_name LIKE ? | -935671684 |
Gather(parallel=true)
ParallelHashJoin(condition="l_partkey = p_partkey", type="inner")
LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true)
LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)", parallel=true)
| 0 | 1 |
+-------------+--------------------------------------------------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
1 row in set (0.02 sec)ステップ 2:ヒントを使用した代替プランの検証
/*+TDDL:BKA_JOIN(lineitem, part)*/ ヒントを使用して、ベースラインを変更せずに BKA 結合プランを生成します。
EXPLAIN /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';出力:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 論理プラン |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Gather(parallel=true) |
| ParallelBKAJoin(condition="l_partkey = p_partkey", type="inner") |
| LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true) |
| Gather(concurrent=true) |
| LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)") |
| HitCache:true |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
8 行 (0.01 秒)HitCache:false は、ヒントで駆動されるプランがまだベースラインにないことを示します。ベースラインは変更されません。
ステップ 3:BKA 結合プランをベースラインに追加
BASELINE ADD を実行して、既存のハッシュ結合プランと並行して BKA 結合プランを追加します。プランエニュメレーターは、ランタイム時にコストに基づいて両者から選択します。
BASELINE ADD SQL /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';出力:
+-------------+--------+
| BASELINE_ID | STATUS |
+-------------+--------+
| -399023558 | OK |
+-------------+--------+
1 row in set (0.09 sec)BASELINE LIST は、同じベースライン ID に対して、BKA join プラン (PLAN_ID: -1024543942) と元のハッシュ結合プラン (PLAN_ID: -935671684) の 2 つのプランを表示するようになりました。どちらも FIXED=0 および ACCEPTED=1 となっています。
BASELINE LIST;出力:
+-------------+--------------------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| BASELINE_ID | PARAMETERIZED_SQL | PLAN_ID | EXTERNALIZED_PLAN | FIXED | ACCEPTED |
+-------------+--------------------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| -399023558 | SELECT *
FROM lineitem
JOIN part ON l_partkey = p_partkey
WHERE p_name LIKE ? | -1024543942 |
Gather(parallel=true)
ParallelBKAJoin(condition="l_partkey = p_partkey", type="inner")
LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true)
Gather(concurrent=true)
LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)")
| 0 | 1 |
| -399023558 | SELECT *
FROM lineitem
JOIN part ON l_partkey = p_partkey
WHERE p_name LIKE ? | -935671684 |
Gather(parallel=true)
ParallelHashJoin(condition="l_partkey = p_partkey", type="inner")
LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true)
LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)", parallel=true)
| 0 | 1 |
+-------------+--------------------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
2 行がセット内にあります (0.03 秒)p_name LIKE ? の値が変化すると、PolarDB-X は異なる実行計画を動的に選択します。
ステップ 4: BKA 結合プランの修正
パラメーター値に関係なく BKA 結合計画を常に使用するには、BASELINE FIX を実行します。
BASELINE FIX SQL /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';出力:
+-------------+--------+
| BASELINE_ID | STATUS |
+-------------+--------+
| -399023558 | OK |
+-------------+--------+
1 row in set (0.07 sec)固定すると、BASELINE LIST には FIXED=1 の BKA 結合計画と FIXED=0 のハッシュ結合計画が表示されます。
mysql> baseline list\G
*************************** 1. row ***************************
BASELINE_ID: -399023558
PARAMETERIZED_SQL: SELECT *
FROM lineitem
JOIN part ON l_partkey = p_partkey
WHERE p_name LIKE ?
PLAN_ID: -1024543942
EXTERNALIZED_PLAN:
Gather(parallel=true)
ParallelBKAJoin(condition="l_partkey = p_partkey", type="inner")
LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true)
Gather(concurrent=true)
LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)")
FIXED: 1
ACCEPTED: 1
*************************** 2. row ***************************
BASELINE_ID: -399023558
PARAMETERIZED_SQL: SELECT *
FROM lineitem
JOIN part ON l_partkey = p_partkey
WHERE p_name LIKE ?
PLAN_ID: -935671684
EXTERNALIZED_PLAN:
Gather(parallel=true)
ParallelHashJoin(condition="l_partkey = p_partkey", type="inner")
LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true)
LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)", parallel=true)
FIXED: 0
ACCEPTED: 1
2 rows in set (0.01 sec)ステップ 5: 固定プランを確認する
ヒントワードを付けずに EXPLAIN を再度実行してください。
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';出力:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 論理プラン |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Gather(parallel=true) |
| ParallelBKAJoin(condition="l_partkey = p_partkey", type="inner") |
| LogicalView(tables="[00-03].lineitem", shardCount=4, sql="SELECT `l_orderkey`, `l_partkey`, `l_suppkey`, `l_linenumber`, `l_quantity`, `l_extendedprice`, `l_discount`, `l_tax`, `l_returnflag`, `l_linestatus`, `l_shipdate`, `l_commitdate`, `l_receiptdate`, `l_shipinstruct`, `l_shipmode`, `l_comment` FROM `lineitem` AS `lineitem`", parallel=true) |
| Gather(concurrent=true) |
| LogicalView(tables="[00-03].part", shardCount=4, sql="SELECT `p_partkey`, `p_name`, `p_mfgr`, `p_brand`, `p_type`, `p_size`, `p_container`, `p_retailprice`, `p_comment` FROM `part` AS `part` WHERE (`p_name` LIKE ?)") |
| HitCache:true |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
8 行 (0.01 秒)HitCache:true と ParallelBKAJoin によって、PolarDB-Xではヒントワードが不要で、常に固定の BKA 結合プランが使用されることを確認できます。