Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

MySQL Query Optimization

9,044 views

Published on

MySQL Query Optimization Presentation

Published in: Technology, Business

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>

×