MySQL Query Optimization

8.789 visualizações

Publicada em

MySQL Query Optimization Presentation

Publicada em: Tecnologia, Negócios
0 comentários
6 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

Sem downloads
Visualizações
Visualizações totais
8.789
No SlideShare
0
A partir de incorporações
0
Número de incorporações
19
Ações
Compartilhamentos
0
Downloads
127
Comentários
0
Gostaram
6
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

MySQL Query Optimization

  1. 1. Otimização MySQL Bianca Caruso da Paixão
  2. 2. Otimizando queries no MySQL <ul><li>Quando realizamos uma consulta, o MySQL analisa para verificar a possibilidade de otimização. O otimizador de consulta do MySQL tira vantagem dos índices, sempre que possível, mas também usa outras informações. </li></ul><ul><li>O plano de execução da query pode ser analisado com a declaração EXPLAIN . Este retorna informações como: </li></ul><ul><ul><li>Os índices que serão usados </li></ul></ul><ul><ul><li>Tipos de joins </li></ul></ul><ul><ul><li>Estimativa do número de linhas que serão examinadas </li></ul></ul><ul><li>Possíveis usos do EXPLAIN: </li></ul><ul><ul><li>Verificar se escrever uma consulta de forma diferente afeta a decisão quanto ao uso de um índice </li></ul></ul><ul><ul><li>Verificar se o acréscimo de índices em uma tabela afetam o plano de execução </li></ul></ul><ul><ul><ul><li>Ex: Usando o índice da chave primária </li></ul></ul></ul><ul><ul><ul><li>mysql> explain select * from t1 where id > 100 and id < 1000G </li></ul></ul></ul><ul><ul><ul><li>*************************** 1. row *************************** </li></ul></ul></ul><ul><ul><ul><li>id: 1 </li></ul></ul></ul><ul><ul><ul><li>select_type: SIMPLE </li></ul></ul></ul><ul><ul><ul><li>table: t1 </li></ul></ul></ul><ul><ul><ul><li>type: range </li></ul></ul></ul><ul><ul><ul><li>possible_keys: PRIMARY </li></ul></ul></ul><ul><ul><ul><li>key: PRIMARY </li></ul></ul></ul><ul><ul><ul><li>key_len: 4 </li></ul></ul></ul><ul><ul><ul><li>ref: NULL </li></ul></ul></ul><ul><ul><ul><li>rows: 561 </li></ul></ul></ul><ul><ul><ul><li>Extra: Using where </li></ul></ul></ul><ul><ul><ul><li>1 row in set (0.00 sec) </li></ul></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  3. 3. Otimizando queries no MySQL <ul><li>Uso do ANALYZE TABLE </li></ul><ul><ul><li>Essa função analisa e armazena a distribuição das chaves na tabela. </li></ul></ul><ul><ul><li>Durante a análise é gerado um lock de leitura para tabelas MyISAM e BDB e para tabelas INNODB um lock de escrita </li></ul></ul><ul><ul><li>MySQL usa a distribuição de chaves armazenada para decidir a ordem das tabelas nos joins. Além disso, pode ser usado para decidir quais índices serão usados em uma tabela para uma determinada query </li></ul></ul><ul><ul><li>Por default, statements do ANALYZE TABLE são escritos no binary log, dessa maneira são replicados para o slave </li></ul></ul><ul><li>Tente comparar colunas que contenham o mesmo tipo de dados </li></ul><ul><ul><li>Quando usar colunas indexadas em comparações, use colunas que são do mesmo tipo. Tipos de dados idênticos proporcionam melhor desempenho que tipos não semelhantes. </li></ul></ul><ul><ul><li>Ex: INT é diferente de BIGINT </li></ul></ul><ul><ul><li>CHAR(10) é considerado igual a CHAR(10) ou VARCHAR(10) </li></ul></ul><ul><ul><li>Ex2: Primeiro comparando INT com INT e depois INT com BIGINT </li></ul></ul><ul><ul><ul><li>mysql> select t1.id, t2.id from t2, t1 where t2.id=t1.id; </li></ul></ul></ul><ul><ul><ul><li>3345194 rows in set ( 25.73 sec ) </li></ul></ul></ul><ul><ul><ul><li>mysql> alter table t2 modify id bigint; </li></ul></ul></ul><ul><ul><ul><li>mysql> select t1.id, t2.id from t2, t1 where t2.id=t1.id; </li></ul></ul></ul><ul><ul><ul><li>3345194 rows in set ( 1 min 6.56 sec ) </li></ul></ul></ul>
  4. 4. Otimizando queries no MySQL <ul><li>Tente colocar as colunas indexadas isoladamente em expressões de comparação </li></ul><ul><ul><li>Se usar uma chamada de função em uma coluna, o MySQL não poderá usar o índice, porque terá que computar o valor da expressão para todas as linhas </li></ul></ul><ul><ul><li>mysql> SELECT count(*) FROM t1 WHERE id < 9874/35; </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>| count(*) | </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>| 189 | </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>1 row in set ( 0.00 sec ) </li></ul></ul><ul><ul><li>mysql> SELECT count(*) FROM t1 WHERE id * 35 < 9874; </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>| count(*) | </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>| 189 | </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>1 row in set ( 1.04 sec ) </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  5. 5. Otimizando queries no MySQL <ul><li>Forneça sugestões ao otimizador quando necessário </li></ul><ul><ul><li>Normalmente, o otimizador determina a ordem na qual irá varrer as tabelas, visando a rápida recuperação das linhas. Entretanto, em alguns casos, ele pode não fazer a melhor escolha. </li></ul></ul><ul><ul><li>É possível sobrepor a escolha do otimizador com algumas palavras chaves </li></ul></ul><ul><ul><ul><li>STRAIGHT_JOIN força as tabelas a serem unidas pela ordem especificada na cláusula FROM </li></ul></ul></ul><ul><ul><ul><ul><li>Devemos ordenar as tabelas de forma que a seleção mais restritiva venha em primeiro lugar. Quanto mais cedo reduzirmos a quantidade de linhas candidatas, melhor é a consulta. </li></ul></ul></ul></ul><ul><ul><ul><ul><li>Ex: SELECT straight_join ... FROM t1, t2, t3 ...; </li></ul></ul></ul></ul><ul><ul><ul><ul><li>SELECT .... FROM t1 straight_join t2 straight_join t3 ...; </li></ul></ul></ul></ul><ul><ul><ul><li>FORCE INDEX , USE INDEX ou IGNORE INDEX após o nome da tabela na lista de tabelas de uma junção </li></ul></ul></ul><ul><ul><ul><ul><li>Serve para orientar o servidor como usar os índices de sua preferência </li></ul></ul></ul></ul><ul><li>Evite o uso excessivo da conversão de tipos automática do MySQL </li></ul><ul><ul><li>Supondo um campo de inteiros (id) </li></ul></ul><ul><ul><li>Ex: SELECT * FROM t1 WHERE id = 6; </li></ul></ul><ul><ul><li>SELECT * FROM t1 WHERE id = ‘6’; </li></ul></ul><ul><ul><li>A segunda consulta envolve uma conversão de tipos, o que envolve uma pequena queda de desempenho. Caso a coluna seja indexada, uma comparação que envolve conversão de tipos pode impedir que o índice seja usado. </li></ul></ul><ul><li>Não utilizar funcionalidades desatualizadas </li></ul><ul><ul><ul><li> </li></ul></ul></ul>
  6. 6. Otimizando queries no MySQL <ul><li>Esvaziar uma tabela com efeitos colaterais mínimos: </li></ul><ul><ul><li>O TRUNCATE é uma operação mais rápida que DELETE porque não há necessidade de apagar cada linha individualmente </li></ul></ul><ul><ul><li>Quando realizado um TRUNCATE, ocorre o drop da tabela e em seguida outra é criada vazia com as mesmas definições. Nesse caso o AUTO_INCREMENT é setado para zero. </li></ul></ul><ul><ul><li>Tabelas Innodb </li></ul></ul><ul><ul><ul><li>Caso exista uma FOREIGN KEY que referencia a tabela, o TRUNCATE deleta linha a linha e a constraint é checada em cada linha. Isso é o mesmo que realizar um DELETE sem cláusula WHERE </li></ul></ul></ul><ul><li>Escolha os tipos de dados adequados </li></ul><ul><ul><li>Não defina o tamanho das colunas acima do necessário </li></ul></ul><ul><ul><li>Se a coluna for indexada, usar valores mais curtos proporcionará um aumento de desempenho </li></ul></ul><ul><ul><li>Tabelas MyISAM </li></ul></ul><ul><ul><ul><li>Linhas com comprimento variável serão mais fragmentadas para tabelas nas quais são realizadas muitas operações de exclusão e modificação </li></ul></ul></ul><ul><ul><ul><li>Tabelas com linhas de comprimento fixo são processadas mais rapidamente e são mais fáceis de reconstruir caso aconteça uma corrupção da tabela, entretanto ocupam mais espaço </li></ul></ul></ul><ul><ul><li>Tabelas Innodb </li></ul></ul><ul><ul><ul><li>O formato interno de armazenamento de linhas não trata de forma diferente colunas de comprimento fixo e comprimento variável. </li></ul></ul></ul><ul><ul><ul><li>O fator desempenho está relacionado ao espaço de armazenamento, dessa maneira, como CHAR ocupa mais espaço </li></ul></ul></ul><ul><ul><li>Ex: De INT(11) para MEDIUMINT(7) </li></ul></ul><ul><ul><li> mysql> select * from t1; </li></ul></ul><ul><ul><li> 4195328 rows in set (7.83 sec) </li></ul></ul><ul><ul><li> mysql> alter table t1 modify id MEDIUMINT(7) UNSIGNED NOT NULL; </li></ul></ul><ul><ul><li> mysql> select * from t1; </li></ul></ul><ul><ul><li> 4195328 rows in set (6.89 sec) </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  7. 7. Otimizando queries no MySQL <ul><li>Definir colunas como NOT NULL </li></ul><ul><ul><li>Acelera o processamento </li></ul></ul><ul><ul><li>Requer menos espaço de armazenamento </li></ul></ul><ul><li>Devemos considerar o uso de colunas ENUM </li></ul><ul><ul><li>Podemos substituir uma coluna string com baixa cardinalidade em colunas ENUM </li></ul></ul><ul><ul><li>Processamento é mais rápido pois interiormente são tratados como valores numéricos </li></ul></ul><ul><li>Usando o OPTIMIZE TABLE </li></ul><ul><ul><li>É aconselhável após remoção de grandes quantidades de dados </li></ul></ul><ul><ul><li>O OPTIMIZE TABLE pode ser usado com MyISAM e tabelas BDB, mas só desfragmenta tabelas MyISAM </li></ul></ul><ul><ul><li>Um método de desfragmentação que funciona para qualquer motor de armazenamento é: </li></ul></ul><ul><ul><ul><li>Esvaziar a tabela com mysqldump </li></ul></ul></ul><ul><ul><ul><li>Descartar a tabela </li></ul></ul></ul><ul><ul><ul><li>Recriá-la usando o arquivo de dump </li></ul></ul></ul><ul><li>Minimizar o tráfego na rede, selecionando somente os dados necessários </li></ul><ul><ul><li>Uma consulta SELECT * pode não ser uma boa solução, a menos que tenhamos certeza de que a cláusula WHERE irá restringir os resultados apenas para as linhas desejadas. </li></ul></ul><ul><ul><li>Se uma query maior é mais eficiente, prefira ela a várias pequenas queries </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  8. 8. Otimizando queries no MySQL <ul><li>Usando o PROCEDURE ANALYSE() </li></ul><ul><ul><li>É usado para obtermos informações sobre as colunas da tabela. </li></ul></ul><ul><ul><li>Uma das saídas é uma sugestão para a otimização dos tipos de dados para cada coluna na tabela </li></ul></ul><ul><ul><li>Ex: mysql> select * from t1 procedure analyse()G </li></ul></ul><ul><ul><li>*************************** 1. row *************************** </li></ul></ul><ul><ul><li>Field_name: bianca.t1.id </li></ul></ul><ul><ul><li>Min_value: 1 </li></ul></ul><ul><ul><li>Max_value: 4587994 </li></ul></ul><ul><ul><li>Min_length: 1 </li></ul></ul><ul><ul><li>Max_length: 7 </li></ul></ul><ul><ul><li>Empties_or_zeros: 0 </li></ul></ul><ul><ul><li>Nulls: 0 </li></ul></ul><ul><ul><li>Avg_value_or_avg_length: 2426342.1562 </li></ul></ul><ul><ul><li>Std: 0.0000 </li></ul></ul><ul><ul><li>Optimal_fieldtype: MEDIUMINT(7) UNSIGNED NOT NULL </li></ul></ul><ul><ul><li>*************************** 2. row *************************** </li></ul></ul><ul><ul><li>Field_name: bianca.t1.nome </li></ul></ul><ul><ul><li>Min_value: aaa </li></ul></ul><ul><ul><li>Max_value: dddd </li></ul></ul><ul><ul><li>Min_length: 3 </li></ul></ul><ul><ul><li>Max_length: 5 </li></ul></ul><ul><ul><li>Empties_or_zeros: 0 </li></ul></ul><ul><ul><li>Nulls: 0 </li></ul></ul><ul><ul><li>Avg_value_or_avg_length: 4.0000 </li></ul></ul><ul><ul><li>Std: NULL </li></ul></ul><ul><ul><li>Optimal_fieldtype: ENUM('aaa','bbbb','ccccc','dddd') NOT NULL </li></ul></ul><ul><ul><li>2 rows in set (6.56 sec) </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  9. 9. Otimizando queries no MySQL <ul><li>Carregando dados eficientemente </li></ul><ul><ul><li>Carregamento massivo é mais eficiente do que carregamento linha a linha pois o cache só será descartado após o termino da carga do grupo e não após a carga de cada registro. </li></ul></ul><ul><ul><ul><li>Ex: LOAD DATA - 4195328 rows affected ( 58.69 sec ) </li></ul></ul></ul><ul><ul><ul><li>INSERT - 4195328 rows affected ( 100.92 sec ) </li></ul></ul></ul><ul><ul><li>A carga é mais rápida quando as tabelas não tiverem índices, pois a cada adição de um registro, o índice deve ser modificado no data file. </li></ul></ul><ul><ul><li>Declarações SQL mais curtas são mais rápidas do que as longas, pois envolvem menos análise gramatical por parte do servidor </li></ul></ul><ul><ul><li>Se tivermos que usar INSERT, devemos tentar usar a forma que permite especificar linhas múltiplas em uma única declaração: </li></ul></ul><ul><ul><ul><li>Ex: INSERT INTO t1 (nome) VALUES (‘aaaaa’), (‘bbbb’), (‘cccc’), .... </li></ul></ul></ul><ul><ul><li>Use INSERT ... ON DUPLICATE KEY update [INSERT IGNORE] ao invés de selecionar antes, verificar se existe e atualizar com UPDATE </li></ul></ul><ul><li>Query Cache </li></ul><ul><ul><li>O query cache armazena o texto do SELECT junto com o resultado correspondente. Se um statement idêntico é recebido posteriormente, o servidor envia o resultado do query cache ao invés de fazer o parse e o processamento do comando. </li></ul></ul><ul><ul><li>É muito útil em ambientes onde as tabelas não mudam muito e há muita query idêntica. </li></ul></ul><ul><ul><li>Aumentar muito o query cache pode gerar um overhead para mante-lo. </li></ul></ul><ul><ul><ul><li>Tamanhos de 10MB geralmente são beneficentes, tamanhos de 100MB já não tem o mesmo efeito. </li></ul></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  10. 10. Otimizando queries no MySQL <ul><li>Não use COUNT(*) em tabelas INNODB para cada consulta, faça isso periodicamente e crie/atualize tabelas de resumo. Mas se precisar do total de registros numa consulta, use SQL_CALC_FOUND_ROWS e SELECT FOUND_ROWS() </li></ul><ul><ul><li>Ex: </li></ul></ul><ul><ul><li>mysql> select count(*) from t1; </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>| count(*) | </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>| 4195328 | </li></ul></ul><ul><ul><li>+----------+ </li></ul></ul><ul><ul><li>1 row in set (3.67 sec) </li></ul></ul><ul><ul><li>mysql> SELECT SQL_CALC_FOUND_ROWS * FROM t1 limit 1; </li></ul></ul><ul><ul><li>mysql> SELECT FOUND_ROWS(); </li></ul></ul><ul><ul><li>+--------------+ </li></ul></ul><ul><ul><li>| FOUND_ROWS() | </li></ul></ul><ul><ul><li>+--------------+ </li></ul></ul><ul><ul><li>| 4195328 | </li></ul></ul><ul><ul><li>+--------------+ </li></ul></ul><ul><ul><li>1 row in set (0.00 sec) </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  11. 11. Otimizando queries no MySQL <ul><li>Para armazenar data e hora atuais, utilize o tipo de dados TIMESTAMP ao invés de DATETIME . TIMESTAMP armazena 4bytes e DATETIME armazena 8bytes </li></ul><ul><li>Uso dos Índices </li></ul><ul><ul><li>São usados para acelerar procuras por linhas que casam com condições de uma cláusula WHERE ou por linhas que casam com linhas de outras tabelas, quando uma junção é executada </li></ul></ul><ul><ul><li>Para consultas que usam as funções MIN() ou MAX(), os valores podem ser encontrados sem examinar todas as linhas </li></ul></ul><ul><ul><li>Para executar operações de ordenação e agrupamento (cláusulas ORDER BY e GROUP BY) </li></ul></ul><ul><ul><li>Para ler toda a informação necessária em uma consulta </li></ul></ul><ul><ul><ul><li>Ex: Supondo que uma consulta seleciona valores de uma coluna numérica indexada de uma tabela MyISAM. Nesse caso, o MySQL não precisa ler os valores duas vezes, assim o arquivo de dados não precisa ser consultado, somente o arquivo de índices. </li></ul></ul></ul><ul><ul><li>Aconselhável usar em colunas com cardinalidade relativamente alta em relação do número de linhas da tabela (ou seja, colunas com poucos valores duplicados) </li></ul></ul><ul><ul><li>Índices possuem um melhor processamento em colunas menores </li></ul></ul><ul><ul><li>Índices menores requer menos acesso a disco </li></ul></ul><ul><ul><li>Indexar prefixos em colunas CHAR, VARCHAR, BINARY, VARBINARY, BLOB ou TEXT irá economizar espaço no índice e tornará as consultas mais rápidas. </li></ul></ul><ul><ul><li>Indexar apenas o necessário, pois todo índice adicional ocupa espaço extra em disco e prejudica o desempenho das operações de escrita. </li></ul></ul><ul><ul><li>Não crie índices duplicados </li></ul></ul><ul><ul><li>INNODB sempre mantém a chave primária como parte de cada índice, então não a crie muito grande </li></ul></ul><ul><ul><li>A medida que os dados crescem, os índices mais adequados podem mudar, assim como a estrutura. </li></ul></ul><ul><ul><li>ORDER BY e LIMIT funcionam melhor em colunas indexadas </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  12. 12. Otimizando queries no MySQL <ul><li>Evitar ‘%’ no início de queries com LIKE </li></ul><ul><ul><li>Ex: </li></ul></ul><ul><ul><li>mysql> select * from t1 where nome like 'aa%'; </li></ul></ul><ul><ul><li>1048832 rows in set (6.76 sec) </li></ul></ul><ul><ul><li>mysql> select * from t1 where nome like '%aa%'; </li></ul></ul><ul><ul><li>1048832 rows in set (7.18 sec) </li></ul></ul><ul><li>JOINs podem ser mais eficientes que tabelas desnormalizadas. Portanto normalize antes e só “desnormalize” onde for necessário. </li></ul><ul><li>Evite subqueries e uso de IN em cláusulas WHERE, o uso de JOIN pode ser mais eficiente </li></ul><ul><li>Separar queries muito complexas em outras mais simples </li></ul><ul><li>Não utilizar flags booleano </li></ul><ul><li>Não use ORDER BY RAND() se houver mais de 2000 registros </li></ul><ul><li>Escolha o character set apropriado (latin1 é mais rápido que UTF8) </li></ul><ul><ul><li>Ex: De Latin1 para UTF8 </li></ul></ul><ul><ul><li>mysql> select * from t1; </li></ul></ul><ul><ul><li>4195328 rows in set (7.83 sec) </li></ul></ul><ul><ul><li>mysql> alter table t1 default character set = utf8; </li></ul></ul><ul><ul><li>mysql> select * from t1; </li></ul></ul><ul><ul><li>4195328 rows in set (11.75 sec) </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>
  13. 13. Otimizando queries no MySQL <ul><li>Referências: </li></ul><ul><ul><li>Top100 SQLPerformanceTips: </li></ul></ul><ul><ul><li>http :// forge .mysql.com/ wiki /Top10SQLPerformanceTips </li></ul></ul><ul><ul><li>Arquivo de Configuração (dicas): </li></ul></ul><ul><ul><li>http ://docs. cellblue . nl /2007/03/17/ easy -mysql-performance- tweaks / </li></ul></ul><ul><ul><li>Revista SQL Magazine </li></ul></ul><ul><ul><ul><li> </li></ul></ul></ul>

×