Este documento fornece dicas para melhorar o desempenho de consultas no MySQL, explicando como analisar o desempenho de consultas existentes, criar índices, otimizar consultas SQL e melhorar operações como inserts, updates e deletes. Recomenda analisar tabelas com ANALYZE TABLE, usar índices apropriados, evitar varreduras completas e melhorar a modelagem da base de dados quando possível.
2. É necessária a otimização?
Grande parte da responsabilidade para que o
banco de dados funcione bem é de quem
modela a base de dados!
Uma Base de dados
bem modelada é uma
base importante para
se criar sistemas
coesos e robustos!
FreeDigitalPhotos.net
3. É necessária a otimização?
Mas em alguns sistemas, temos uma
modelagem muito particular...
Nem sempre temos
uma base de dados
modelada como
sonhamos...
FreeDigitalPhotos.net
4. É necessária a otimização?
Nestes casos, a otimização deve ser
primordial!
"Não posso escolher como me sinto, mas
posso escolher o que fazer a respeito".
William Shakespeare
FreeDigitalPhotos.net
5. Entendendo o banco de dados
relacional
Resumidamente, existem duas maneiras de se
trabalhar com um banco de dados relacional...
FreeDigitalPhotos.net FreeDigitalPhotos.net
Você é livre para escolher qual utilizar!
"Não é livre quem não obteve domínio sobre si".Pitágoras
6. Entendendo o banco de dados
relacional
Banco de dados relacionais são matemáticos.
Eles trabalham, resumidamente, com teoria
de conjuntos...
● Produto cartesiano
● Cardinalidade
FreeDigitalPhotos.net
"A matemática é o alfabeto com o qual Deus escreveu o universo". Pitágoras
7. Ferramentas úteis de análise
Podemos analisar uma função explícita,
utilizando o BENCHMARK.
SELECT BENCHMARK(1000000,2+2);
O MySQL irá executar um milhão de vezes a
expressão, em determinado tempo.
8. Ferramentas úteis de análise
Podemos entender melhor o funcionamento
de nosso script usando o EXPLAIN.
EXPLAIN SELECT * FROM ctbplc;
O MySQL retornará um registro contendo
uma análise do script.
9. Ferramentas úteis de análise
● Colunas de retorno do EXPLAIN SELECT
○ ID: Número sequencial que identifica as consultas
dentro do SELECT.
○ SELECT_TYPE: Tipo de cláuso SQL:
■ SIMPLE (Select simples)
■ PRIMARY (Select mais externa)
■ UNION (segunda select ou select proveniente do UNION)
■ DEPENDENT UNION (segunda select ou select proveniente do
UNION)
■ SUBQUERY (primeiro select encadeado - subquery)
■ DEPENDENT SUBQUERY (primeiro select encadeado da subquery)
■ DERIVED (select de tabela derivada - Subquery da cláusula FROM)
10. Ferramentas úteis de análise
● Colunas de retorno do EXPLAIN SELECT
○ TABLE: Tabela do registro de saída.
○ TYPE: Tipo de JOIN:
■ SYSTEM (tabela que só tem uma linha, tabela de sistema)
■ CONST (tabela que tem no máximo uma linha coincidente. São constantes)
■ EQ_REF (todas as partes da chaves são usadas para combinação de registros)
■ REF (idem ao EQ_REF, mas com índices não únicos)
■ REF_OR_NULL (idem ao REF, mas com busca IS NULL)
■ RANGE (faixa de busca quando o campo é comparado a uma constante)
■ INDEX (quando a consulta só usa colunas que são parte de um índice)
■ ALL (varredura completa na tabela para a busca de registros)
11. Ferramentas úteis de análise
● Colunas de retorno do EXPLAIN SELECT
○ POSSIBLE_KEYS: Sugestão de índices a serem
utilizados.
○ KEY: Chave que está sendo utilizado na consulta.
○ KEY_LEN: Tamanho da chave do campo KEY.
○ REF: Colunas utilizadas pela chave do campo KEY.
○ ROWS: Quantidade de linhas que será analisada
para gerar a consulta.
12. Ferramentas úteis de análise
● Colunas de retorno do EXPLAIN SELECT
○ EXTRA: Sugestão de índices a serem utilizados.
■ Distinct (Termina a busca quando encontra o primeiro registro coincidente)
■ Not exists (idem ao Distinct, mas com LEFT JOIN)
■ Range checked for each record (index map: #)
(O MySQL não encontrou um bom índice para usar)
■ Using filesort (Pesquisa extra na tabela para realizar a ordem de classificação)
■ Using index (Recuperação feita apenas com índices)
■ Using temporary (Utilização de tabelas temporárias para realizar a busca)
■ Using where (Tipo de restrição na busca de registros)
Detalhes: http://dev.mysql.com/doc/refman/5.1/en/explain-output.html
13. Melhorando a performance
● Dicas importantes para SELECT a que já
existem:
○ ANALYZE TABLE: Esta função atualizará as estatísticas sobre a
tabela. Tais estatísticas são utilizadas pelo MYSQL para seleção
de como e qual índice pode ser utilizado.
● Utilize SHOW INDEX FROM para verificar se
a referência de cardinalidade (coluna
Cardinality) está atualizada!
● Os campos das chaves utilizadas devem ser do
mesmo tipo e tamanho para que as buscas sejam
mais rápidas (também influencia no Join).
14. Melhorando a performance
● Dicas importantes para WHERE:
○ ANALYZE TABLE: Esta função atualizará as
estatísticas sobre a tabela. Tais estatísticas são
utilizadas pelo MYSQL para seleção de como e
qual índice pode ser utilizado.
○ Se todas colunas usadas do índice são numéricas,
então somente a árvore de índice é usada para
resolver a consulta.
15. Melhorando a performance
● Dicas importantes para WHERE:
○ Se você não utiliza colunas de todas tabelas
usadas, o MySQL irá parar a varredura das
tabelas não usadas logo que encontrar a primeira
coincidência.
SELECT DISTINCT t1.a
FROM t1,t2
WHERE t1.a=t2.a;
16. Melhorando a performance
● Dicas importantes para WHERE:
○ Você está unindo muitas tabelas e as colunas nas
quais você está fazendo um ORDER BY não são
todas da primeira tabela que não é constante.
○ O ideal é que os campos do ORDER BY sejam da
primeira tabela.
17. Melhorando a performance
● Dica importante para WHERE:
○ Tente usar campos no ORDER BY façam parte de
índices. Isso evita um processo de ordenação por
parte do MySQL.
○ Internamente, o MySQL ordena as consultas
GROUP BY como de fosse o ORDER BY. Para que
só o agrupamento aconteça, inclua no seu script
um ORDER BY NULL;
18. Melhorando a performance
● Dica importante para LIMIT:
○ O MySQL vai buscar a quantidade de registros
estipulados no LIMIT e só depois vai executar
outras funções (ORDER BY ou GROUP BY, por
exemplo).
19. Melhorando a performance
● Dicas importantes para INSERT:
○ Importação de dados: Pode-de utilizar o LOAD
DATA INFILE. A velocidade de inserção de
registros pode melhorar em até 20x.
ZQuery.Close;
ZQuery.SQL.Clear;
ZQuery.SQL.Text:='LOAD DATA INFILE ''c:ctbplc.csv'' INTO TABLE ctbplc FIELDS
TERMINATED BY '','' ENCLOSED BY ''"''LINES TERMINATED BY ''n''ignore 1 lines;';
ZQuery.ExecSQL;
20. Melhorando a performance
● Dicas importantes para INSERT:
○ Na importação de um volume grande de dados,
também é válido desabilitar os índices com ALTER
TABLE <TABELA> DISABLE/ENABLE KEYS.
21. Melhorando a performance
● Dicas importantes para INSERT:
○ Lotes de inserção: Pode-de utilizar o BEGIN /
END / COMMIT para montar um bloco de
comandos insert. A vantagem em velocidade se
torna interessante com blocos de 1000 registro.
ZQuery.Close;
ZQuery.SQL.Clear;
ZQuery.SQL.Text:='start transaction';
ZQuery.ExecSQL;
try
//Comandos dos inserts
...
ZQuery.Close;
ZQuery.SQL.Clear;
ZQuery.SQL.Text:='commit';
ZQuery.ExecSQL;
except
ZQuery.Close;
ZQuery.SQL.Clear;
ZQuery.SQL.Text:='rollback';
ZQuery.ExecSQL;
end;
22. Melhorando a performance
● Dica importante para UPDATE:
○ Deixar para alterar todo o registro de uma só vez.
23. Melhorando a performance
● Dica importante para DELETE:
○ Se for "limpar" uma tabela, use o TRUNCATE
TABLE
24. Melhorando a performance
● Dicas importantes:
○ Dentro do possível, dê preferência para a
utilização de conexões persistentes. Isso evita
sobrecarga de conexões no servidor de banco de
dados.
25. Melhorando a performance
● Dicas importantes:
○ Se suas buscas usam uma determinada ordem de
campos, mas na tabela esses campos estão em uma
ordem diferente, mude a ordem dos campos da
tabela com ALTER TABLE... ORDER BY expr1,
expr2....
26. Melhorando a performance
● Quebrando alguns paradigmas:
○ Índices com muitos campos podem ser substituidos
por um único campo "hash": SELECT * FROM
<nome_tabela> WHERE col_hash=MD5(concat
(col1,col2)) AND col_1='constante' AND
col_2='constante'.
27. Melhorando a performance
● Quebrando alguns paradigmas:
○ Tabelas com muita alteração: evite as colunas
varchar e blob. Dê preferência a registros de
tamanho fixo.
28. Melhorando a performance
● Quebrando alguns paradigmas:
○ Não tenha vergonha de quebrar, em pontos
críticos, a 3ª forma normal.
Mais detalhes: http://pt.wikipedia.org/wiki/Banco_de_dados_relacional
29. Entendendo melhor as coisas...
● Como funcionam os índices?
FreeDigitalPhotos.net
30. Entendendo melhor as coisas...
● Onde os índices são usados:
○ Para encontrar rapidamente os registros que
coincidam com uma cláusula WHERE.
○ Para recuperar registros de outras tabelas ao
realizar joins.
○ Para encontrar o valor MAX() ou MIN() para uma
coluna indexada especifica.
○ Para ordenar ou agrupar uma tabela.
31. Entendendo melhor as coisas...
● Exemplos:
○ Se a tabela possuir um índice de múltiplas colunas, qualquer prefixo
mais à esquerda do índice pode ser usado pelo MySQL para
encontrar registros. Por exemplo, se você possui um índice de três
colunas em (col1, col2, col3), você tem capacidades de busca
indexada em (col1), (col1, col2) e (col1, col2, col3).
○ SELECT col3 FROM <nome_tabela> WHERE col1=1 .
○ SELECT * FROM <nome_tabela> WHERE col1=val1 AND col2=val2 .
32. Entendendo melhor as coisas...
● Exemplos:
○ Se a tabela possuir um índice de múltiplas colunas, qualquer prefixo
mais à esquerda do índice pode ser usado pelo MySQL para
encontrar registros. Por exemplo, se você possui um índice de três
colunas em (col1, col2, col3), você tem capacidades de busca
indexada em (col1), (col1, col2) e (col1, col2, col3).
○ SELECT * FROM <nome_tabela> WHERE col1=val1;
○ SELECT * FROM <nome_tabela> WHERE col2=val2;
○ SELECT * FROM <nome_tabela> WHERE col2=val2 AND col3=val3;
33. Entendendo melhor as coisas...
● Exemplos:
CREATE TABLE teste (
id INT NOT NULL,
ultimo_nome CHAR(30) NOT NULL,
primeiro_nome CHAR(30) NOT NULL,
PRIMARY KEY (id),
INDEX nome (ultimo_nome,primeiro_nome));
○ SELECT * FROM teste WHERE ultimo_nome="Lopes";
○ SELECT * FROM teste WHERE ultimo_nome="Lopes" AND primeiro_nome="
Helder";
○ SELECT * FROM teste WHERE ultimo_nome="Lopes" AND (primeiro_nome="
Helder" OR primeiro_nome="Francisco");
○ SELECT * FROM teste WHERE ultimo_nome="Lopes" AND primeiro_nome >="
H" AND primeiro_nome < "S";
○ SELECT * FROM teste WHERE primeiro_nome="Helder";
○ SELECT * FROM teste WHERE ultimo_nome="Lopes" OR primeiro_nome="
Helder";
34. Entendendo melhor as coisas...
● Exemplos:
○ Um índice é usado para colunas que você compara com os seguintes
operadores: =, >, >=, <, <=, BETWEEN, IS NULL ou um LIKE com um
padrão que começa com um prefixo sem meta caracteres.
○ SELECT * FROM <nome_tabela> WHERE key_col LIKE "Helder%";
○ SELECT * FROM <nome_tabela> WHERE key_col LIKE "Hel%_er%"
○ SELECT * FROM <nome_tabela> WHERE key_col LIKE "%Helder%";
35. Entendendo melhor as coisas...
● Cuidado com os AND:
○ Qualquer índice que não cobre todos os níveis de
AND na cláusula WHERE não é utilizado para
otimizar a consulta.
36. Entendendo melhor as coisas...
● Índices pré-fixados:
○ Para colunas CHAR, VARCHAR, BLOB e TEXT pode-
se indexar um prefixo da coluna.
○ Isto é muito mais rápido e necessita de menos
espaço em disco do que indexar a coluna inteira.
CREATE TABLE teste (
nome CHAR(200) NOT NULL,
INDEX nome_indice (nome(10))
);
37. Bibliografia
● Baron Schwartz, Peter Zaitsev, Vadim Tkachenko, Jeremy D. Zawodny, Arjen Lentz, Derek J.
Balling. High Performance MySQL: Optimization, Backups, Replication, and More. O'Reilly Media,
Inc., 2008; 2ª ed; ISBN 0596554753.
● Wikipédia. Árvore B. http://pt.wikipedia.org/wiki/%C3%81rvore_B Acessado em
18/02/2013.