PolarDB-X menyimpan cache rencana eksekusi untuk pernyataan SQL berparameter guna menjaga stabilitas kinerja kueri kompleks meskipun terjadi pembaruan versi. Mekanisme ini terdiri dari dua lapisan: plan cache dan SQL Plan Management (SPM).
Cara kerja
Saat PolarDB-X menerima sebuah Pernyataan SQL, sistem memprosesnya dalam urutan berikut:
Parameterize: Ganti semua nilai konstan dalam pernyataan dengan karakter placeholder (
?).Periksa plan cache: Gunakan SQL berparameter sebagai kunci untuk mencari rencana eksekusi yang telah di-cache. Jika tidak ditemukan rencana yang di-cache, panggil pengoptimal untuk menghasilkan rencana baru.
Rutekan pernyataan simple: Eksekusi langsung pernyataan simple. Manajemen rencana eksekusi hanya berlaku untuk pernyataan kompleks.
Pilih rencana terbaik untuk pernyataan kompleks: Eksekusi pernyataan menggunakan rencana eksekusi tetap yang disimpan dalam baseline. Jika terdapat beberapa rencana dalam baseline, Plan Enumerator memilih rencana dengan biaya terendah.
Plan cache
Plan cache diaktifkan secara default di PolarDB-X. Setelah parameterisasi, PolarDB-X mengganti semua konstanta dalam Pernyataan SQL dengan ? dan membangun daftar parameter. Dalam rencana eksekusi, SQL di dalam operator LogicalView berisi placeholder ? tersebut.
Jalankan EXPLAIN pada sembarang pernyataan untuk memeriksa apakah pernyataan tersebut mengenai plan cache. Bidang HitCache dalam output menunjukkan hasilnya:
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';Output mencakup indikator HitCache:true atau HitCache:false.
Manajemen rencana eksekusi
Setelah Pernyataan SQL kompleks melewati plan cache, SQL Plan Management (SPM) menerapkan manajemen rencana eksekusi terhadapnya.
Baik plan cache maupun manajemen rencana eksekusi menggunakan Pernyataan SQL berparameter sebagai kunci pencarian. Plan cache mencakup semua Pernyataan SQL; manajemen rencana eksekusi hanya berlaku untuk kueri kompleks.
Setiap Pernyataan SQL dipetakan ke tepat satu baseline. Sebuah baseline menyimpan satu atau beberapa rencana eksekusi. SPM mengevaluasi biaya setiap rencana berdasarkan nilai parameter saat ini dan memilih rencana dengan biaya terendah. Saat sebuah rencana eksekusi dari plan cache masuk ke SPM:
Jika rencana tersebut dikenal, SPM memeriksa apakah rencana tersebut memiliki biaya terendah.
Jika rencana tersebut tidak dikenal, SPM mengevaluasinya untuk menentukan apakah diperlukan optimasi.
Selama evolusi rencana otomatis, SPM secara otomatis menambahkan rencana yang lebih baik—yang ditemukan oleh Plan Enumerator—ke dalam baseline.
Perintah BASELINE
PolarDB-X menyediakan family perintah BASELINE untuk mengelola rencana eksekusi.
Sintaks:
BASELINE (LOAD|PERSIST|CLEAR|VALIDATE|LIST|DELETE) [Signed Integer, Signed Integer, ...]
BASELINE (ADD|FIX) SQL (HINT Select Statement)Referensi perintah:
| Perintah | Deskripsi |
|---|---|
BASELINE ADD SQL <hint> <select> | Menambahkan rencana eksekusi yang dihasilkan dengan hint ke dalam baseline. Rencana baru dan rencana yang sudah ada tetap tersedia; Plan Enumerator memilih rencana dengan biaya terendah pada waktu proses. |
BASELINE FIX SQL <hint> <select> | Menetapkan rencana eksekusi sebagai rencana wajib. PolarDB-X selalu menggunakan rencana ini terlepas dari nilai parameternya. |
BASELINE LIST | Menampilkan semua baseline beserta rencana eksekusinya. |
BASELINE LOAD | Memuat baseline tertentu dari tabel sistem ke dalam memori. |
BASELINE LOAD_PLAN | Memuat rencana eksekusi tertentu dari tabel sistem ke dalam memori. |
BASELINE PERSIST | Menyimpan baseline tertentu ke disk. |
BASELINE PERSIST_PLAN | Menyimpan rencana eksekusi tertentu ke disk. |
BASELINE CLEAR | Menghapus baseline tertentu dari memori. |
BASELINE CLEAR_PLAN | Menghapus rencana eksekusi tertentu dari memori. |
BASELINE DELETE | Menghapus baseline tertentu dari disk. |
BASELINE DELETE_PLAN | Menghapus rencana eksekusi tertentu dari disk. |
Kolom output `BASELINE LIST`:
| Kolom | Deskripsi |
|---|---|
BASELINE_ID | Pengenal unik untuk baseline, yang diturunkan dari SQL berparameter. |
PARAMETERIZED_SQL | Pernyataan SQL setelah semua konstanta diganti dengan ?. |
PLAN_ID | Pengenal unik untuk rencana eksekusi dalam baseline ini. |
EXTERNALIZED_PLAN | Pohon rencana eksekusi logis yang telah diserialisasi. |
FIXED | 1 jika rencana ini ditetapkan oleh BASELINE FIX dan selalu dipilih, terlepas dari biayanya. 0 jika rencana ikut dalam pemilihan berbasis biaya. |
ACCEPTED | 1 jika rencana ini diterima dan memenuhi syarat untuk dipilih oleh Plan Enumerator. 0 jika rencana masih menunggu verifikasi. |
Sebuah baseline dapat berisi beberapa rencana denganACCEPTED=1. Ketika tidak ada rencana yang memilikiFIXED=1, Plan Enumerator memilih rencana dengan biaya terendah pada waktu proses. Ketika suatu rencana memilikiFIXED=1, PolarDB-X selalu menggunakan rencana tersebut dan mengabaikan biaya.
Optimalkan rencana eksekusi
Setelah terjadi perubahan data atau peningkatan pengoptimal PolarDB-X, mungkin tersedia rencana eksekusi yang lebih baik untuk suatu kueri. Gunakan BASELINE ADD dan BASELINE FIX untuk memperkenalkan dan menetapkan rencana yang lebih baik.
Contoh berikut menunjukkan cara mengubah kueri Hash Join menjadi Batched Key Access join (BKA join) dan menetapkan rencana tersebut.
Langkah 1: Periksa rencana eksekusi saat ini
Jalankan EXPLAIN untuk melihat rencana saat ini dan memverifikasi bahwa rencana tersebut mengenai plan cache.
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';Output:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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)Jalankan BASELINE LIST untuk melihat baseline. Baseline tersebut berisi satu rencana (ParallelHashJoin), dengan FIXED=0 dan ACCEPTED=1.
BASELINE LIST;Output:
+-------------+--------------------------------------------------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| 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)Langkah 2: Verifikasi rencana alternatif dengan hint
Gunakan hint /*+TDDL:BKA_JOIN(lineitem, part)*/ untuk menghasilkan rencana BKA join tanpa mengubah baseline.
EXPLAIN /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';Output:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| LOGICAL 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 ?)") |
| HitCache:true |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
8 rows in set (0.01 sec)HitCache:false mengonfirmasi bahwa rencana yang dihasilkan dengan hint belum ada dalam baseline. Baseline tetap tidak berubah.
Langkah 3: Tambahkan rencana BKA join ke dalam baseline
Jalankan BASELINE ADD untuk menambahkan rencana BKA join bersama rencana Hash Join yang sudah ada. Plan Enumerator akan memilih di antara keduanya berdasarkan biaya pada waktu proses.
BASELINE ADD SQL /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';Output:
+-------------+--------+
| BASELINE_ID | STATUS |
+-------------+--------+
| -399023558 | OK |
+-------------+--------+
1 row in set (0.09 sec)Sekarang BASELINE LIST menampilkan dua rencana untuk ID baseline yang sama — rencana BKA join (PLAN_ID: -1024543942) dan rencana Hash Join asli (PLAN_ID: -935671684). Keduanya memiliki FIXED=0 dan ACCEPTED=1.
BASELINE LIST;Output:
+-------------+--------------------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| 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 rows in set (0.03 sec)Saat nilai p_name LIKE ? berubah, PolarDB-X secara dinamis memilih rencana eksekusi yang berbeda.
Langkah 4: Tetapkan rencana BKA join
Untuk selalu menggunakan rencana BKA join terlepas dari nilai parameternya, jalankan 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%';Output:
+-------------+--------+
| BASELINE_ID | STATUS |
+-------------+--------+
| -399023558 | OK |
+-------------+--------+
1 row in set (0.07 sec)Setelah ditetapkan, BASELINE LIST menunjukkan rencana BKA join dengan FIXED=1 dan rencana Hash Join dengan 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)Langkah 5: Verifikasi rencana yang telah ditetapkan
Jalankan kembali EXPLAIN tanpa hint apa pun.
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';Output:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| LOGICAL 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 ?)") |
| HitCache:true |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
8 rows in set (0.01 sec)HitCache:true dan ParallelBKAJoin mengonfirmasi bahwa PolarDB-X kini selalu menggunakan rencana BKA join yang telah ditetapkan, tanpa memerlukan hint.