Este artigo é compartilhado pela Huawei Cloud Community " [Coluna de tecnologia MySQL] GaussDB (para MySQL) Big IN Query Optimization ", autor: banco de dados GaussDB.
Introdução ao histórico
Em um ambiente de produção, muitas vezes encontramos instruções SQL de negócios do cliente para filtragem e consulta e, em seguida, realizamos processamento de agregação, e a lista de predicados IN contém milhares ou até dezenas de milhares de valores constantes. Conforme mostrado abaixo, o tempo de execução de tais instruções é muito longo.
Otimização MySQL
Quando o MySQL de código aberto processa a coluna IN (const1, const2, ....), se houver um índice na coluna, o otimizador selecionará a varredura de intervalo para varredura, caso contrário, usará a varredura completa da tabela. A variável de sistema range_optimizer_max_mem_size controla a memória máxima que pode ser usada durante a análise do processo de otimização de intervalo. Se houver muitos elementos da lista no predicado IN, o conteúdo de cada IN será tratado como OR. Cada OR ocupará aproximadamente 230 bytes. Se o uso de memória exceder a memória máxima definida, a otimização do intervalo falhará e o otimizador alterará a estratégia, como a conversão para uma varredura completa da tabela, fazendo com que o desempenho da consulta diminua.
Para este problema de otimização, ele pode ser resolvido ajustando range_optimizer_max_mem_size. A memória definida por range_optimizer_max_mem_size está no nível da sessão. Cada sessão que executa este tipo de instrução ocupará a mesma memória. Em cenários de grande simultaneidade, isso levará ao uso excessivo da memória da instância e ao risco de OOM da instância.
Para consultas de intervalo, o MySQL define a variável de sistema eq_range_index_dive_limit para controlar se o otimizador executa mergulho de índice (índice div) ao processar consultas de intervalo equivalentes. O mergulho no índice usa o índice para completar a descrição do número de tuplas, o que pode obter informações mais precisas e otimizar melhor a estratégia de consulta, mas o tempo de execução também é longo. Quando o número de combinações IN excede um determinado número, o mergulho de índice não é aplicável. O sistema usa valores de informações estatísticas de índice estático para selecionar os índices. Isso pode fazer com que o MySQL não consiga fazer bom uso do índice, resultando em regressão de desempenho.
Grande otimização IN do GaussDB (para MySQL)
coluna IN (const1, const2, ....)
coluna IN (SELECT ... FROM tabela_temporária)
A concatenação pode ser em duas ordens:
-
Varredura de materialização: Indica uma varredura completa da tabela materializada, desde a tabela materializada até a aparência.
-
Pesquisa de materialização: Indica que desde a aparência até a tabela materializada, você pode utilizar o construtor principal para pesquisar dados na tabela materializada.
Varredura física e química
-
Execute a subconsulta, use o índice auto_distinct_key e desduplique os resultados ao mesmo tempo;
-
Salve os resultados da etapa anterior na tabela temporária modelo 1;
-
Obtenha uma linha de dados da tabela temporária e encontre a linha que atende às condições suplementares na aparência;
-
Repita a etapa 3 até que o percurso da tabela temporária seja concluído.
Pesquisa materializada
-
Execute a subconsulta primeiro;
-
Salve os resultados obtidos na etapa anterior em uma tabela temporária;
-
Pegue uma linha de dados da aparência, vá até a tabela temporária materializada para encontrar linhas que atendam às condições suplementares, use a chave primária da tabela materializada e verifique uma linha por vez;
-
Repita 3 até ver todo o visual.
O otimizador escolhe diferentes ordens de concatenação dependendo do tamanho da aparência interna. Em cenários reais, a quantidade de dados nas tabelas geralmente consultadas é muito grande, dezenas de milhões ou mesmo centenas de milhões, o número de elementos na lista IN é muito menor que o número de tabelas, e o otimizador escolherá a Materialização; -scan método para varredura Se a chave primária for usada durante a aparência do índice de consulta, o número total de linhas verificadas após a otimização será N. Quando M for muito maior que N, a melhoria de desempenho será muito óbvia.
Instruções
O parâmetro rds_in_predicate_conversion_threshold é uma opção para modificar a função de consulta na parte inferior do predicado IN. Quando o número de elementos na lista de predicados IN da instrução SQL exceder o valor do parâmetro, a estratégia de otimização será iniciada. A função é usada através do valor desta variável. A seguir está um exemplo simples que ilustra o uso da otimização:
Estrutura da tabela
criar tabela t1(id int, a int, chave idx1(a));
selecione * de t1 onde a in (1,2,3,4,5);
Defina set rds_in_predicate_conversion_threshold = 0 e defina range_optimizer_max_mem_size=1 para desligar a função de otimização de predicado IN grande e a estratégia de otimização de varredura de intervalo. Verifique o plano de execução da instrução de consulta acima.
> definir rds_in_predicate_conversion_threshold = 0; > definir range_optimizer_max_mem_size=1; > explique select * from t1 onde a in (1,2,3,4,5); O resultado é o seguinte: +----+-------------+-------+------------+------+-- -------------+------+---------+------+------+----- -+-------------+ | identificação | tipo_selecionado | mesa | divisórias | tipo | chaves_possíveis | chave | key_len | referência | linhas | filtrado | Extras | +----+-------------+-------+------------+------+-- -------------+------+---------+------+------+----- -+-------------+ | 1 | SIMPLES | t3 | NULO | TODOS | chave1 | NULO | NULO | NULO | 3 | 50,00 | Usando onde | +----+---------+-------+------------+------+-- -------------+------+---------+------+------+----- -++-------------+ 1 linha no conjunto, 2 avisos (0,00 seg) mostrar avisos; +---------+------+-------------------------------- -------------------------------------------------- -----------------------------------------+ | Nível | Código | Mensagem | +---------+------+-------------------------------- -------------------------------------------------- -----------------------------------------+ | Aviso | 3170 | Capacidade de memória de 1 byte para 'range_optimizer_max_mem_size' excedida. A otimização de intervalo não foi feita para esta consulta. | | Nota | 1003 | /* selecione#1 */ selecione `test`.`t3`.`id` AS `id`,`test`.`t3`.`a` AS `a` de `test`.`t3` onde (` teste`.`t3`.`a` em (3,4,5)) | +---------+------+-------------------------------- -------------------------------------------------- -----------------------------------------+ 2 linhas no conjunto (0,00 seg)
Verificou-se que um aviso foi relatado quando a instrução acima foi executada. As informações de aviso mostraram que, como a memória usada durante o processo de otimização de intervalo excedeu range_optimizer_max_mem_size, a otimização do limite de intervalo não foi usada para a instrução. Como resultado, o tipo de varredura muda para ALL e se torna uma varredura completa da tabela.
Defina set rds_in_predicate_conversion_threshold = 3 para ativar a opção de otimização de predicado IN grande, o que significa que quando os elementos da lista de predicados IN excederem 3, a estratégia de otimização de consulta de fila IN grande será ativada. Execute a instrução EXPLAIN FORMAT=TREE para ver se a otimização entra em vigor.
> definir rds_in_predicate_conversion_threshold = 3; > explicar formato=árvore selecione * de t1 onde a in (1,2,3,4,5); +---------------------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------+ | EXPLICAR | +---------------------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------+ | -> Junção interna de loop aninhado (custo = 0,70 linhas = 1) -> Filtro: (t1.a não é nulo) (custo = 0,35 linhas = 1) -> Verificação de tabela em t1 (custo = 0,35 linhas = 1) -> Pesquisa de índice de linha única em <in_predicate_2> usando <auto_distinct_key> (a=t1.a) (custo=0,35 linhas=1) | +---------------------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------+ 1 linha no conjunto (0,00 seg)
A tabela <in_predicate_*> (* é um número) no plano de execução é uma tabela temporária construída no Big INTool, que armazena todos os dados na lista de predicados IN.
Restrições de uso
As instruções de consulta suportadas pela otimização Big IN incluem a seguinte lista de instruções:
-
escolher
-
Inserir...selecionar
-
substituir...selecionar
-
ponto de vista de apoio
-
STMT preparado
Restrições e Limitações
A consulta do rotor Big IN usa a solução de otimização de subconsulta fornecida pelo mysql para obter desempenho. Portanto, existem as seguintes restrições de uso, caso contrário, reduzirá o desempenho.
-
Cenários onde a indexação não pode ser usada não são suportados
-
Suporta apenas constante IN LIST (incluindo NOW(), ? e outras instruções que não envolvem consultas de tabela)
-
Procedimentos/funções/gatilhos armazenados não são suportados
-
Não suportado ou ausente
Comparação de teste de cenário típico
A estrutura do teste da tabela é a seguinte:
CREATE TABLE `sbtest1` ( `id` int NOT NULL AUTO_INCREMENT, `k` int NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '' , CHAVE PRIMÁRIA (`id`), CHAVE `k_1` (`k`) ) ENGINE=InnoDB; O volume de dados da tabela é 1000w. > selecione contagem(*) de sbtest1; +----------+ | contagem(*) | +----------+ | 10000000 | +----------+
A instrução de consulta é a seguinte, na qual o campo de condição é indexado e a lista IN contém 10.000 números constantes.
selecione contagem (*) de sbtest1 onde k em (2708275,5580784,7626186,8747250,228703,4589267,5938459,6982345,2665948,4830545,4929382,8723757,354179,1903 875,5111120,5471341,7098051,3113388,2584956,6550102 ,2842606,2744112,7077924,4580644,5515358,1787655,6391388,6044316,2658197,5628504,413887,6058866,3321587,1430333,445303,73 73496,9133196,6760595,4735642,4756387,9845147,9362192,7271805,4351748,6625915 ,3813276,4236692,8308973,4407131,9481423,3301846,432577,810938,3830320,6120078,6765157,6456566,6649509,1123840,2906490,99 65014.3725748,...);
A comparação de desempenho é mostrada na figura abaixo:
Pode-se observar que após a otimização na lista, o desempenho melhorou 36 vezes em comparação com o método original.
Clique para seguir e conhecer as novas tecnologias da Huawei Cloud o mais rápido possível~
Estudantes do ensino médio criam sua própria linguagem de programação de código aberto como uma cerimônia de maioridade - comentários contundentes de internautas: Contando com a defesa, a Apple lançou o chip M4 RustDesk Os serviços domésticos foram suspensos devido a fraude desenfreada. No futuro, ele planeja produzir um jogo independente na plataforma Windows Taobao (taobao.com) Reiniciar o trabalho de otimização da versão web, destino dos programadores, Visual Studio Code 1.89 lança Java 17, a versão Java LTS mais comumente usada, Windows 10 tem um participação de mercado de 70%, o Windows 11 continua diminuindo Open Source Daily | Google apoia Hongmeng para assumir o controle do Rabbit R1 de código aberto; a ansiedade e as ambições da Microsoft encerraram a plataforma aberta;