O PolarDB-X armazena em cache planos de execução de instruções SQL parametrizadas para manter a estabilidade do desempenho de consultas complexas durante atualizações de versão. Esse mecanismo consiste em duas camadas: plan cache e SQL Plan Management (SPM).
Como funciona
Ao receber uma instrução SQL, o PolarDB-X a processa na seguinte ordem:
Parametrização: Substitui todos os valores constantes da instrução pelo caractere de espaço reservado (
?).Verificação do plan cache: Usa o SQL parametrizado como chave para buscar um plano de execução em cache. Se não houver plano armazenado, aciona o otimizador para gerar um novo.
Roteamento de instruções simples: Executa diretamente as instruções simples. O gerenciamento de planos de execução aplica-se apenas a instruções complexas.
Seleção do melhor plano para instruções complexas: Executa a instrução com o plano de execução fixo armazenado na baseline. Se houver múltiplos planos na baseline, o Plan Enumerator selecione aquele com o menor custo.
Plan cache
O plan cache vem ativado por padrão no PolarDB-X. Após a parametrização, o PolarDB-X substitui todas as constantes de uma instrução SQL por ? e cria uma lista de parâmetros. No plano de execução, o SQL dentro do operador LogicalView contém esses espaços reservados ?.
Execute EXPLAIN em qualquer instrução para verifique se ela atinge o plan cache. O campo HitCache na saída indica o resultado:
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';
A saída inclui um indicador HitCache:true ou HitCache:false.
Gerenciamento de planos de execução
Depois que uma instrução SQL complexa passa pelo plan cache, o SQL Plan Management (SPM) aplica o gerenciamento de planos de execução sobre ela.
Tanto o plan cache quanto o gerenciamento de planos de execução usam a instrução SQL parametrizada como chave de busca. O plan cache abrange todas as instruções SQL; já o gerenciamento de planos de execução aplica-se somente a consultas complexas.
Cada instrução SQL mapeia exatamente para uma baseline. Uma baseline contém um ou mais planos de execução. O SPM avalia o custo de cada plano com base nos valores atuais dos parâmetros e selecione o plano de menor custo. Quando um plano de execução do plan cache entra no SPM:
Se o plano for conhecido, o SPM verifique se ele possui o menor custo.
Se o plano for desconhecido, o SPM o avalia para determinar se há necessidade de otimização.
Durante a evolução automática de planos, o SPM adiciona automaticamente à baseline os melhores planos descobertos pelo Plan Enumerator.
Comandos BASELINE
O PolarDB-X fornece a família de comandos BASELINE para gerencie planos de execução.
Sintaxe:
BASELINE (LOAD|PERSIST|CLEAR|VALIDATE|LIST|DELETE) [Signed Integer, Signed Integer, ...]
BASELINE (ADD|FIX) SQL (HINT Select Statement)
Referência de comandos:
|
Comando |
Descrição |
|
|
Adiciona à baseline um plano de execução gerado com uma hint. Tanto o novo plano quanto os existentes permanecem disponíveis; o Plan Enumerator selecione o de menor custo em tempo de execução. |
|
|
Fixa um plano de execução como obrigatório. O PolarDB-X sempre usa esse plano, independentemente dos valores dos parâmetros. |
|
|
Lista todas as baselines e seus respectivos planos de execução. |
|
|
Carrega a baseline especificada da tabela do sistema para a memória. |
|
|
Carrega o plano de execução especificado da tabela do sistema para a memória. |
|
|
Grava a baseline especificada em disco. |
|
|
Grava o plano de execução especificado em disco. |
|
|
Remove a baseline especificada da memória. |
|
|
Remove o plano de execução especificado da memória. |
|
|
Exclua a baseline especificada do disco. |
|
|
Exclua o plano de execução especificado do disco. |
Colunas de saída do BASELINE LIST:
|
Coluna |
Descrição |
|
|
Identificador exclusivo da baseline, derivado do SQL parametrizado. |
|
|
A instrução SQL após a substituição de todas as constantes por |
|
|
Identificador exclusivo do plano de execução dentro desta baseline. |
|
|
A árvore lógica do plano de execução serializada. |
|
|
|
|
|
|
Uma baseline pode conter vários planos comACCEPTED=1. Quando nenhum plano temFIXED=1, o Plan Enumerator selecione o plano de menor custo em tempo de execução. Quando um plano temFIXED=1, o PolarDB-X sempre usa esse plano, ignorando o custo.
Otimizar planos de execução
Após alterações nos dados ou uma atualização do otimizador do PolarDB-X, pode existir um plano de execução melhor para uma consulta. Use BASELINE ADD e BASELINE FIX para introduzir e fixar planos melhores.
O exemplo a seguir mostra como alternar uma consulta Hash Join para um Batched Key Access join (BKA join) e fixar esse plano.
Etapa 1: Verifique o plano de execução atual
Execute EXPLAIN para visualizar o plano atual e confirme se ele atinge o plan cache.
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';
Saída:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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)
Execute BASELINE LIST para visualizar a baseline. Ela contém um plano (ParallelHashJoin), com FIXED=0 e ACCEPTED=1.
BASELINE LIST;
Saída:
+-------------+--------------------------------------------------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| 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)
Etapa 2: Validar o plano alternativo com uma hint
Use a hint /*+TDDL:BKA_JOIN(lineitem, part)*/ para gerar um plano BKA join sem modificar a baseline.
EXPLAIN /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';
Saída:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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:false |
| |
| |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
8 rows in set (0.14 sec)
O valor HitCache:false confirme que o plano orientado por hint ainda não está na baseline. A baseline permanece inalterada.
Etapa 3: Adicionar o plano BKA join à baseline
Execute BASELINE ADD para adicionar o plano BKA join ao lado do plano Hash Join existente. O Plan Enumerator escolhe entre eles em tempo de execução com base no custo.
BASELINE ADD SQL /*+TDDL:bka_join(lineitem, part)*/ SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';
Saída:
+-------------+--------+
| BASELINE_ID | STATUS |
+-------------+--------+
| -399023558 | OK |
+-------------+--------+
1 row in set (0.09 sec)
O comando BASELINE LIST agora exibe dois planos para o mesmo ID de baseline — o plano BKA join (PLAN_ID: -1024543942) e o plano Hash Join original (PLAN_ID: -935671684). Ambos possuem FIXED=0 e ACCEPTED=1.
BASELINE LIST;
Saída:
+-------------+--------------------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+----------+
| 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)
Conforme o valor de p_name LIKE ? muda, o PolarDB-X selecione dinamicamente diferentes planos de execução.
Etapa 4: Fixar o plano BKA join
Para usar sempre o plano BKA join, independentemente dos valores dos parâmetros, execute 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%';
Saída:
+-------------+--------+
| BASELINE_ID | STATUS |
+-------------+--------+
| -399023558 | OK |
+-------------+--------+
1 row in set (0.07 sec)
Após a fixação, o BASELINE LIST mostra o plano BKA join com FIXED=1 e o plano Hash Join com 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)
Etapa 5: Verifique o plano fixado
Execute EXPLAIN novamente sem nenhuma hint.
EXPLAIN SELECT * FROM lineitem JOIN part ON l_partkey=p_partkey WHERE p_name LIKE '%green%';
Saída:
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 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)
Os indicadores HitCache:true e ParallelBKAJoin confirme que o PolarDB-X agora usa sempre o plano BKA join fixado, sem necessidade de hint.