Quando os planos de execução mudam inesperadamente — devido ao crescimento de dados, desvio de estatísticas ou atualizações de versão — geralmente é necessário adicionar hints do otimizador diretamente no SQL. Se não for possível modificar o código da aplicação, o Statement Outline permite injetar esses hints sem alterar o SQL. Ele fixa o plano de execução para uma instrução específica ao corresponder a um digest SQL normalizado e reescrever a consulta com o hint configurado antes que o otimizador a processe.
O PolarDB fornece o pacote DBMS_OUTLN com stored procedures para adicionar, visualizar, inspecionar e excluir outlines.
Após adicionar um outline, verifique se ele funciona imediatamente. ExecuteCALL dbms_outln.preview_outline('<schema>', '<query>')— um resultado não vazio confirma que o outline correspondeu. Alternativamente, executeEXPLAINna consulta alvo e verifique se a colunaExtraexibeUsing outline <ID>.
Pré-requisitos
Antes de começar, certifique-se de ter:
-
Um cluster PolarDB for MySQL executando uma das seguintes versões:
PolarDB for MySQL 5.6 com versão secundária 5.6.1.0.36 ou posterior
PolarDB for MySQL 5.7 com versão secundária 5.7.1.0.2 ou posterior
PolarDB for MySQL 8.0.1 com versão secundária 8.0.1.1.1 ou posterior
PolarDB for MySQL 8.0.2
Para verificar a versão do seu cluster, consulte Visualizar o número da versão.
O PolarDB for MySQL 5.6 não suporta optimizer hints. Use add_index_outline (um index hint) para clusters da versão 5.6.
Como funciona
O Statement Outline intercepta uma instrução SQL correspondente e a reescreve com o hint configurado antes que o otimizador a processe. A correspondência baseia-se em um digest normalizado do texto SQL, opcionalmente restrito a um banco de dados específico:
Se
Schema_nameestiver definido, tanto o nome do banco de dados quanto o digest SQL devem corresponder à regra do outline.Se
Schema_nameestiver em branco, apenas o digest SQL precisa corresponder.
Todos os hints são persistidos na tabela de sistema mysql.outline e carregados na memória durante a inicialização.
Escolha um tipo de outline
|
Outline de optimizer hint |
Outline de index hint |
|
|
Mecanismo |
Injeta hints |
Anexa |
|
Controles |
Ordem de junção, algoritmo de junção, comportamento do bloco de consulta, variáveis de sessão |
Índice usado pelo otimizador para uma tabela específica |
|
Suporte ao MySQL 5.6 |
Não |
Sim |
|
Procedimento |
|
|
Início rápido
Os exemplos a seguir mostram as operações de outline mais comuns. Todos usam a sintaxe CALL dbms_outln.<procedure>().
Fixar uma consulta com um optimizer hint
Force um índice específico usando um optimizer hint:
CALL dbms_outln.add_optimizer_outline(
'test', -- Schema_name
'/*+ INDEX(t1 i_a) */', -- Hint string
'SELECT test.t1.a AS a FROM test.t1' -- Original SQL
);
Verifique se o outline correspondeu:
CALL dbms_outln.preview_outline('test', 'SELECT test.t1.a AS a FROM test.t1');
Fixar uma consulta com um index hint
Use add_index_outline para aplicar um hint USE INDEX. Esta abordagem funciona em clusters MySQL 5.6:
CALL dbms_outln.add_index_outline(
'outline_db', -- Schema_name
'', -- Digest (leave blank; the system calculates it from Query)
1, -- Position: the 1st table in the SQL
'USE INDEX', -- Type
'ind_1', -- Index name(s)
'', -- Scope (blank = all query types)
"SELECT * FROM t1 WHERE t1.col1 = 1 AND t1.col2 = 'xpchild'" -- Original SQL
);
Fixar a ordem de junção
Force it1 e it2 a se unirem primeiro, deixando o otimizador lidar com as tabelas restantes:
CALL dbms_outln.add_optimizer_outline(
'outline_db',
'/*+ JOIN_PREFIX(it1, it2) */',
'SELECT it3.id3, it2.i2, it1.id2
FROM t3 it3, t1 it1, t2 it2
WHERE it3.i3 = it1.id1
AND it2.id2 = it1.id2
GROUP BY it3.id3, it1.id2'
);
Definir uma variável de sessão para uma única instrução
Aplique hints SET_VAR para substituir uma variável de sessão durante a execução de uma única instrução:
CALL dbms_outln.add_optimizer_outline(
'test',
'/*+ SET_VAR(max_execution_time=1) */',
'SELECT * FROM t1'
);
Roteamento para nós somente leitura row-store ou columnstore
Em clusters com nós somente leitura In-Memory Columnar Index (IMCI), force o roteamento de instruções com SET_VAR:
-- Force execution on columnstore (IMCI) read-only nodes
CALL dbms_outln.add_optimizer_outline('test', '/*+ SET_VAR(cost_threshold_for_imci=0) */', 'SELECT test.t1.a AS a FROM test.t1');
-- Force execution on row-store read-only nodes
CALL dbms_outln.add_optimizer_outline('test', '/*+ SET_VAR(use_imci_engine=OFF) */', 'SELECT test.t1.a AS a FROM test.t1');
Gerencie outlines
Adicionar um outline de optimizer hint
CALL dbms_outln.add_optimizer_outline('<Schema_name>', '<Hint>', '<Query>');
|
Parâmetro |
Descrição |
|
|
Nome do banco de dados. Deixe em branco para corresponder apenas pelo digest. |
|
|
String completa do optimizer hint, como |
|
|
Instrução SQL original a ser fixada. |
QuandoQuerycontiver strings entre aspas duplas, envolva toda aQueryem aspas duplas e use aspas simples internamente, ou vice-versa. Um outline criado com aspas simples emQuerycorresponde ao SQL independentemente do uso de aspas simples ou duplas na execução.
Exemplo: Fixar um limite de tempo de execução em uma consulta:
SQL original:
SELECT * FROM t1 WHERE name = "Tom";
Normalize as aspas para o outline:
SELECT * FROM t1 WHERE name = 'Tom';
Adicione o outline:
CALL dbms_outln.add_optimizer_outline("", "/*+ max_execution_time(1000) */", "SELECT * FROM t1 WHERE name='Tom'");
Adicionar um outline de index hint
CALL dbms_outln.add_index_outline('<Schema_name>', '<Digest>', <Position>, '<Type>', '<Hint>', '<Scope>', '<Query>');
|
Parâmetro |
Descrição |
|
|
Nome do banco de dados. |
|
|
String de hash de 64 bytes obtida pelo hash de |
|
|
Posição baseada em 1 da tabela à qual o hint se aplica. Para a N-ésima tabela no texto SQL, defina |
|
|
Tipo de index hint: |
|
|
Lista separada por vírgulas de nomes de índices, como |
|
|
Restringe o hint a um tipo de consulta: |
|
|
Instrução SQL original a ser fixada. |
Exemplo:
CALL dbms_outln.add_index_outline(
'outline_db', '', 1, 'USE INDEX', 'ind_1', '',
"SELECT * FROM t1 WHERE t1.col1 = 1 AND t1.col2 = 'xpchild'"
);
Visualize um outline
preview_outline mostra quais outlines correspondem a uma determinada consulta sem executá-la. Use este recurso para validar um outline antes de usá-lo em produção.
CALL dbms_outln.preview_outline('<Schema_name>', '<Query>');
Exemplo:
CALL dbms_outln.preview_outline('outline_db', "SELECT * FROM t1 WHERE t1.col1 = 1 AND t1.col2 = 'xpchild'");
Saída:
+------------+------------------------------------------------------------------+------------+------------+-------+---------------------+
| SCHEMA | DIGEST | BLOCK_TYPE | BLOCK_NAME | BLOCK | HINT |
+------------+------------------------------------------------------------------+------------+------------+-------+---------------------+
| outline_db | b4369611be7ab2d27c85897632576a04bc08f50b928a1d735b62d0a140628c4c | TABLE | t1 | 1 | USE INDEX (`ind_1`) |
+------------+------------------------------------------------------------------+------------+------------+-------+---------------------+
1 row in set (0.01 sec)
Visualize outlines ativos
show_outline lista todos os outlines atualmente carregados na memória, juntamente com as contagens de acertos e estouro.
CALL dbms_outln.show_outline();
Saída:
+------+------------+------------------------------------------------------------------+-----------+-------+------+-------------------------------------------------------+------+----------+-------------------------------------------------------------------------------------+
| ID | SCHEMA | DIGEST | TYPE | SCOPE | POS | HINT | HIT | OVERFLOW | DIGEST_TEXT |
+------+------------+------------------------------------------------------------------+-----------+-------+------+-------------------------------------------------------+------+----------+-------------------------------------------------------------------------------------+
| 33 | outline_db | 36bebc61fce7e32b93926aec3fdd790dad5d895107e2d8d3848d1c60b74bcde6 | OPTIMIZER | | 1 | /*+ SET_VAR(foreign_key_checks=OFF) */ | 1 | 0 | SELECT * FROM `t1` WHERE `id` = ? |
| 32 | outline_db | 36bebc61fce7e32b93926aec3fdd790dad5d895107e2d8d3848d1c60b74bcde6 | OPTIMIZER | | 1 | /*+ MAX_EXECUTION_TIME(1000) */ | 2 | 0 | SELECT * FROM `t1` WHERE `id` = ? |
| 34 | outline_db | d4dcef634a4a664518e5fb8a21c6ce9b79fccb44b773e86431eb67840975b649 | OPTIMIZER | | 1 | /*+ BNL(t1,t2) */ | 1 | 0 | SELECT `t1` . `id` , `t2` . `id` FROM `t1` , `t2` |
| 35 | outline_db | 5a726a609b6fbfb76bb8f9d2a24af913a2b9d07f015f2ee1f6f2d12dfad72e6f | OPTIMIZER | | 2 | /*+ QB_NAME(subq1) */ | 2 | 0 | SELECT * FROM `t1` WHERE `t1` . `col1` IN ( SELECT `col1` FROM `t2` ) |
| 36 | outline_db | 5a726a609b6fbfb76bb8f9d2a24af913a2b9d07f015f2ee1f6f2d12dfad72e6f | OPTIMIZER | | 1 | /*+ SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) */ | 2 | 0 | SELECT * FROM `t1` WHERE `t1` . `col1` IN ( SELECT `col1` FROM `t2` ) |
| 30 | outline_db | b4369611be7ab2d27c85897632576a04bc08f50b928a1d735b62d0a140628c4c | USE INDEX | | 1 | ind_1 | 3 | 0 | SELECT * FROM `t1` WHERE `t1` . `col1` = ? AND `t1` . `col2` = ? |
| 31 | outline_db | 33c71541754093f78a1f2108795cfb45f8b15ec5d6bff76884f4461fb7f33419 | USE INDEX | | 2 | ind_2 | 1 | 0 | SELECT * FROM `t1` , `t2` WHERE `t1` . `col1` = `t2` . `col1` AND `t2` . `col2` = ? |
+------+------------+------------------------------------------------------------------+-----------+-------+------+-------------------------------------------------------+------+----------+-------------------------------------------------------------------------------------+
HIT: quantidade de vezes que este outline foi correspondido e aplicado.OVERFLOW: número de vezes em que o outline não encontrou o bloco de consulta ou a tabela alvo na instrução.
Exclua um outline
CALL dbms_outln.del_outline(<Id>);
Informe o ID obtido em show_outline. O outline é removido imediatamente tanto da memória quanto da tabela de sistema mysql.outline.
Exemplo:
CALL dbms_outln.del_outline(32);
Se o ID não existir, o sistema registra um aviso em vez de um erro:
CALL dbms_outln.del_outline(1000);
-- Query OK, 0 rows affected, 2 warnings (0.00 sec)
SHOW WARNINGS;
+---------+------+----------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------+
| Warning | 7521 | Statement outline 1000 is not found in table |
| Warning | 7521 | Statement outline 1000 is not found in cache |
+---------+------+----------------------------------------------+
Verifique se um outline foi aplicado
Após adicionar um outline, confirme seu funcionamento com um destes métodos.
Método 1: preview_outline (recomendado)
CALL dbms_outln.preview_outline('outline_db', "SELECT * FROM t1 WHERE t1.col1 = 1 AND t1.col2 = 'xpchild'");
Um conjunto de resultados não vazio indica que o outline correspondeu.
Método 2: EXPLAIN
Execute EXPLAIN na consulta alvo. Se o outline foi aplicado, a coluna Extra inclui Using outline <ID>.
EXPLAIN SELECT * FROM t1 WHERE t1.col1 = 1 AND t1.col2 = 'xpchild';
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+------------------------------+
| 1 | SIMPLE | t1 | NULL | ref | ind_1 | ind_1 | 5 | const | 1 | 100.00 | Using where; Using outline 1 |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+------------------------------+
O textoUsing outline <ID>emExtraaparece apenas nas versões:
PolarDB for MySQL 8.0.1 com versão secundária 8.0.1.0.34 ou posterior
PolarDB for MySQL 8.0.2 com versão secundária 8.0.2.2.27 ou posterior
Para ver o SQL reescrito, execute SHOW WARNINGS após o EXPLAIN:
SHOW WARNINGS;
+-------+------+----------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------+
| Note | 1003 | /* select#1 */ SELECT `outline_db`.`t1`.`id` AS `id`,... USE INDEX (`ind_1`) WHERE ... |
+-------+------+----------------------------------------------------------+
Sharding Outline
Em cenários de sharding de tabelas, as tabelas físicas seguem um padrão de sufixo numérico — t_001, t_002, ..., t_999. Um Statement Outline padrão corresponde apenas ao nome exato da tabela, exigindo uma regra separada por tabela. Em uma configuração grande de sharding, isso se torna inviável.
O Sharding Outline resolve esse problema tratando os números finais como um padrão curinga. Ele reconhece t_1, t_2 e t_100 como o único padrão t_?, permitindo que uma única regra cubra todas as tabelas correspondentes.
|
Outline Padrão |
Sharding Outline |
|
|
Correspondência de nome de tabela |
Correspondência exata, ex.: |
Padrão curinga, ex.: |
|
Escopo |
Uma tabela específica |
Todas as tabelas fragmentadas com um padrão de nomenclatura consistente |
|
Regras necessárias |
Uma por tabela fragmentada |
Uma regra para todas as tabelas correspondentes |
|
Manutenção |
Alta: criação, atualização e exclusão em lote |
Baixa: uma única regra gerencia todas as tabelas correspondidas |
Versões suportadas:
PolarDB for MySQL 8.0.1 com versão secundária 8.0.1.1.54 ou posterior
PolarDB for MySQL 8.0.2 com versão secundária 8.0.2.2.33 ou posterior
Ative o Sharding Outline
No console do PolarDB, acesse Parameter Settings e defina
loose_outline_templated_digest_for_sharding_tablecomoON. Para detalhes, consulte Definir parâmetros.Chame
add_optimizer_outline_shardingusando qualquer nome de tabela fragmentada comoQuery. O procedimento transforma automaticamente os números finais no padrãot_?.
CALL dbms_outln.add_optimizer_outline_sharding(
'test', -- Schema_name
'', -- Digest (leave blank)
1, -- Position
'/*+ MAX_EXECUTION_TIME(1000) */', -- Hint string
"SELECT t_1.c_1 FROM t_1" -- Any sharded table name works: t_1, t_2, t_100
);
Após a execução, a regra se aplica a todas as tabelas que correspondem a t_? — incluindo t_1, t_2 e t_100.
Parâmetros
Configure o comportamento do Statement Outline no console do PolarDB em Parameter Settings. Consulte Definir parâmetros.
|
Parâmetro |
Escopo |
Descrição |
|
|
Global |
Ativa ou desativa o Statement Outline. |
|
|
Sessão |
Ativa ou desativa o Sharding Outline. |
Apêndice: Tabela de sistema do Statement Outline
O PolarDB armazena todas as regras de outline em mysql.outline. A tabela é criada automaticamente na inicialização.
CREATE TABLE `mysql`.`outline` (
`Id` bigint(20) NOT NULL AUTO_INCREMENT,
`Schema_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`Digest` varchar(64) COLLATE utf8_bin NOT NULL,
`Digest_text` longtext COLLATE utf8_bin,
`Type` enum('IGNORE INDEX','USE INDEX','FORCE INDEX','OPTIMIZER')
CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`Scope` enum('','FOR JOIN','FOR ORDER BY','FOR GROUP BY')
CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '',
`State` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'Y',
`Position` bigint(20) NOT NULL,
`Hint` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Statement outline'
|
Campo |
Descrição |
|
|
ID exclusivo do outline. |
|
|
Nome do banco de dados. Vazio significa correspondência apenas por digest. |
|
|
String de hash de 64 bytes obtida pelo hash de |
|
|
Forma normalizada da instrução SQL usada para cálculo do digest. |
|
|
|
|
|
Escopo do index hint: |
|
|
Indica se a regra está ativa. |
|
|
Para optimizer hints: posição baseada em 1 do bloco de consulta alvo do hint. Para index hints: posição baseada em 1 da tabela no texto SQL. |
|
|
Para optimizer hints: string completa do hint, ex.: |