Você está na página 1de 64

Otimizao de Banco de Dados

Otimizao de Banco de dados

SUMRIO Captulo 1 Viso Geral da Otimizao .......................................................03 1.1 - Limitaes do Projeto MySQL/Trocas ...........................................03 1.2 - Portabilidade .................................................................................04 1.3 - Para que Utilizamos o MySQL? ....................................................05 1.4 - O Pacote de Benchmark do MySQL .............................................07 1.5 - Utilizando seus Prprios Benchmarks ..........................................08 Captulo 2 Otimizando SELECTs e Outras Consultas ..............................10 2.1 - Sintaxe de EXPLAIN (Obter informaes sobre uma SELECT) ...10 2.2 - Estimando o Desempenho de uma Consulta ................................19 2.3 - Velocidade das Consultas que Utilizam SELECT .........................20 2.4 - Como o MySQL Otimiza Clusulas WHERE ................................20 2.5 - Como o MySQL Otimiza IS NULL .................................................22 2.6 - Como o MySQL Otimiza Clusulas DISTINCT .............................23 2.7 - Como o MySQL Otimiza LEFT JOIN e RIGHT JOIN ....................24 2.8 - Como o MySQL Otimiza Clusulas ORDER BY ...........................25 2.9 - Como o MySQL Otimiza Clusulas LIMIT ....................................27 2.10 - Performance das Consultas que Utilizam INSERT .....................27 2.11 - Performance das Consultas que Utilizam UPDATE ...................30 2.12 - Performance das Consultas que Utilizam DELETE ....................30 2.13 - Mais Dicas sobre Otimizaes ....................................................30 Captulo 3 Detalhes Sobre Locks ...............................................................34 3.1 - Como o MySQL Trava as Tabelas ................................................34 3.2 - Detalhes sobre Lock de Tabelas ...................................................35 Captulo 4 Otimizando a Estrutura de Banco de Dados ...........................37 4.1 - Opes do Projeto ........................................................................37 4.2 - Deixando os Dados com o Menor Tamanho Possvel ..................37 4.3 - Como o MySQL Utiliza ndices .....................................................39 4.4 - ndices de Colunas ........................................................................41 4.5 - ndices de Mltiplas Colunas ........................................................42 4.6 - Como o MySQL Conta as Tabelas Abertas ..................................43 4.7 - Como o MySQL Abre e Fecha as Tabelas ...................................43 4.8 - Desvantagem em Criar um Nmero Grande de Tabelas no Mesmo Banco de Dados ...............................................................................................45 Captulo 5 Otimizando o Servidor MySql ...................................................46 5.1 - Sintonia dos Parmetros em Tempo de Sistema/Compilao e na Inicializao ......................................................................................................46 5.2 - Parmetros de Sintonia do Servidor .............................................47 5.3 - Como a Compilao e a Ligao Afetam a Velocidade do MySql 49 5.4 - Como o MySQL Utiliza a Memria ................................................51 5.5 - Como o MySQL Utiliza o DNS ......................................................53 5.6 - Sintaxe de SET .............................................................................53 Captulo 6 Detalhes de Disco ......................................................................59 6.1 - Utilizando Links Simblicos ...........................................................61 6.1.1 - Utilizando Links Simblicos para Bancos de Dados ........61 6.1.2 - Utilizando Links Simblicos para Tabelas ........................62 6.1.3 - Usando Links Simblicos para Bancos de Dados no Windows ...........................................................................................................63 .

Otimizao de Banco de dados

1. Viso Geral da Otimizao


1.1. Limitaes do Projeto MySQL/Trocas 1.2. Portabilidade 1.3. Para que Utilizamos o MySQL? 1.4. O Pacote de Benchmark do MySQL 1.5. Utilizando seus Prprios Benchmarks A parte mais importante para obter um sistema rpido com certeza o projeto bsico. Voc tambm precisa saber quais tipos de coisas seus sistema estar fazendo, e quais so gargalos existentes. Os gargalos mais comuns so:

Pesquisa em disco necessrio tempo para o disco encontrar uma quantidade de dados. Com discos modernos em 1999, o tempo mdio para isto era normalmente menor que 10ms, portanto em teoria poderamos fazer 100 buscas por segundo. Este tempo melhora moderadamente com discos novos e isso muito difcil otimizar para uma nica tabela. A maneira de otimizar isto colocando os dados em mais de um disco. Leitura de disco/Escrita (I/O) Quando o disco estiver na posio correta precisaremos que os dados sejam lidos. Com discos mais modernos em 1999, um disco retorna algo em torno de 10-20Mb/s. Isto mais fcil de otimizar que as buscas porque voc pode ler vrios discos em paralelo. Ciclos de CPU. Quando tivermos os dados na memria principal (ou se eles j estiverem l) precisaremos process-los para conseguir nosso resultado. O fator de limitao mais comum ter ppequenas tabelas, comparadas com a memria. Mas, com pequenas tabelas, normalmente no teremos problemas com velocidade. Largura de banda da memria. Quando a CPU precisa de mais dados que podem caber no cache da CPU a largura da banda da memria principal se torna um gargalo. Isto um gargalo muito incomum para a maioria dos sistema, mas bom estarmos ciente dele.

1.1. Limitaes do Projeto MySQL/Trocas


Quando usamos o mecanismos de armazenamento MyISAM, o MySQL utiliza travamento de tabela extremamente rpidos (mltiplas leituras / nica escrita). O maior problema com este tipo de tabela ocorre quando voc tem uma mistura do fluxo fixo de atualizaes e selees lentas na mesma tabela. Se isto for um problema com algumas tabelas, voc pode usa outro tipo de tabela. O MySQL pode trabalhar com tabelas transacionais e no transacionais. Para trabalhar sem problemas com tabelas no transacionais (nas quais no se pode fazer um rollback se alguma coisa der errada), o MySQL tem as seguintes regras:

Otimizao de Banco de dados

Todas as colunas possuem valor padro. Se voc inserir um valor 'errado' em uma coluna, como um NULL em uma coluna NOT NULL ou um valor numrico muito grande em uma coluna numrica, o MySQL definir a coluna com o 'melhor valor possvel' em vez de dar um erro. Para valores numricos isto 0, o menor valor possvel ou o maior valor possvel. Para strings into tanto uma string vazia quanto a maior string possvel que possa estar na coluna. Todas as expresses calculadas retornam um valor que pode ser usado em vez de apresentar uma condio de erro. Por exemplo, 1/0 retorna NULL

O mostrado acima quer dizer que no se deve usar o MySQL para verificar o contedo dos campos, mas deve se fazer isto no aplicativo.

1.2. Portabilidade
Como todos os servidores SQL implementam diferentes partes de SQL, trabalhoso escrever aplicativos SQL portveis. Para selects/inserts muito simples muito fcil, mas quanto mais recursos voc precisa, mais difcil se torna. Se voc quiser uma aplicao quue rpida com muitos bancos de dados ela se torna ainda mais difcil. Para fazer um aplicativo portvel complexo voc precisa escolher um nmero de servidores SQL com o qual ele deve trabalhar. Voc pode utilizar o MySQL programa/web-page crash-me http://www.mysql.com/information/crash-me.php - para encontrar funes, tipos e limites que voc pode utilizar com uma seleo de servidores de bancos de dados. O Crash-me agora testa quase tudo possvel, mas continua compreensvel com aproximadamente 450 itens testados. Por exemplo, voc no deve ter nomes de colunas maior do que 18 caracteres se desejar utilizar o Informix ou DB2. Os programas de benchmarks e crash-me do MySQL so bastante independentes do bancos de dados. Dando uma olhada em como ns os tratamos, voc pode sentir o que necessrio para escrever sua aplicao independente do banco de dados. Os benchmarks podem ser encontrados no diretrio sql-bench na distribuio fonte do MySQL. Eles so escritos em Perl com a interface de banco de dados DBI (que resolve a parte do problema de acesso). Veja http://www.mysql.com/information/benchmarks.html para os resultados deste benchmark. Como pode ser visto nestes resultados, todos os bancos de dados tem alguns pontos fracos. Isto , eles possuem diferentes compromissos de projeto que levam a comportamentos diferentes.

Otimizao de Banco de dados

Se voc procura por independencia de banco de dados, precisar ter uma boa idia dos gargalos de cada servidor SQL. O MySQL muito rpido para recuperao e atualizao de dados, mas ter problemas em misturar leituras/escritas lentas na mesma tabela. O Oracle, por outro lado, possui um grande problema quando voc tentar acessar registros que foram recentemente atualizados (at eles serem atualizados no disco). Bancos de dados transacionais geralmente no so muito bons gerando tabelas de resumo das tabelas log, nestes casos o travamento de registros praticamente intil. Para fazer sua aplicao realmente independente de banco de dados, voc precisar definir uma interface que possa ser expandida, por meio da qual voc far a manipulao dos dados. Como o C++ est disponvel na maioria dos sistemas, faz sentido utilizar classes C++ para fazer a interface ao banco de dados. Se voc utilizar algum recurso especfico para algum banco de dados (como o comando REPLACE no MySQL), voc deve codificar um mtodo para os outros serviodores SQL para implementar o mesmo recurso (mas mais lento). Com o MySQL voc pode utilizar a sintaxe /*! */ para adicionar palavras chave especficas do MySQL para uma query. O cdigo dentro de /**/ ser tratado como um comentrio (ignorado) pela maioria dos servidores SQL. Se alta performance REAL mais importante que exatido, como em algumas aplicaes WEB, uma possibilidade criar uma camada de aplicao que armazena todos os resultados para lhe fornecer uma performance ainda mais alta. Deixando resultados antigos 'expirar' depois de um tempo, voc pode manter o cache razoavelmente atual. Isto muito bom no caso de uma carga extremamente pesada, pois neste caso voc pode aumentar o cache dinamicamente e configurar o tempo de expirao maior at que as coisas voltem ao normal. Neste caso a informao de criao de tabelas devem conter informaes do tamanho inicial do cache e com qual frequncia a tabela, normalmente, deve ser renovada.

1.3. Para que Utilizamos o MySQL?


Durante o desenvolvimento inicial do MySQL, os recursos do MySQL foram desenvolvidos para atender nosso maior cliente. Eles lidam com data warehousing para alguns dos maiores varejistas na Sucia. De todas as lojas, obtemos resumos semanais de todas as transaes de cartes de bonus e esperamos fornecer informaes teis para ajudar os donos das lojas a descobrir como suas campanhas publicitrias esto afetando seus clientes. Os dados so bem grandes (cerca de 7 milhes de transaes por ms), e armazenamos dados por cerca de 4-10 anos que precisamos apresentar para os usurios. Recebemos requisies semanais dos clientes que desejam ter acesso 'instantneo' aos novos relatrios contendo estes dados.

Otimizao de Banco de dados

Resolvemos este problema armazenando todas informaes mensalmente em tabelas com transaes compactadas. Temos um conjunto de macros (script) que geram tabelas resumidas agrupadas por diferentes critrios (grupo de produto, id do cliente, loja...) das tabelas com transaes. Os relatrios so pginas Web que so geradas dinamicamente por um pequeno shell script que analisa uma pgina Web, executa as instrues SQL na mesma e insere os resultados. Ns usariamos PHP ou mod_perl mas eles no estavam disponveis na poca. Para dados graficos escrevemos um ferramenta simples em C que pode produzir GIFs baseados no resultado de uma consulta SQL (com alguns processamentos do resultado). Isto tambm executado dinamicamente a partir do script Perl que analisa os arquivos HTML. Na maioria dos casos um novo relatrio pode simplesmente ser feito copiando um script existente e modificando a consulta SQL no mesmo. Em alguns casos, precisamos adicionar mais campos a uma tabela de resumo existente ou gerar uma nova, mas isto tambm bem simples, pois mantemos todas as tabelas com as transas no disco. (Atualmente possuimos pelo menos 50G de tabelas com transaes e 200G de outos dados do cliente.) Ns tambm deixamos nossos clientes acessarem as tabelas sumrias diretamente com ODBC para que os usurios avanados possam tambm fazer experimentar com os dados. Ns no tivemos nenhum problema lidando com isso em um servidor Sun Ultra SPARCstation (2x200 Mhz) bem modesto. Atualmente atualizamos um de nossos servidores para um UltraSPARC com 2 CPUs de 400 Mhz, e planejamos lidar com transaes no nvel de produto, o que pode significar um aumento de pelo menos dez vezes nosso volume de dados. Acreditamos que podemos lidar com isto apenas adicionando mais disco aos nossos sistemas. Tambm estamos experimentando com Intel-Linux para obter mais poder de CPU por um melhor preo. Agora que possuimos o formato binrios do bancos de dados portveis (a partir da verso 3.23), comearemos a utiliz-lo para partes da aplicao. Nossa sensao inicial que o Linux ir atuar muito melhor em cargas baixas e mdias e o Solaris ir atuar melhor quando voc comear a ter uma carga alta pelo uso extremo de IO de disco, mas ainda no temos nada conclusivo sobre isto. Depois de algumas discusses com um desenvolvedor do kernel do Linux, conclumos que isto pode ser um efeito colateral do Linux; alocar muitos recursos para uma tarefa batch que a performance interativa se torna muito baixa. Isto deixa a mquina muito lenta e sem resposta enquanto grandes batches estiverem em execuo. Esperamos que isto tenha um tratamento melhor em futuras verses do kernel Linux.

Otimizao de Banco de dados

1.4. O Pacote de Benchmark do MySQL


Esta seo deve conter uma descrio tcnica do pacote de benchmarks do MySQL (e crash-me), mas a descrio ainda no est pronta. Atualmente, voc pode ter uma boa idia do benchmark verificando os cdigos e resultados no diretrio sql-bench em qualquer distribuio fonte do MySQL. Este conjunto de benchmark pretende ser um benchmark que ir dizer a qualquer usurio que operaes uma determinada implementao SQL ir realizar bem ou mal. Note que este benchmark utiliza uma nica thead, portanto ele mede o tempo mnimo para as operaes realizadas. Planejamos adicionar vrios testes multi-threaded no conjunto de benchmark no futuro. A seguinte tabela mostra alguns resultados comparativos de benchmark para diversos servidores de bancos de dados quando acessados por meio do ODBC em uma mquina Windows NT 4.0.
Lendo 2000000 linhas por ndice mysql mysql_odbc db2_odbc informix_odbc ms-sql_odbc oracle_odbc solid_odbc sybase_odbc Segundos Segundos 367 249 464 1206 121126 1634 20800 877 17614

Inserindo 350768 linhas mysql mysql_odbc db2_odbc informix_odbc ms-sql_odbc oracle_odbc solid_odbc sybase_odbc

Segundos Segundos 381 206 619 3460 2692 4012 11291 1801 4802

Para os testes anteriores, o MySQL foi executado com um cache de ndices de 8M. Perceba que a Oracle no est includa porque eles solicitaram a remoo. Todos benchmarks Oracle devem ser aprovados pela Oracle! Acreditamos que os benchmarks da Oracle so MUITO tendecioso pois os benchmarks acima devem ser executados supostamente para uma instalao padro para um nico cliente.

Otimizao de Banco de dados

Para executar a suite de benchmarks, as seguintes exigncias devem ser satisfeitas:

O pacote de benchamark fornecido com a distribuio fonte do MySQL, assim voc deve ter uma distribuio fonte. Voc tambm pode fazer um download de uma distribuio em http://www.mysql.com/downloads/, ou usar a rvore fonte de desenvolvimento atual. Os scripts do benchmark so escritos em Perl e usam o mdulo Perl DBI para acessar o servidor de banco de dados, assim o DBI deve estar instalado. Voc tambm precisar do driver DBD espercfico do servidor para cada um dos servidores que voc quer testar. Por exemplo, para testar o MySQL, PostgreSQL, e DB2, os mdulos DBD::mysql, DBD::Pg e DBD::DB2 devem estar instalados.

O pacote de benchmark est localizado no diretrio sql-bench da distribio fonte do MySQL. Para executar o teste de benchmark, altera a localizao dentro daquele diretrio e execute o script run-all-tests: shell> cd sql-bench shell> perl run-all-tests --server=server_name server_name um dos servidores suportados. Voc pode obter uma lista de todos parmetros e servidores suportados executando run-all-tests --help. crash-me tenta determinar quais recursos um banco de dados suporta e quais suas capacidades e limitaes atuais para a execuo de consultas. Por exemplo, ele determina:

Quais tipos de colunas so suportados Quantos ndices so suportados Quais funes so suportadas Qual o tamanho mximo de uma query Qual o tamanho mximo de um registro do tipo VARCHAR

1.5. Utilizando seus Prprios Benchmarks


Definitivamente voc deve fazer benchmarks de sua aplicao e banco de dados para saber quais so os gargalos. Corrigindo (ou substituindo o gargalho com um ``mdulo burro'') voc pode facilmente identificar o prximo gargalo (e continuar). Mesmo se a performance geral para sua aplicao atualmente aceitvel, voc deve pelo menos criar um plano para cada gargalo e decidir como resolv-lo se algum dia voc precisar de performance extra. Para um exemplo de programas de benchmarks portveis, consulte o conjunto de benchmarks do MySQL. Voc pode pegar qualquer programa deste conjunto e modific-lo para suas necessidades. Fazendo isto voc pode tentar solues diferentes para seu problema e testar qual a mais rpida para voc.

Otimizao de Banco de dados

Outro pacote de benchmark grtis o Open Source Database Benchmark disponvel em http://osdb.sourceforge.net/. muito comum que um problemas ocorram apenas quando o sistema estiver muito carregado. Ns tivemos alguns clientes que nos contactaram quando eles testaram um sistema em produo e encontraram problemas de carga. Na maioria dos casos, problemas de desempenho ocorrem devido a assuntos relacionados ao projeto bsico do banco de dados (busca em tabelas no so bons com alta carga) ou problemas com o sistema operacional e de bibliotecaa. A maioria das vezes, estes problemas seriam MUITO mais fceis de resolver se os sistemas j no estivessem em uso. Para evitar problemas deste tipo, voc deve colocar algum esforo em testar a performance de toda sua aplicao sobre a pior carga possvel! Voc pode utilizar o Super Smack para isto. Ele est disponvel em: http://www.mysql.com/Downloads/super-smack/super-smack-1.0.tar.gz. Como o nome sugere, ele pode derrubar seu sistema se voc solicitar, portanto, utilize-o somente em sistemas de desenvolvimento.

Otimizao de Banco de dados

10

2. Otimizando SELECTs e Outras Consultas


2.1. Sintaxe de EXPLAIN (Obter informaes sobre uma SELECT) 2.2. Estimando o Desempenho de uma Consulta 2.3. Velocidade das Consultas que Utilizam SELECT 2.4. Como o MySQL Otimiza Clusulas WHERE 2.5. Como o MySQL Otimiza IS NULL 2.6. Como o MySQL Otimiza Clusulas DISTINCT 2.7. Como o MySQL Otimiza LEFT JOIN e RIGHT JOIN 2.8. Como o MySQL Otimiza Clusulas ORDER BY 2.9. Como o MySQL Otimiza Clusulas LIMIT 2.10. Performance das Consultas que Utilizam INSERT 2.11. Performance das Consultas que Utilizam UPDATE 2.12. Performance das Consultas que Utilizam DELETE 2.13. Mais Dicas sobre Otimizaes Primeiramente, uma coisa que afeta todas as consultas: Quanto mais complexo seu sistema de permisses, maior a sobrecarga. Se voc no tiver nenhuma instruo GRANT realizada, MySQL otmizar a verificao de permisses de alguma forma. Dessa forma, se voc possui um volume muito alto, o tempo pode piorar tentando permitir o acesso. Por outro lado, maior verificao de permisses resulta em uma sobrecarga maior. Se o seu problema com alguma funo explcita do MySQL, voc pode sempre consultar o tempo da mesma com o cliente MySQL:
mysql> SELECT BENCHMARK(1000000,1+1); +------------------------+ | BENCHMARK(1000000,1+1) | +------------------------+ | 0 | +------------------------+ 1 row in set (0.32 sec)

O exemplo acima demonstra que o MySQL pode excutar 1.000.000 expresses + ou em 0.32 segundos em um PentiumII 400MHz. Todas funes MySQL devem ser bem otimizadas, mas existem algumas excesses e o benchmark(loop_count,expression) uma tima ferramenta para saber se existe um problema com sua query.

2.1. Sintaxe de EXPLAIN (Obter informaes sobre uma SELECT)


ou EXPLAIN nome_tabela EXPLAIN SELECT opes_select

Otimizao de Banco de dados

11

EXPLAIN nome_tabela um sinnimo para DESCRIBE nome_tabela ou SHOW COLUMNS FROM nome_tabela. Quando uma instruo SELECT for precedida da palavra chave EXPLAIN, o MySQL explicar como ele deve processar a SELECT, fornecendo informao sobre como as tabelas esto sendo unidas e em qual ordem. Com a ajuda de EXPLAIN, voc pode ver quando devem ser adicionados ndices tabelas para obter uma SELECT mais rpida que utiliza ndices para encontrar os registros. Voce deve executar frequentemente ANALYZE TABLE para atualizar estatsticas de tabela tais como a cardinalidade das chaves que podem afetar a escolha que o otimizador faz. Voc tambm pode ver se o otimizador une as tabelas em uma melhor ordem. Para forar o otimizador a utilizar uma ordem especfica de join para uma instruo SELECT, adicione uma clusula STRAIGHT_JOIN. Para ligaes mais complexas, EXPLAIN retorna uma linha de informao para cada tabela utilizada na instruo SELECT. As tabelas so listadas na ordem que seriam lidas. O MySQL soluciona todas as joins utilizando um mtodo multi-join de varedura simples. Isto significa que o MySQL l uma linha da primeira tabela, depois encontra uma linha que combina na segunda tabela, depois na terceira tabela e continua. Quando todas tabelas so processadas, ele exibe as colunas selecionadas e recua atravs da lista de tabelas at uma tabela na qual existem registros coincidentes for encontrada. O prximo registro lido desta tabela e o processo continua com a prxima tabela. No MySQL verso 4.1 a sada do EXPLAIN foi alterada para funcionar melhor com construes como UNIONs, subqueries e tabelas derivadas. A mais notvel a adio de duas novas colunas: id e select_type. A sada de EXPLAIN inclui as seguintes colunas:

id Identificador SELECT, o nmero sequncial desta SELECT dentro da consulta.

select_type Tipo de clusula SELECT, que pode ser uma das seguintes: SIMPLE SELECT simples (sem UNIONs ou subqueries). PRIMARY SELECT mais externa.

Otimizao de Banco de dados

12

UNION Segunda SELECT e as SELECTs posteriores do UNION DEPENDENT UNION Segunda SELECT e SELECTs posteriores do UNION, dependente da subquery exterior. SUBQUERY Primeiro SELECT na subquery. DEPENDENT SUBQUERY Primeiro SELECT, dependente da subquery exterior. DERIVED SELECT de tabela derivada (subquery na clusula FROM).

table A tabela para a qual a linha de sada se refere.

type

O tipo de join. Os diferentes tipos de joins so listados aqui, ordenados do melhor para o pior tipo: system A tabela s tem uma linha (= tabela de sistema). Este um caso especial do tipo de join const. const A tabela tm no mximo um registro coincidente, o qual ser lido na inicializao da consulta. Como s h um registro, os valores da coluna neste registro podem ser considerados constantes pelo resto do otimizador. Tabelas const so muito rpidas e so lidas apenas uma vez! const usado quando voc compara todas as partes de uma chave PRIMARY/UNIQUE com restries:
SELECT * FROM const_table WHERE primary_key=1; SELECT * FROM const_table WHERE primary_key_part1=1 AND primary_key_part2=2;

Otimizao de Banco de dados

13

eq_ref Uma linha ser lida desta tabela para cada combinao de linhas da tabela anterior. Este o melhor tipo de join depois dos tipos const. usado quando todas as partes do ndice so usados pela join e o ndice nico (UNIQUE) ou uma chave primria (PRIMARY KEY). eq_ref pode ser usado para coluna indexadas que comparada com o\ operador =. O item comparado pode ser uma constante ou uma expresso que usa colunas de tabelas que so lidas antes desta tabela. Nos seguintes examplos, ref_table poder usar eq_ref
SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1; ref

Todas as colunas com valores de ndices correspondentes sero lidos desta tabela para cada combinao de registros da tabela anterior. ref usado se o join usa apenas o prefixo mais a esquerda da chave, ou se a chave no nica (UNIQUE) ou uma chave primria (PRIMARY KEY) (em outras palavras, se a join no puder selecionar um nico registro baseado no valor da chave). Se a chave que usada coincide apenas em alguns registros, este tipo de join bom. ref pode ser usado para colunas indexadas que so comparadas com o operador =. Nos seguintes exemplos, ref_table poder usar ref
SELECT * FROM ref_table WHERE key_column=expr; SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;

ref_or_null Como ref, mas com o adicional que faremos uma busca extra para linhas com NULL.
SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;

Esta otimizao do tipo join nova para o MySQL 4.1.1 e mais usada na resoluo de sub queries. range

Otimizao de Banco de dados

14

Apenas registros que esto numa dada faixa sero retornados, usando um ndice para selecionar os registros. A coluna key indica qual ndice usado. key_len contm a maior parte da chave que foi usada. A coluna ref ser NULL para este tipo. range pode ser usado para quando uma coluna de chave comparada a uma constante com =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN e IN.
SELECT * FROM range_table WHERE key_column = 10; SELECT * FROM range_table WHERE key_column BETWEEN 10 and 20; SELECT * FROM range_table WHERE key_column IN (10, 20, 30); SELECT * FROM range_table WHERE key_part1= 10 and key_part2 IN (10, 20, 30);

index Isto o mesmo que ALL, exceto que apenas a rvore de ndice varrida. Isto normalmente mais rpido que ALL, j que o arquivo de ndice normalmente menor que o arquivo de dados. Ele pode ser usado quando a consulta s usa colunas que so parte de um ndice. ALL Ser feita uma varredura completa da tabela para cada combinao de registros da tabela anterior. Isto normalmente no bom se a tabela a primeiro tabela no marcada como const, e normalmente muito ruim em todos os casos ordenados. Voc normalmente pode ebitar ALL adicionando mais ndices, assim o registro pode ser retornado baseado em valores constantes ou valores de colunas de tabelas anteriores.

possible_keys

A coluna possible_keys indica quais ndices o MySQL pode utilizar para encontrar os registros nesta tabela. Note que esta coluna totalmente independente da ordem das tabelas. Isto significa que algumas das chaves em possible_keys podem no ser usadas na prtica com a ordem de tabela gerada. Se esta coluna for NULL, no existem ndices relevantes. Neste caso, voc poder melhora a performance de sua query examinando a clusula WHERE para ver se ela refere a alguma coluna ou colunas que podem ser indexadas. Se for verdade, crie um ndice apropriado e confira a consulta com EXPLAIN novamente. Para ver os ndices existentes em uma tabela, utilize SHOW INDEX FROM nome_tabela.

key

Otimizao de Banco de dados

15

A coluna key indica a chave (ndice) que o MySQL decidiu usar. A chave ser NULL se nenhum ndice for escolhido. Para forar o MySQL a usar um ndice listado na coluna possible_keys, use USE INDEX/IGNORE INDEX em sua consulta. Executando myisamchk -- ou ANALYSE TABLE na tabela tambm ajudar o otimizador a escolher ndices melhores.

key_len A coluna key_len indica o tamanho da chave que o MySQL decidiu utilizar. O tamanho ser NULL se key for NULL. Note que isto nos diz quantas partes de uma chave multi-partes o MySQL realmente est utilizando.

ref A coluna ref exibe quais colunas ou contantes so usadas com a key para selecionar registros da tabela.

rows A coluna rows informa o nmero de linhas que o MySQL deve examinar para executar a consulta.

Extra

Esta coluna contem informaes adicionais de como o MySQL ir resolver a consulta. A seguir uma explicao das diferentes strings de texto que podem ser encontradas nesta coluna: Distinct O MySQL no continuar a procurar por mais registros para a combinao de registro atual depois de ter encontrado o primeiro registro coincidente. Not exists O MySQL estava apto a fazer uma otimizao LEFT JOIN na consulta e no examinar mais registros nesta tabela para a combinao do registro anterior depois que encontrar um registro que satisfaa o critrio do LEFT JOIN. Exemplo:
SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;

Assume que t2.id definido com NOT NULL. Neste caso o MySQL ir percorrer t1 e procurar pelos registros em t2 atravs de t1.id. Se o MySQL encontrar um registro combinando em t2, ele sabe que t2.id nunca poder ser NULL e no ir percorrer at o resto dos registros em t2 que possuirem o mesmo id. Em outras

Otimizao de Banco de dados

16

palavras, para cada registro em t1 o MySQL s precisa fazer uma nica pesquisa em t2, independente de quantos registros coincidentes existirem em t2. range checked for each record (index map: #) O MySQL no encontrou um bom ndice para usar. No lugar, ele ir fazer uma verificao sobre qual ndice usar (se existir) para cada combinao das tabelas precedentes, e usar este ndice para recuperar os registros da tabela. Isto no muito rpido mas mais rpido que fazer um join sem um ndice. Using filesort O MySQL precisar fazer uma passada extra para descobrir como recuperar os registros na ordem de classificao. A classificao feita indo atravs de todos os registros de acordo com join type e armazenar a chave de ordenao mais o ponteiro para o registro para todos os registros que combinarem com o WHERE. Ento as chaves so classificadas. Finalmente os registros so recuperados na ordem de classificao. Using index A informao da coluna recuperada da tabela utilizando somente informaes na rvore de ndices sem ter que fazer uma pesquisa adicional para ler o registro atual. Isto pode ser feito quando todas as colunas usadas para a tabela fizerem parte do mesmo ndice. Using temporary Para resolver a consulta, o MySQL precisar criar uma tabela temporria para armazenar o resultado. Isto acontece normalmente se voc fizer um ORDER BY em um conjunto de colunas diferentes das quais voc fez um GROUP BY. Using where Uma clusula WHERE ser utilizada para restringir quais registros sero combinados com a prxima tabela ou enviar para o cliente. se voc no possui esta informao e a tabela do tipo ALL ou index, pode existir alguma coisa errada na sua query (Se voc no pretender examinar todos os registros da tabela). Se voc desejar deixar suas consultas o mais rpido possvel, voc deve dar uma olhada em Using filesort e Using temporary. Voc pode ter uma boa indicao de quo boa sua join multiplicando todos os valores na coluna rows na sada de EXPLAIN. Isto deve dizer a grosso modo quantos registros o MySQL deve examinar para executar a consulta. Este nmero tambm usado quando voc restringe consultas com a varivel max_join_size. O exemplo a seguir mostra como um JOIN pode ser otimizado progressivamente utilizando a informao fornecida por EXPLAIN.

Otimizao de Banco de dados

17

Suponha que voc tem a instruo SELECT exibida abaixo, que voc est examinando utilizando EXPLAIN:
EXPLAIN SELECT tt.TicketNumber, tt.TimeIn, tt.ProjectReference, tt.EstimatedShipDate, tt.ActualShipDate, tt.ClientID, tt.ServiceCodes, tt.RepetitiveID, tt.CurrentProcess, tt.CurrentDPPerson, tt.RecordVolume, tt.DPPrinted, et.COUNTRY, et_1.COUNTRY, do.CUSTNAME FROM tt, et, et AS et_1, do WHERE tt.SubmitTime IS NULL AND tt.ActualPC = et.EMPLOYID AND tt.AssignedPC = et_1.EMPLOYID AND tt.ClientID = do.CUSTNMBR;

Para este exemplo, assuma que:

As colunas comparadas foram declaradas como a seguir:


Tabela tt tt tt et do Coluna ActualPC AssignedPC ClientID EMPLOYID CUSTNMBR Tipo da coluna CHAR(10) CHAR(10) CHAR(10) CHAR(15) CHAR(15)

As tabelas possuem os ndices mostrados abaixo:


Tabela tt tt tt et do ndice ActualPC AssignedPC ClientID EMPLOYID (chave primria) CUSTNMBR (chave primria)

The tt.ActualPC values aren't evenly distributed.

Initially, before any optimizations have been performed, the EXPLAIN statement produces the following information:
table et do et_1 tt type possible_keys key key_len ALL PRIMARY NULL NULL ALL PRIMARY NULL NULL ALL PRIMARY NULL NULL ALL AssignedPC,ClientID,ActualPC NULL NULL range checked for each record (key map: 35) ref NULL NULL NULL NULL rows 74 2135 74 3872 Extra

Otimizao de Banco de dados

18

Como o tipo ALL em todas tabelas, esta sada indica que o MySQL est gerando um produto Cartesiano de todas as tabelas! Isto levar muito tempo para ser executado, pois o produto do nmero de registros em cada tabela deve ser examinado ! Neste caso, existem 74 * 2135 * 74 * 3872 registros. Se as tabelas forem maiores, imagine quanto tempo este tipo de consulta pode demorar. Um dos problemas aqui que o MySQL no pode (ainda) utilizar ndices em colunas de maneira eficiente se elas foram declaras ide forma diferente. Neste contexto, VARCHAR e CHAR so o mesmo a menos que tenham sido declarados com tamanhos diferentes. Como tt.ActualPC declarado como CHAR(10) e et.EMPLOYID declarado como CHAR(15), existe aqui uma diferena de tamanho. Para corrigir esta diferena entre tamanhos de registros, utilize ALTER TABLE para alterar o tamanho de ActualPC de 10 para 15 caracteres:
mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

Agora ambos campos tt.ActualPC e et.EMPLOYID so VARCHAR(15). Executando a instruo EXPLAIN novamente produzir este resultado:
table type possible_keys key key_len ref tt ALL AssignedPC,ClientID,ActualPC NULL NULL NULL where do ALL PRIMARY NULL NULL NULL range checked for each record (key map: 1) et_1 ALL PRIMARY NULL NULL NULL range checked for each record (key map: 1) et eq_ref PRIMARY PRIMARY 15 tt.ActualPC rows 3872 2135 74 1 Extra Using

Isto no est perfeito, mas est bem melhor ( o produto dos valores de rows agora menor por um fator de 74 ). Esta verso executada em vrios segundos. Uma segunda alterao pode ser feita para eliminar as diferenas de tamanho das colunas para as comparaes tt.AssignedPC = et_1.EMPLOYID e tt.ClientID = do.CUSTNMBR
mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15), -> MODIFY ClientID VARCHAR(15);

Agora EXPLAIN produz a sada mostrada abaixo:


table type et ALL tt ref where possible_keys PRIMARY AssignedPC, key key_len ref NULL NULL NULL ActualPC 15 et.EMPLOYID rows Extra 74 52 Using

et_1 do

ClientID, ActualPC eq_ref PRIMARY eq_ref PRIMARY

PRIMARY PRIMARY

15 15

tt.AssignedPC 1 tt.ClientID 1

Este resultado quase o melhor que se pode obter.

Otimizao de Banco de dados

19

O problema restante que, por padro, o MySQL assume que valores na coluna tt.ActualPC esto distribudos igualmente, e este no o caso para a tabela tt. Felizmente, fcil informar ao MySQL sobre isto:
shell> myisamchk --analyze PATH_TO_MYSQL_DATABASE/tt shell> mysqladmin refresh

Agora a join est perfeita, e EXPLAIN produz esta sada:


table type tt ALL where possible_keys key AssignedPC NULL key_len ref NULL NULL rows Extra 3872 Using

et et_1 do

ClientID, ActualPC eq_ref PRIMARY eq_ref PRIMARY eq_ref PRIMARY

PRIMARY 15 PRIMARY 15 PRIMARY 15

tt.ActualPC 1 tt.AssignedPC 1 tt.ClientID 1

Perceba que a coluna rows na sada de EXPLAIN uma boa ajuda para otimizador de joins do MySQL. Para otimizar uma consulta, voc deve conferir se os nmeros esto perto da realidade. Se no, voc pode obter melhor desempenho utilizando STRAIGHT_JOIN em sua instruo SELECT e tentar listar as tabelas em uma ordem diferente na clusula FROM.

2.2. Estimando o Desempenho de uma Consulta


Na maioria dos casos voc pode estimar a performance contando buscas em disco. Para tabelas pequenas, normalmente voc pode encontrar o registro com 1 pesquisa em disco (uma vez que o ndice provavelmente est no cache). Par tabelas maiores, voc pode estimar (usando indces de arvores B++) que voc precisar de: log(row_count) / log(index_block_length / 3 * 2 / (index_length + data_pointer_length)) + 1 buscas em disco para encontrar um registro. No MySQL um bloco de ndice tem geralmente 1024 bytes e o ponteiro de dados 4 bytes. Uma tabela de 500.000 registros com um ndice com tamanho de 3 (inteiro mdio) lhe d: log(500,000)/log(1024/3*2/(3+4)) + 1 = 4 pesquisas. Como o ndice acima necessita cerca de 500,000 * 7 * 3/2 = 5.2M, (assumindo que os buffers de ndices so carregados at 2/3, que o normal) voc provavelmente ter grande parte dos ndices em memria e provavelmente precisar somente de 1 ou 2 chamadas para ler dados do SO para encontrar o registro. Entretanto, para escritas, voc precisar utilizar 4 requisies para encontrar onde posicionar o novo ndice e normalmente 2 buscas para atualizar o ndice e escrever o registro. Perceba que o que foi dito acima no significa que sua aplicao perder performance por N log N! Como tudo armazenado no cache de seu SO ou do servidor SQL as coisas comearo a ficar um pouco mais lentas quando as tabelas comearem a

Otimizao de Banco de dados

20

crescer. Quando os dados se tornam muito grandes para o cache, as coisas comearo a ficar bem mais lentas at que suas aplicaes estejam limitadas a buscas em disco (o que aumenta em N log N). Para evitar isto, aumente o cache de ndice quando os dados crescerem.

2.3. Velocidade das Consultas que Utilizam SELECT


Em geral, quando voc desejar tornar uma consulta lenta SELECT ... WHERE mais rpida, a primeira coisa que deve ser conferida se voc pode ou no adicionar um ndice. Todas as referncias entre diferentes tabelas devem ser feitas normalmente com ndices. Voc pode utilizar o comando EXPLAIN para determinas quais ndices so usados para uma SELECT. Algumas dicas gerais:

Para ajudar o MySQL a otimizar melhor as consultas, execute myisamchk -analyze em uma tabela depois dela ter sido carregada com dados relevantes. Isto atualiza um valor para cada parte do ndice que indica o nmero mdio de registros que tem o mesmo valor. (Para ndices nicos, isto sempre 1, claro). O MySQL usar isto para decidir qual ndice escolher quando voc conectar duas tabelas utilizando uma 'expresso no constante'. Os resultados de analyze podem ser conferidos utilizando SHOW INDEX FROM nome_tabela e examindo a coluna Cardinality. Para ordenar um ndice e dados de acordo com um ndice, utilize myisamchk -sort-index --sort-records=1 (se voc deseja ordenar pelo ndice 1). Se voc possui um ndice unico no qual deseja ler todos registros na ordem do ndice, esta uma boa forma para torn-lo mais rpido. Perceba entretanto, que esta ordenao no foi escrita de maneira otimizada e levar muito tempo em tabelas grandes!

2.4. Como o MySQL Otimiza Clusulas WHERE


As otimizaes WHERE so colocadas aqui na parte da SELECT porque normalmente elas so usadas com SELECT, mas as mesmas otimizaes aplicam-se para WHERE em instrues DELETE e UPDATE. Note tambm que esta seo est incompleta. O MySQL faz vrias otimizaes e ainda no tivemos tempo para documentarmos todas elas. Algumas das otimizaes feitas pelo MySQL so so listadas abaixo: Remoo de parnteses desnecessrios:
((a AND b) AND c OR (((a AND b) AND (c AND d)))) -> (a AND b AND c) OR (a AND b AND c AND d)

Otimizao de Banco de dados

21

Enlaos de constantes:
(a<b AND b=c) AND a=5 -> b>5 AND b=c AND a=5

Remoo de condies contantes (necessrio por causa dos enlaos de contantes):


(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6) -> B=5 OR B=6

Expresses constantes utilizadas por ndices so avaliadas somente uma vez.

COUNT(*) em uma nica tabela sem um WHERE recuperado diretamente da informao da tabela dos tipos MyISAM e HEAP. Isto tambm feito para qualquer expresso NOT NULL quando usada somente com uma tabela. Pr deteco de expresses contantes invlidas. O MySQL detecta rapidamente que algumas instrues SELECT so impossveis e no retornar registros. HAVING fundido com WHERE se no for utilizado GROUP BY ou funes de agrupamento (COUNT(), MIN()...). Para cada sub-join, um WHERE mais simples construdo para obter uma avaliao mais rpida de WHERE para cada sub-join e tambm para saltar registros da maneira mais rpida possvel. Todas tabelas constantes so lidas primeiro, antes de qualquer tabelas na consulta. Uma tabela constante : Uma tabela vazia ou uma tabela com 1 registro. Uma tabela que usada com uma clusula WHERE em um ndice UNIQUE, ou uma PRIMARY KEY, onde todas as partes do ndice so usadas com expresses constantes e as partes do ndice so definidas como NOT NULL.

Todas as tabelas seguintes so usadas como tabelas constantes:


mysql> SELECT * FROM t WHERE primary_key=1; mysql> SELECT * FROM t1,t2 -> WHERE t1.primary_key=1 AND t2.primary_key=t1.id;

A melhor combinao de join para unir as tabelas encontrada tentando todas as possibilidades. Se todas colunas em ORDER BY e em GROUP BY vierem da mesma tabela, ento esta tabela ser preferencialmente a primeira na unio. Se existerem uma clusula ORDER BY e uma GROUP BY diferente, ou se a ORDER BY ou GROUP BY conterem colunas de tabelas diferentes da primeira tabela na fila de join, uma tabela temporria ser criada. Se voc utilizar SQL_SMALL_RESULT, o MySQL usar a tabela temporria em memria. Cada ndice de tabela consultado e o melhor ndice que cobrir menos de 30% dos registros usado. Se nenhum ndice for encontrado, uma varredura rpida feita pela tabela. Em alguns casos, o MySQL pode ler registros do ndice mesmo sem consultar o arquivo de dados. Se todas colunas usadas do ndice so numricas, ento somente a rvore de ndice usada para resolver a consulta.

Otimizao de Banco de dados

22

Antes de dar sada em cada registro, aqueles que no combinam com a clusula HAVING so ignorados.

Alguns exemplos de querrys que so mais rpidas:


mysql> mysql> mysql> -> mysql> -> mysql> ->
SELECT COUNT(*) FROM tbl_name; SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name; SELECT MAX(key_part2) FROM tbl_name WHERE key_part_1=constant; SELECT ... FROM tbl_name ORDER BY key_part1,key_part2,... LIMIT 10; SELECT ... FROM tbl_name ORDER BY key_part1 DESC,key_part2 DESC,... LIMIT 10;

As seguintes consultas so resolvidas utilizando somente a rvore de ndices (assumindo que as colunas indexadas so numricas):
mysql> SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val; mysql> SELECT COUNT(*) FROM tbl_name -> WHERE key_part1=val1 AND key_part2=val2; mysql> SELECT key_part2 FROM tbl_name GROUP BY key_part1;

As consultas a seguir utilizam indexao para recuperar os registros na ordem de classificao sem um passo de ordenao separado:
mysql> SELECT ... FROM tbl_name -> ORDER BY key_part1,key_part2,... ; mysql> SELECT ... FROM tbl_name -> ORDER BY key_part1 DESC,key_part2 DESC,... ;

2.5. Como o MySQL Otimiza IS NULL


O MySQL pode fazer a mesma otimizao em column IS NULL que ele pode com column = constant_value. Por exemplos, o MySQL pode usar ndices e faixas para buscar por NULL com IS NULL.
SELECT * FROM table_name WHERE key_col IS NULL; SELECT * FROM table_name WHERE key_col <=> NULL; SELECT * FROM table_name WHERE key_col=# OR key_col=# OR key_col IS NULL

Se voc usa column_name IS NULL em um NOT NULL em uma clusula WHERE na tabela que no usada no OUTER JOIN, esta espresso ser otimizada de qualquer forma. O MySQL 4.1. pode adicionalmente otimizar a combinao column = expr AND column IS NULL, uma forma que comum em sub queries resolvidas. EXPLAIN mostrar ref_or_null quando esta otimizao usada.

Otimizao de Banco de dados

23

Esta otimizao pode tratar um IS NULL para qualquer parte da chave. Alguns exemplos de consultas que so otimizadas (assumindo chave em t2 (a,b)):
SELECT * FROM t1 WHERE t1.a=expr OR t1.a IS NULL; SELECT * FROM t1,t2 WHERE t1.a=t2.a OR t2.a IS NULL; SELECT * FROM t1,t2 WHERE (t1.a=t2.a OR t2.a IS NULL) AND t2.b=t1.b; SELECT * FROM t1,t2 WHERE t1.a=t2.a AND (t2.b=t1.b OR t2.b IS NULL); SELECT * FROM t1,t2 WHERE (t1.a=t2.a AND t2.a IS NULL AND ...) OR (t1.a=t2.a AND t2.a IS NULL AND ...);

ref_or_null funciona fazendo primeiro uma leitura na chave indicada e depois disto uma busca separada por linhas com chave NULL. Note que a otimizao s pode tratar um nvel IS NULL.
SELECT * FROM t1,t2 where (t1.a=t2.a AND t2.a IS NULL) OR (t1.b=t2.b AND t2.b IS NULL);

No caso acima o MySQL s usar busca de chave na parte (t1.a=t2.a AND t2.a IS NULL) e no poder usar a parte da chave em b.

2.6. Como o MySQL Otimiza Clusulas DISTINCT


DISTINCT combinado com ORDER BY tambm ir em vrios casos criar uma tabela temporria. Note que como DISTINCT pode usar GROUP BY, voc deve estar ciente de como o MySQL funciona com campos na parte ORDER BY ou HAVING que no so parte dos campos selecionados. See Seco 6.3.7.3, GROUP BY com Campos Escondidos. Quando combinando LIMIT row_count com DISTINCT, o MySQL ir parar logo que encontrar row_count registros nicos. Se voc no utiliza colunas de todas tabelas usadas, o MySQL ir parar a varredura das tabelas no usadas logo que encontrar a primeira coincidncia.
SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a;

Neste caso, assumindo que t1 usando antes de t2 (confira com EXPLAIN), MySQL ir parar de ler de t2 (para aquele registro particular em t1) quandoo primeiro registro em t2 for encontrado.

Otimizao de Banco de dados

24

2.7. Como o MySQL Otimiza LEFT JOIN e RIGHT JOIN


A LEFT JOIN B join_condition no MySQL est implementada como a seguir:

A tabela B configurada para ser dependente da tabela A e de todas as tabelas das quais A depende. A tabela A configurada para ser dependente de todas as tabelas (exceto B) que so usadas na condio LEFT JOIN. A condio LEFT JOIN usada para decidir como devemos recuperar registros a partir da tabela B. (Em outras palavras, qualquer condio na clasula WHERE no usada). Todas as otimizaes padres de join so feitas, com a excesso que uma tabela sempre lida depois de todas as tabelas das quais dependente. Se existir uma dependncia circular o MySQL ir emitir um erro. Todas as otimizaes padres de WHERE so realizadas. Se existir um registro em A que coincida com a clusula WHERE, mas no existir nenhum registro em B que coincida com a condio ON ento um registro extra em B gerado com todas as colunas com valor NULL. Se voc utiliza LEFT JOIN para encontrar registros que no existem em alguma tabela e est usando o seguinte teste: nome_coluna IS NULL na parte WHERE, onde nome_colun um campo que declarado como NOT NULL, ento o MySQL para de pesquisar por mais registros (para uma combinao particular de chaves) depois de ter encontrado um registro que combinar com a condio LEFT JOIN.

RIGHT JOIN implementado de forma anloga LEFT JOIN. A ordem de leitura das tabelas forada por LEFT JOIN e STRAIGHT JOIN ir ajudar o otimizador de joins (que calcula em qual ordem as tabelas devem ser unidas) a fazer seu trabalho mais rapidamente, j que havero poucas permutaes de tabelas a serem conferidas. Perceba que o texto acima significa que se voc fizer uma consulta do tipo:
SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d (d.key=a.key) WHERE b.key=d.key

A partir do MySQL 4.0.14, o MySQL faz a seguinte otimizao LEFT JOIN: Se a condio WHERE sempre falsa para a linha NULL gerada, o LEFT JOIN alterado para um join normal. Por exemplo, na seguinte consulta a clusula WHERE seria falso se t2.coluna fosse NULL, asssim seguro converter para uma join normal.
SELECT * FROM t1 LEFT t2 ON (column) WHERE t2.column2 =5; -> SELECT * FROM t1,t2 WHERE t2.column2=5 AND t1.column=t2.column;

Otimizao de Banco de dados

25

Isto pode ser feito mais rpido j que o MySQL pode agora usar a tabela t2 antes da tabela t1 se resultasse consulta melhor. Para forar uma ordem de tabela especfica, use STRAIGHT JOIN. O MySQL ir fazer uma pesquisa completa em b j que o LEFT JOIN ir fora-lo a ser lido antes de d. A correo neste caso alterar a consulta para:
SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d (d.key=a.key) WHERE b.key=d.key

2.8. Como o MySQL Otimiza Clusulas ORDER BY


Em alguns casos o MySQL pode utilizar ndices para satisfazer uma requisio de ORDER BY ou GROUP BY sem fazer uma ordenao extra. O ndice tambm pode ser usado mesmo se o ORDER BY no coincidir exatamente com o ndice, uma vez que todas as partes de ndices no usadas e todos os extras na coluna ORDER BY so constantes na clusula WHERE. A seguinte consulta usar o ndice para resolver a parte ORDER BY / GROUP BY:
SELECT SELECT SELECT SELECT SELECT DESC * * * * * FROM FROM FROM FROM FROM t1 t1 t1 t1 t1 ORDER WHERE WHERE ORDER WHERE BY key_part1,key_part2,... key_part1=constante ORDER BY key_part2 key_part1=constante GROUP BY key_part2 BY key_part1 DESC,key_part2 DESC key_part1=1 ORDER BY key_part1 DESC,key_part2

Alguns casos onde o MySQL no pode usar ndices para resolver o ORDER BY: (Note que o MySQL ainda usar ndices para encontrar o registro que coincide com a clusula WHERE):

Voc est fazendo um ORDER BY em diferentes chaves: SELECT * FROM t1 ORDER BY key1,key2

Voc est fazendo um ORDER BY usando partes de chaves no consecutivas. SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2

Voc est misturando ASC e DESC. SELECT * FROM t1 ORDER BY key_part1 DESC,key_part2 ASC

As chaves usadas para buscar os registros so as mesmas usadas para fazer o ORDER BY:

Otimizao de Banco de dados

26

SELECT * FROM t1 WHERE key2=constant ORDER BY key1

Voc est unindo muitas tabelas e as colunas nas quais voc est fazendo um ORDER BY no so todas da primeira tabela que no const e que usada para retornar registros. (Esta a primeira tabela na sada do EXPLAIN que no usa um mtodo de busca de registro const). Voc tem diferentes expresses ORDER BY e GROUP BY. O ndice da tabela usada um tipo de ndice que no armazena registros em ordem. (Como o ndice HASH em tabelsn HEAP).

Nestes casos onde o MySQL tem que ordenar o resultado, ele usa o seguinte algoritmo:

L todos os registros de acordo com a chave ou por uma varredura da tabela. Registros que no coincidem com a clusula WHERE so saltados. Armazena a chave ordenada em um buffer (de tamanho sort_buffer). Quando o buffer ficar cheio, execute ordeno-o e armazene o resultado em um arquivo temposrrio. Salve um ponteiro para o bloco ordenado. (No caso de todos os regitros caberem no buffer ordenado, nenhum arquivo temporrio criado). Repete o armazenamento acima at todas as linhas tenham sido lidos. Faz um multi-merge at MERGEBUFF (7) regies para um bloco em outro arquivo temporrio. Repete at que todos os blocos do primeiro arquivo estejam no segundo arquivo. Repete o seguinte at que restem menos que MERGEBUFF2 (15) blocos. No ltimo multi-merge, s o ponteiro para o registro (ltima parte de chave ordenada) escrito em um arquivo de resultado. Agora o cdigo em sql/records.cc ser usado para ler atravs deles ordenadamente usando os ponteiros de registro no arquivo resultante. Para otimizao , lemos em um grande bloco de ponteiros de registros, ordena-os ento lemos o registros ordenadamente de de um buffer de registro. (read_rnd_buffer_size) .

Voc pode verificar com EXPLAIN SELECT ... ORDER BY se o MySQL pode usar ndices para resolver a consulta. Se voc obtiver Using filesort na coluna extra, ento o MySQL no pode usar ndices para resolver o ORDER BY. Se voc quiser ter uma velocidade ORDER BY maior, primeiro voc deve ver se voc pode fazer que o MySQL use ndices em vez de fazer um fase de ordenao extra. Se no for possvel, ento voc pode fazer:

Aumente o tamanho da varivel sort_buffer_size. Aumente o temenho da varivel read_rnd_buffer_size. Altere tmpdir para apontar para um disco dedicado com muito espao vazio. Se voc usa o MySQL 4.1 ou posterior voc pode distribuir a carga entre diversos discos fsicos definindo tmpdir com uma lista de caminhos separados por dois pontos : (ponto e vrgula ; no Windows). Eles sero usados de acordo com o mtodo round-robin. Nota: Estes caminho devem estar em diferentes discos fsicos, e no em diferentes parties do mesmo disco.

Otimizao de Banco de dados

27

Por padro, o MySQL ordena todas as consultas GROUP BY x,y[,...] como se voc tivesse especificado ORDER BY x,y[,...]. Se voc incluir a clusula ORDER BY explicitamente, o MySQL a otimizar sem qualquer penalidade na velocidade, embora a ordenacao ainda ocorra. Se a consulta inclui um GROUP BY mas voc deseja evitar a sobrecarga da ordenar o resultado, voc pode suprimir a ordenacao especificando ORDER BY NULL:
INSERT INTO foo SELECT a,COUNT(*) FROM bar GROUP BY a ORDER BY NULL;

2.9. Como o MySQL Otimiza Clusulas LIMIT


Em alguns casos o MySQL ir tratar a consulta de maneira diferente quando voc estiver utilizando LIMIT row_count e no estiver utilizando HAVING:

Se voc estiver selecionando apenas alguns registros com LIMIT, o MySQL usar ndices em alguns casos quando ele normalmente preferiria fazer uma varredura completa na tabela. Se voc utilizar LIMIT row_count com ORDER BY, O MySQL ir terminar a ordenao logo que ele encontrar os primeiros row_count registros em vez de ordenar a tabela inteira. Ao combinar LIMIT row_count com DISTINCT, o MySQL ir parar logo que ele encontrar row_count registros nicos. Em alguns casos um GROUP BY pode ser resolvido lendo a chave em ordem (ou fazer uma classificao na chave) e ento calcular resumos at o valor da chave alterar. Neste caso, LIMIT row_count no ir calcular nenhum GROUP BY desnecessrio. Logo que o MySQL enviar os primeiros # registros para o cliente, ele ir abortar a consulta. LIMIT 0 ir sempre retornar rapidamente um conjunto vazio. Isto util para conferir a consulta e obter os tipos de campos do resultado. Quando o servidor utiliza tabelas temporrias para resolver a consulta, o LIMIT row_count usado para calcular a quantidade de espao necessrio.

2.10. Performance das Consultas que Utilizam INSERT


O tempo para inserir um registro consiste aproximadamente de:

Conexo: (3) Enviar a consulta para o servidor: (2) Analisar a consulta (2) Inserir o registro: (1 x tamanho do registro) Inserir os ndices: (1 x nmero de ndices) Fechar: (1)

Otimizao de Banco de dados

28

onde os nmeros so de certa forma proporcionais ao tempo total. Isto no leva em consideraco o sobrecarga inicial para abrir tabelas (que feita uma vez para cada consulta concorrente em execuo). O tamanho da tabela diminuem a velocidade da insero de ndices em N log N (Arvores B). Algumas maneiras de acelerar as inseres:

Se voc estiver inserindo vrios registros do mesmo cliente ao mesmo tempo, utilize instrues INSERT com listas de mltiplos valores. Isto muito mais rpido (muitas vezes em alguns casos) do que utilizar instrues INSERT separadas. Se voc esta adicionando dados a uma tabela que no est vazia, voc pode ajustar a varivel bulk_insert_buffer_size para tornr isto mais rpido. Se voc inserir vrios registros de diferentes clientes, voc pode obter velocidades mais altas utilizando a instruo INSERT DELAYED. Perceba que com MyISAM voc pode inserir registros ao mesmo tempo que SELECTs estejam executando se no existirem registros apagados nas tabelas. Ao carregar uma tabela de um arquivo texto, utilize LOAD DATA INFILE. Isto normalmente 20 vezes mais rpido do que utilizar vrias instrues INSERT. possvel com algum trabalho extra fazer o LOAD DATA INFILE executar ainda mais rpido quando a tabela tiver vrios ndices. Utilize o seguinte procedimento: a. Opcionalmente crie a tabela com CREATE TABLE. Por exemplo, utilizando mysql ou Perl-DBI. b. Execute a instruo FLUSH TABLES ou o comando shell mysqladmin flush-tables. c. Utilize myisamchk --keys-used=0 -rq /path/to/db/nome_tabela. Isto remover o uso de todos os ndices da tabela. d. Insira dados na tabela com LOAD DATA INFILE. Isto no atualizar ndices e ser muito mais rpido. e. Se no futuro voc precisar da tabela somente para leitura, execute myisampack na mesma para torn-la menor. f. Recrie os ndices com myisamchk -r -q /caminho/para/bd/nome_tabela. Isto criar a rvore de ndices em memria antes de escrev-la para o disco, que muito mais rpido porque evita que seja feita muita busca disco. A rvore de ndices resultante tambm balanceada perfeitamente. g. Execute uma instruo FLUSH TABLES ou o comando shell mysqladmin flush-tables.

Note que LOAD DATA INFILE tamb faz a otimizao acima se voc a insero for em uma tabela vazia; a principal diferena com o procedimento acima qeu voc pode deixar o myisamchk alocar muita mais memria temporria para a criao do ndice que voc deseje que o MySQL alocasse para todas as recriaes de ndice. Desde o MySQL 4.0 voc tambm pode usar ALTER TABLE nome_tbl em vez de DISABLE KEYS myisamchk --keys-used=0 -rq /caminho/para/bd/nome_tbl e ALTER TABLE nome_tbl ENABLE KEYS em vez

Otimizao de Banco de dados

29

de myisamchk -r -q /caminho/para/bd/nome_tbl. Deste modo voc tambm pode saltar os passos FLUSH TABLES.

Voc pode acelerar inseres feitas usando vrias instrues bloqueando suas tabelas:
LOCK TABLES a WRITE; INSERT INTO a VALUES (1,23),(2,34),(4,33); INSERT INTO a VALUES (8,26),(6,29); UNLOCK TABLES;

mysql> mysql> mysql> mysql>

A principal diferena na velocidade que o buffer de ndices descarregado no disco somente uma vez, depois de todas instrues INSERT term sido completadas. Normalmente existiria tantas descargas do buffer de ndices quanto instrues INSERT diferentes. O bloqueio no necessrio se voc pode inserir todos registros com uma simples instruo. Para tabelas transacionais, voc deve usar BEGIN/COMMIT em vez de LOCK TABLES para conseguir um aumento na velocidade. O bloqueio ir tambm diminuir o tempo total de testes de multi-conexes, mas o tempo mximo de espera para algumas threads ir aumentar (porque eles esperam pelos bloqueios). Por exemplo:
thread 1 faz 1000 inseres thread 2, 3 e 4 faz 1 insero thread 5 faz 1000 inseres

Se voc no estiver usando travas, 2, 3 e 4 iro terminar antes de 1 e 5, Se estiver utilizando travas, 2, 3 e 4 provavelmente no iro terminar antes de 1 ou 5, mas o tempo total deve ser cerca de 40% mais rpido. Como as operaes INSERT, UPDATE e DELETE so muito rpidas no MySQL, voc obter melhor perfomance geral adicionando travas em tudo que fizer mais que cerca de 5 inseres ou atualizaes em um registro. Se voc fizer vrias inseres em um registro, voc pode utilizar LOCK TABLES seguido de um UNLOCK TABLES de vez em quando (em torno de 1000 registro) para permitr que outras threads acessem a tabela. Isto tambm continua mostrando um bom ganho de performance. Com certeza, LOAD DATA INFILE muito mais rpido para carregar dados. Para obter mais velocidade para LOAD DATA INFILE e INSERT, aumente o tamanho do buffer de chaves.

Otimizao de Banco de dados

30

2.11. Performance das Consultas que Utilizam UPDATE


Consultas de atualizao so otimizadas como uma consulta que usa SELECT com a sobrecarga adicional de escrita. A velocida da escrita depende do tamanho dos dados e do nmero de ndices que sero atualizados. ndices que no forem alterados no sero atualizados. Outra forma para obter atualizaes rpidas atrasar as atualizaes e ento fazer vrias atualizaes em um registro posteriormente. Fazer vrias atualizaes em um registro muito mais rpido do que fazer uma por vez se voc travar a tabela. Perceba que, com formato de registros dinmicos, atualizar um registro para um valor maior que o tamanho total pode dividir o registro. Portanto, se voc faz isso frequentemente, muito importante usar OPTIMZE TABLE de vez em quando.

2.12. Performance das Consultas que Utilizam DELETE


Se voc deseja apagar todos os registros em uma tabela, deve usar TRUNCATE TABLE nome_tabela. O tempo para apagar um registro exatamente proporcional ao nmero de ndices. Para apagar registros mais rapidamente, voc pode aumentar o tamanho do cache de ndices.

2.13. Mais Dicas sobre Otimizaes


Dicas no ordenadas para sistemas rpidos:

Utilize conexes persistentes aos banco de dados para evitar a sobrecarga da conexo. Se voc no poder utilizar conexes persistentes e for fazer vrias novas conexes para o banco de dados, voc pode desejar alterar o valor da varivel thread_cache_size. Sempre verifique se todas as suas consultas realmente utilizam os ndices que foram criados nas tabelas. No MySQL voc pode fazer isto com o comando EXPLAIN. See Explain: (manual) Explain. Tente evitar consultas SELECT complexas em tabelas que so muito atualizadas. Isto evita problemas com travamento de tabelas. Com tabelas MyISAM que no tenham linhas deletadas, voc pode inserir registros ao mesmo tempo que outra tabela a estiver lendo. Se este recurso importante para voc, deve considerar mtodos onde voc no tem que apagar registrou ou executar OPTIMIZE TABLE depois de ter apagado vrios registros.

Otimizao de Banco de dados

31

Utilize ALTER TABLE ... ORDER BY expr1,expr2... se voc na maioria das vezes recupera registros na ordem expr1,expr2... Utilizando esta opo depois de grandes alteraes para a tabela, pode lhe dar um ganho de performance. Em alguns casos pode fazer sentido introduzir uma coluna 'hash' baseada nas informaes das outras colunas. Se esta coluna for curta e razoavelmente nica pode ser muito mais rpido do que ter um grande ndice em vrias colunas. No MySQL muito fcil usar esta coluna extra: SELECT * FROM nome_tabela WHERE hash=MD5(concat(col1,col2)) AND col_1='constante' AND col_2='constante' Para tabelas que alteram muito voc deve tentar evitar todas colunas VARCHAR ou BLOB. Voc ter tamanho de registro dinmico assim que usar um simples campo VARCHAR ou BLOB. Normalmente no muito til cortar uma tabela em diferentes tabelas apenas porque os registros esto 'grandes'. Para acessar um registro, o maior problema para a performance a busca em disco para encontra o primeiro byte do registro. Depois de encontrar os dados a maioria dos novos discos podem ler o registro inteiro rpido o bastante para a maioria das aplicaes. Os nicos caos onde realmente faz sentido dividir uma tabela se ela uma tabela de registros com tamanho dinmico (veja acima) que voc pode alterar para um tamanho fixo, ou se voc frequentemente precisa examinar a tabela e no precisa da maioria das colunas. Se frequentemente voc precisar calcular alguma coisa baseada em informao de vrios registros (ex: contagem de registros), provavlmente melhor introduzir uma nova tabela e atualizar o contador em tempo real. Uma atualizao do tipo UPDATE table set count=count+1 where index_column=constante muito rapida! Isto realmente importante quando voc usa bancos de dados como o MySQL que s tem travamento de tabelas (multiplos leituras/escrita nica). Isto tambm dar melhor performance com a maioria dos banco de dados, j que o gerenciador de bloqueio de registro ter menos a fazer neste caso.

Se voc precisar colerar estatisicas de tabelas maiores, utilize tabelas resumo em vez de buscar em toda a tabela. Manter os resumos deve ser mais rpido que tentar criar estatitscas instantaneamente. muito mais rpido criar novas tabelas atravs dos logs quando as coisas mudam (dependendo das descises de negcio) que ter que alterar a aplicao em execuo. Se possvel, deve-se classificar relatrios como 'instantneo' ou 'estatsticos' onde os dados necessrios para relatrios estaisticos so gerados apenas com base nas tabelas resumo que so geradas a partir dos dados atuais. Tire vantagem do fato de que a coluna tem valores padres. Insira valores explicitamente apenas quando os valores a serem inseridos diferem do padro. Isto reduz a analise que o MySQL precisa fazer e aumenta a velocidade de insero. Em alguns casos conveniente empacotar e armazenar os dados em um campo blob. Neste caso voc deve adicionar algum cdigo em sua aplicao para empacotar/desempacotar as coisas no campo blob, mas isto pode poupar vrios acessos a algum estgio. Isto prtico quando voc possui dados que no conformam com uma estrutura esttica de tabela.

Otimizao de Banco de dados

32

Normalmente, voc deve tentar manter todos dados no-redundantes (o que chamado de 3a forma normal na teoria de bancos de dados), mas voc no deve ter medo de duplicar alguns itens ou criar tabelas de resumo se voc precisar delas para ganhar mais velocidade. Stored Procedures ou UDF (funes definidas pelo usurios) pode ser uma boa forma para obter mais performance. Neste caso voc deve, entretanto, sempre ter uma maneira de fazer isso de outra maneira (mais lenta) se voc utilizar algum banco de dados que no suporta isto. Voc sempr pode ganhar velocidade fazendo cache de perguntas/respostas na sua aplicao e tentando fazer vrias inseres/atualizaes ao mesmo tempo. Se seu banco de dados suporta travamento de tabelas (como o MySQL e Oracle), isto deve ajudar a garantir que o cache de ndices descarregado somente uma vez depois de todas atualizaes. Use INSERT /*! DELAYED */ quando no precisar saber quando os dados so gravados. Isto melhora a velocidade porque vrios registros podem ser gravados com uma simples escrita em disco. Use INSERT /*! LOW_PRIORITY */ quando voc desejar que suas consultas sejam mais importantes. Use SELECT /*! HIGH_PRIORITY */ para obter consultas que ignoram a fila. Isto , a consulta feita mesmo se alguem estiver esperando para fazer uma escrita. Use a instruo INSERT multi-linhas para armazenar vrios registros com um comando SQL (vrios servidores SQL suportam isto). Use LOAD DATA INFILE para carregar volumes maiores de dados. Isto mais rpido que as inseres normais e mais rpido at quando o myisamchk for integrado no mysqld. Use colunas AUTO_INCREMENT para garantir valores nicos. Use OPTIMIZE TABLE de vez em quando para evitar fragmentao quando estiver usando formatos de tabela dinmica. Use tabelas HEAP para obter mais velocidade sempre que possvel. Quando estiver usando uma configurao de servidor Web normal, imagens devem ser armazenadas como arquivos. Isto , armazene apenas uma referncia para o arquivo no banco de dados. A principal razo para isto que um servidor Web normal muito melhor trabalhando com cache de arquivos do que com contedo de banco de dados. Portanto ser muito mais fcil obter um sistema rpido se voc utilizar arquivos. Use tabelas em memria para dados no-crticos que so acessados frequentemente (como informaes sobre o ltimo banner visto para usurios que no possuem cookies). Colunas com informaes identicas em diferentes tabelas devem ser declaradas idnticas e ter nomes idnticos. No entanto, antes da verso 3.23, voc pode obter ligaes mais lentas. Tente manter os nomes mais simples (use nome em vez de nome_cliente na tabela cliente). Para deixar seus nomes portveis para outros servidores SQL voc deve mant-los menores que 18 caracteres.

Se voc realmente precisa de alta velocidade, voc deve verificar as interfaces de baixo nvel para armazenagem de dados que os diferentes servidores SQL suportam! Por exemplo, para acessar tabelas MySQL MyISAM diretamente,

Otimizao de Banco de dados

33

voc pode obter um aumento de velocidade de 2-5 vezes comparado ao uso da interface SQL. Para conseguir essa faanha, os dados devem estar no mesmo servidor que sua aplicao, e normalmente devem ser acessados por apenas um processo (porque travamento de arquivos externo so muito lentos). Os problemas acima podem ser eliminados introduzindo comandos MyISAM de baixo nvel no servidor MySQL (isto pode ser a maneira mais fcil para aumentar a performance). Tenha cuidado em projetar a interface com o banco de dados, ela deve ser bem facil para suportar estes tipos de otimizaes. Em vrios casos mais rpido acessar dados de um banco de dados (utilizando uma conexo ativa) do que acessar um arquivo texto, apenas pelo fato do banco de dados ser mais compacto do que o arquivo texto (se voc estiver utilizando dados numricos), e isto ir envolver menos acessos disco. Voc tambm ir poupar cdigo porque no ser necessrio analisar seus arquivos texto para encontrar limites de registros e campos. Voc pode tambm usar replicao para conseguir ainda mais performance nas suas aplicaes. Declarando uma tabela com DELAY_KEY_WRITE=1 ir tornar a atualizao de ndices mais rpida, pois as mesmas no sero escritas em disco at o arquivo ser fechado. O lado ruim que voc deve executar myisamchk nestas tabelas antes de iniciar o mysqld para garantir que os dados esto corretos se o mysqld for finalizado no meio da execuo. Como a informao de chave pode sempre ser gerada a partir dos dados, voc no deve perder nada usando DELAY_KEY_WRITE.

Otimizao de Banco de dados

34

3. Detalhes sobre Locks


3.1. Como o MySQL Trava as Tabelas 3.2. Detalhes sobre Lock de Tabelas

3.1. Como o MySQL Trava as Tabelas


Voc pode encontrar uma discusso sobre diferentes mtodos de bloqueios no apndice. Todos os bloqueios no MySQL so livres de deadlock, exceto para tipos de tabela InnoDB e BDB. Isto gerenciado sempre requisitando todos os bloqueios necessrios de uma vez no comeo de uma consulta e sempre bloqueando as tabelas na mesma ordem. Tipos de tabela InnoDB automaticamente adquire seus locks de registro e os tipos de tabela BDB seus locks de pginas, durante o processamento das instrues SQL, e no no incio da transao. O mtodo de bloqueio que o MySQL utiliza para ESCRITA funciona da seguinte forma:

Se no existirem travas na tabela, coloque uma bloqueio de escrita na mesma. Caso contrrio, coloca a requisio de trava na fila de bloqueios para escrita.

O mtodo de bloqueio que o MySQL utilizado para LEITURA funciona da seguinte maneira:

Se no existirem tarvas na tabela, coloca um bloqueio de leitura na mesma. Caso contrrio, coloca a requisio de trava na fila de bloqueios para leitura.

Quando um bloqueio liberado, a trava fica disponvel para as threads na fila de bloqueios de escrita, e ento para as threads na fila de bloqueios de leitura. Isto significa que se voc possui vrias atualizaes em uma tabela, instrues SELECT iro esperar at que no existam mais atualizaes. Para contornar este problema no caso onde voc precisa fazer vrias operaes de INSERT e SELECT em uma tabela, voc pode inserir registros em uma tabela temporria e atualizar a tabela real com os registros da tabela temporria de uma s vez. Isto pode ser feito usando o cdigo a seguir:
mysql> mysql> mysql> mysql>
LOCK TABLES real_table WRITE, insert_table WRITE; INSERT INTO real_table SELECT * FROM insert_table; TRUNCATE TABLE insert_table; UNLOCK TABLES;

Otimizao de Banco de dados

35

Voc pode utilizar as opes LOW_PRIORITY com INSERT, UPDATE ou DELETE ou HIGH_PRIORITY com SELECT se voc desejar priorizar a recuperao em alguns casos especficos. Tambm podei-se iniciar o mysqld com -low-priority-updates para obter o mesmo comportamento. Utilizar SQL_BUFFER_RESULT pode tambm tornar a criao de locks de tabelas mais curtos. Voc tambm pode alterar o cdigo de bloqueioss no mysys/thr_lock.c para usar uma fila simples. Neste caso, bloqueios de escrita e leitura devem ter a mesma prioridade, o que pode ajudar em algumas aplicaes.

3.2. Detalhes sobre Lock de Tabelas


O cdigo de bloqueio de tabelas no MySQL livre de deadlock. O MySQL utiliza bloqueio de tabelas (no lugar de bloqueio de registros ou colnas) em todos os tipos de tabelas, exceto tabelas BDB, para obter uma alta velocidade nos bloqueios. Para grandes tabelas, bloqueio de tabelas MUITO melhor que bloqueio de registros para a maioria das aplicaes, mas existem, claro, algumas desvantagens. Para tabelas BDB e InnoDB, O MySQL s utiliza bloqueio de tabelas se voc bloquear explicitamente a tabela com LOCK TABLES ou executar um comando quer ir modificar todos os registros na tabela, como ALTER TABLE. Para estes tipos de tabelas ns recomendamos a voc no utilizar LOCK TABLES. No MySQL verso 3.23.7 ou superior , voc pode inserir registros em tabelas MyISAM ao mesmo tempo que outras threads esto lendo da mesma tabela. Perceba que atualmente isto funciona somente se no existirem buracos depois de registros apagados na tabela no momento que a insero feita. Quando todos os buracos forem preenchidos com novos dados, inseres concorrentes iro automaticamente ser habilitadas novamente. O bloqueio de tabelas habilita vrias threads para lerem de uma tabela ao mesmo tempo, mas se uma thread desejar escrever a uma tabela, ela primeiramente deve obter acesso exclusivo. Durante a atualizao, todas outras threads que desejarem acessar esta tabela em particular iro esperar at que a atualizao acabe. Como atualizaes em tabelas normalmente so consideradas mais importantes que SELECT, todas as instrues que atualizam uma tabela tem maior prioridade que instrues que simplesmente recuperam informaes. Isto deve garantir que atualizaes no fiquem na fila por terem sido passadas vrias consultas pesadas em uma tabela especfica. (Voc pode alterar isto utilizando LOW_PRIORITY com a instruo que faz a atualizao ou HIGH_PRIORITY com a instruo SELECT.)

Otimizao de Banco de dados

36

A partir do MySQL verso 3.23.7 pode-se utilizadar a varivel max_write_lock_count para forar o MySQL a fornecer temporariamente a todas as instrues SELECT, que esperam por uma tabela, uma prioridade mais alta depois de um nmero especfico de inseres em uma tabela. O bloqueio de tabela no , no entanto, muito bom sobre os seguintes cenrios:

Um cliente emite uma SELECT que exige muito tempo para ser executada. Outro cliente ento executa um UPDATE na tabela usada. Este cliente ter que esperar at que a SELECT seja terminada. Outro cliente executa outra instruo SELECT na mesma tabela. Como UPDATE tem maior prioridade que SELECT, esta SELECT ir esperar pelo trmino da UPDATE. Ela tambm ir esperar pelo trmino da primeira SELECT! Uma thread est esperando por algo do tipo disco cheio, caso em que todas as threads que desejam acessar a tabela com problema iro ser colocadas em estado de espera at que mais espao em disco seja disponvel.

Algumas solues possveis para este problema so:


Tente deixar suas instrues SELECT sempre rpidas. Voc pode ter que criar algumas tabelas de resumo para fazer isto. Inicie o mysqld com --low-priority-updates. Isto ir fornecer a todas instrues que atualizam (modificam) uma tabela prioridade menor que uma instruo SELECT. Neste caso a ltima instruo SELECT no cenrio anterior deveria executar antes da instruo INSERT. Voc pode fornecer a uma instruo INSERT, UPDATE ou DELETE especfica menor prioridade com o atributo LOW_PRIORITY.

Inicie o mysqld com um valor baixo para max_write_lock_count para fornecer bloqueios de LEITURA depois de um certo nmero de bloqueios de ESCRITA. Voc pode especificar que todas as atualizaes de uma thread especfica deve ser feita utilizando prioridade baixa com o comando SQL: SET SQL_LOW_PRIORITY_UPDATES=1. Voc pode especificar que uma SELECT especfica muito importante com o atributo HIGH_PRIORITY. Se voc tiver problemas com INSERT combinado com SELECT, utilize as novas tabelas MyISAM, pois elas suportam SELECTs e INSERTs concorrentes. Se voc utiliza principalmente instrues INSERT e SELECT misturadas, o atributo DELAYED no INSERT provavelmente ir resolver seus problemas. Se voc tiver problemas com SELECT e DELETE, a opo LIMIT para DELETE pode ajudar.

Otimizao de Banco de dados

37

4. Otimizando a Estrutura de Banco de Dados


4.1. Opes do Projeto 4.2. Deixando os Dados com o Menor Tamanho Possvel 4.3. Como o MySQL Utiliza ndices 4.4. ndices de Colunas 4.5. ndices de Mltiplas Colunas 4.6. Como o MySQL Conta as Tabelas Abertas 4.7. Como o MySQL Abre e Fecha as Tabelas 4.8. Desvantagem em Criar um Nmero Grande de Tabelas no Mesmo Banco de Dados

4.1. Opes do Projeto


O MySQL mantem dados de registros e ndices em arquivos separados. Vrios (quase todos) bancos de dados misturam dados de registros e ndice no mesmo arquivo. Ns acreditamos que a escolha do MySQL melhor para uma ampla escala de sistemas modernos. Outra forma de armazenar os dados de registros manter a informao para cada coluna em uma rea separada (exemplos so o SDBM e o Focus). Isto ir causar um ponto de performance para toda consulta que acessar mais de uma coluna. Como isto degrada rapidamente quando mais de uma coluna acessada, acreditamos que este modelo no bom para propsitos gerais de bancos de dados. O caso mais comum aquele em que o ndice e dados so armazenados juntos (como no Oracle/Sybase). Neste caso voc ir encontrar a informao do registro na folha da pgina de ndice. A coisa boa com este layout que ele, em vrios casos, dependendo de como o ndice armazenado no cache, salva uma leitura de disco. As desvantagens deste layout so:

A varredura da tabela muito mais lenta porque voc tem que ler os ndices para encontrar os dados. No podem ser usados apenas a tabela de ndices para recuperar dados para uma consulta. Voc perde muito espao de armazenagem, j que que os ndices devem ser duplicados nos ns (pois os registros no podem ser armazenados nos ns). Delees iro degenerar a tabela depois de um tempo (j que os ndices nos ns normalmente no so atualizados na deleo). mais difcil fazer o cache somente dos dados de ndices.

4.2. Deixando os Dados com o Menor Tamanho Possvel


Uma das otimizaes mais bsicas tentar manter seus dados (e ndices) utilizando o menor espao possvel no disco (e em memria). Isto pode fornecer grandes

Otimizao de Banco de dados

38

melhorias porque a leitura de disco mais rpida e normalmente menos memria principal ser usada. A indexao tambm exige menos recursos se for feita em colunas menores. O MySQL suporta vrios diferentes tipos de tabelas e formatos de registros. Voc pode ter um timo ganho de performance escolhendo o formato certo de tabela a ser usada. Pode-se obter melhor performance em uma tabela e minimizar espao de armazenagem utilizando as tcnicas listadas abaixo:

Utilize os tipos mais eficientes (menores) sempre que possvel. O MySQL tem vrios tipos especializados que economizam espao em disco e memria. Utilize tipos inteiros menores se possvel para obter tabelas menores. Por exemplo, MEDIUMINT normalmente melhor que INT. Declare colunas para serem NOT NULL se possvel. Isto deixa tudo mais rpido e voc economiza um bit por coluna. Perceba que se voc realmente precisa de NULL nas suas aplicaes, podem ser usados. Tente simplesmente no us-la em todas as colunas por padro. Se voc no possui nenhuma coluna de tamanho varivel (VARCHAR, TEXT ou BLOB), um formato de registro de tamanho fixo para utilizado. Isto mais rpido mas infelizmente pode ocupar mais espao. O ndice primrio de uma tabela deve ser o mais curto possvel. Isto torna a identificao de um registro fcil e eficiente. Para cada tabela, voc deve decidir qual metdo de armazenamento/ndice utilizar. Crie somente os ndices necessrios. ndices so bons para recuperao mas ruins quando voc precisa armazenar os dados rapidamente. Se na maioria das vezes voc acessa uma tabela pesquisando em uma combinao de colunas, crie um ndice para elas. A primeira parte do ndice deve ser a coluna mais utilizada. Se voc SEMPRE utiliza vrias colunas, deve usar a coluna com mais duplicaes em primeiro lugar para obter melhor compactao do ndice. Se for melhor que uma coluna tenha um prefixo nico nos primeiros caracteres, melhor indexar somente este prefixo. O MySQL suporta um ndice em uma parte de uma coluna de caracteres. ndices menores so mais rpidos no somente porque eles exigem menos espao em disco mas tambm porque eles iro fornecer a voc mais acerto no cache de ndice e isto diminui acessos a disco. Em algumas circunstncias pode ser benfico dividir uma tabela que varrida frequentemente em duas. Isto verdade especificamente se a tabela tiver um formato dinmico e for possvel utilizar um formato de tabela esttico que possa ser usada para encontrar os registros relevantes quando se fizer uma varredura da tabela.

Otimizao de Banco de dados

39

4.3. Como o MySQL Utiliza ndices


Os ndices so utilizados para encontrar registros com um valor especfico de uma coluna rapidamente. Sem um ndice o MySQL tem de iniciar com o primeiro registro e depois ler atravs de toda a tabela at que ele encontre os registros relevantes. Quanto maior a tabela, maior ser o custo. Se a tabela possui um ndice para as colunas em questo, o MySQL pode rapidamente obter uma posio para procurar no meio do arquivo de dados sem ter que varrer todos os registros. Se uma tabela possui 1000 registros, isto pelo menos 100 vezes mais rpido do que ler todos os registros sequencialmente. Note que se voc precisar acessar quase todos os 1000 registros, seria mais rpido acess-los sequencialmente porque evitaria acessos ao disco. Todos os ndices do MySQL (PRIMARY, UNIQUE e INDEX) so armazenados em rvores B. Strings so automaticamente compactadas nos espaos finais e prefixados. ndices so utilizados nos seguintes modos:

Para encontrar rapidamente os registros que coincidam com uma clusula WHERE. Para recuperar registros de outras tabelas ao realizar joins. Para encontrar o valor MAX() ou MIN() para uma coluna indexada espeifica. Isto otimizado por um preprocessador que confere se voc est utilizando WHERE key_part_#=constante em todas as partes da chave < N. Neste caso o MySQL ir fazer uma simples procura na chave e trocar a expresso MIN() com uma constante. Se todas as expresses forem trocadas por constantes, a consulta retornar imediatamente:

SELECT MIN(key_part2),MAX(key_part2) FROM nome_tabela where key_part1=10

Para ordenar ou agrupar uma tabela se a ordenao ou agrupamento for feito em um prefixo mais esquerda de uma chave util (por exemplo, ORDER BY key_part_1, key_part_2 ). A chave lida na ordem invertida se todas as partes da chave forem seguidas por DESC. Em alguns casos uma consulta pode ser otimizada para recuperar valores sem consultar o arquivo de dados. Se todas colunas utilizadas para alguma tabela so numricas e formam um prefixo mais esquerda para alguma chave, os valores podem ser recuperados da rvore de ndices para aumentar a velocidade:

SELECT key_part3 FROM nome_tabela WHERE key_part1=1

Suponha que voc utilize a seguinte instruo SELECT:


mysql> SELECT * FROM nome_tabela WHERE col1=val1 AND col2=val2;

Se um ndice de colunas mltiplas existir em col1 e col2, os registros apropriados podem ser recuperados diretamente. Se ndices separados de nicas colunas existirem em col1 e col2, o otimizador tentar encontrar o ndice mais restritivo

Otimizao de Banco de dados

40

decidindo qual ndice ir encontrar menos registros e usar este ndice para recuperar os registros. Se a tabela possuir um ndice de mltiplas colunas, qualquer prefixo mais esquerda do ndice pode ser usado pelo otimizador para encontrar registros. Por exemplo, se voc possui um ndice de trs colunas em (col1, col2, col3), voc tem capacidades de busca indexada em (col1), (col1, col2) e (col1, col2, col3). O MySQL no pode utilizar um ndice parcial se as colunas no formarem um prefixo mais esquerda do ndice. Suponha que voc tenha as instrues SELECT mostradas abaixo:
mysql> SELECT * FROM nome_tabela WHERE col1=val1; mysql> SELECT * FROM nome_tabela WHERE col2=val2; mysql> SELECT * FROM nome_tabela WHERE col2=val2 AND col3=val3;

Se um ndice existir em (col1, col2, col3), somente a primeira consulta anteriores utiliza o ndice. A segunda e terceira consultas involvem colunas indexadas, mas (col2) e (col2, col3) no so os prefixos mais esquerda de (col1, col2, col3). O MySQL tambm utiliza ndices para comparaes do tipo LIKE se o argumento para LIKE for uma string constante que no inicie com um meta caracter Por exemplo as seguintes instrues SELECT utilizam ndices:
mysql> SELECT * FROM nome_tbl WHERE key_col LIKE "Patrick%"; mysql> SELECT * FROM nome_tbl WHERE key_col LIKE "Pat%_ck%";

Na primeira instruo, somente os registros com "Patrick" <= key_col < "Patricl" so considerados. Na segunda instruo, somente registros com "Pat" <= key_col < "Pau" so considerados. As seguintes instrues SELECT no usaro ndices:
mysql> SELECT * FROM nome_tbl WHERE key_col LIKE "%Patrick%"; mysql> SELECT * FROM nome_tbl WHERE key_col LIKE other_col;

Na primeira instruo, o valor LIKE inicia com um meta caracter. Na segunda instruo, o valor LIKE no uma constante. O MySQL 4.0 faz outra otimizao em LIKE. Se voc usar ... LIKE "%string%" e string tiver mais de 3 caracteres, o MySQL usar o algortmo Turbo Boyer-Moore para inicializar o padro para a string e ento usar este padro para realizar a pesquisa mais rpido. Buscas usando nome_coluna IS NULL usa ndices se nome_coluna um ndice. O MySQL normalmente utiliza o ndice que encontra o menor nmero de registros. Um ndice usado para colunas que voc compara com os seguintes operadores: =, >, >=, <, <=, BETWEEN ou um LIKE com um padro que comea com um prefixo sem meta caracteres como 'algo%'.

Otimizao de Banco de dados

41

Qualquer ndice que no cobrem todos os nveis de AND na clusula WHERE no utilizado para otimizar a consulta. Em outras palavras: Para poder usar um ndice, um prefixo do ndice deve ser utilizado em todo agrupamento AND. A seguinte clusula WHERE utilizar ndices:
... WHERE index_part1=1 AND index_part2=2 AND other_column=3 ... WHERE index=1 OR A=10 AND index=2 /* index = 1 OR index = 2 */ ... WHERE index_part1='hello' AND index_part_3=5 /* optimised like "index_part1='hello'" */ ... WHERE index1=1 AND index2=2 OR index1=3 AND index3=3; /* Can use index on index1 but not on index2 or index 3 */

Estas clusulas WHERE no utilizam ndices:

... WHERE index_part2=1 AND index_part3=2 */ ... WHERE index=1 OR A=10 */ ... WHERE index_part1=1 OR index_part2=10 */

/* index_part_1 is not used /* Index is not used in both AND parts /* No index spans all rows

Perceba que algumas vezes o MySQL no utilizar um ndice, mesmo se algum estiver disponvel. Um exemplo deste caso quando o uso do ndice necessita que o MySQL acesse mais de 30% dos registros na tabela. (Neste caso uma varredura da tabela provavelmente mais rpido, j que ela necessitar de menos pesquisas em discos). No entanto, se uma consulta utiliza LIMIT para recuperar somente parte dos registros, o MySQL ir utilizar um ndice de qualquer forma, pois assim pode encontrar os poucos registros mais rapidamente e retornar o resultado.

4.4. ndices de Colunas


Todos os tipos de colunas do MySQL podem ser indexadas. O uso de ndices nas colunas relevantes a melhor forma de melhorar a performance de operaes SELECT. O nmero mximo de ndices por tabelas e o tamanho mximo de um ndice definido pelo mecanismo de armazenamento. Todos os mecanismos de armazenamentos suportam um mnimo de 16 chaves por tabela e um ndice de tamanho total mnimo de 256 bytes. Para colunas CHAR e VARCHAR voc pode indexar um prefixo da coluna. Isto muito mais rpido e necessita de menos espao em disco do que indexar a coluna

Otimizao de Banco de dados

42

inteira. A sintaxe para utilizar na instruo CREATE TABLE para indexar um prefixo de uma coluna se parece com o exemplo a seguir:
INDEX nome_indice (nome_campo(tamanho))

O exemplo abaixo cria um ndice para os primeiros 10 caracteres da coluna nome:


mysql> CREATE TABLE teste ( nome CHAR(200) NOT NULL, INDEX nome_indice (nome(10)));

Para colunas BLOB e TEXT, voc deve indexar um prefixo da coluna. O ndice pode ter at 255 bytes. No MySQL Verso 3.23.23 ou posterior, voc pode tambm criar ndices FULLTEXT especiais. Eles so utilizados para pesquisas textuais. Somente o tipo de tabela MyISAM suporta ndices FULLTEXT e apenas para colunas CHAR, VARCHAR e TEXT. Indexao sempre acontece sobre toda a coluna e indexao parcial (prefixo) no suportada.

4.5. ndices de Mltiplas Colunas


O MySQL pode criar ndices em mltiplas colunas. Um ndice pode consistir de at 15 colunas. (Em colunas CHAR e VARCHAR voc tambm pode utilizar um prefixo da coluna como parte de um ndice). Um ndice de mltiplas colunas pode ser considerado um array ordenado contendo valores que so criados concatenando valores de colunas indexadas. O MySQL utiliza ndices de mltiplas colunas de forma que consultas so rpidas quando voc especifica uma quantidade conhecida para a primeira coluna do ndice em uma clusula WHERE, mesmo se voc no especificar valores para as outras colunas. Suponha que uma tabela tenha a seguinte especificao:
mysql> 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));

Ento o ndice nome um ndice com ultimo_nome e primeiro_nome. O ndice ser usado para consultas que especificarem valores em um limite conhecido para ultimo_nome, ou para ambos ultimo_nome e primeiro_nome. Desta forma, o ndice nome ser usado nas seguintes consultas:
mysql> SELECT * FROM teste WHERE ultimo_nome="Widenius";

Otimizao de Banco de dados

43

mysql> SELECT * FROM teste WHERE ultimo_nome="Widenius" AND primeiro_nome="Michael"; mysql> SELECT * FROM teste WHERE ultimo_nome="Widenius" AND (primeiro_nome="Michael" OR primeiro_nome="Monty"); mysql> SELECT * FROM teste WHERE ultimo_nome="Widenius" AND primeiro_nome >="M" AND primeiro_nome < "N";

Entretanto, o ndice nome no ser usado nas seguintes consultas:


mysql> SELECT * FROM teste WHERE primeiro_nome="Michael"; mysql> SELECT * FROM teste WHERE ultimo_nome="Widenius" OR primeiro_nome="Michael";

4.6. Como o MySQL Conta as Tabelas Abertas


Ao executar o comando mysqladmin status, voc ver algo deste tipo:
Uptime: 426 Running threads: 1 Questions: 11082 Reloads: 1 Open tables: 12

O valor Open tables de 12 ode ser bastante estranho se voc s possui 6 tabelas. O MySQL multithreaded, portanto ele pode haver clientes enviando consultas para uma determinada tabela simultaneamente. Para minimizar o problema com dois clientes tendo diferentes estados no mesmo arquivo, a tabela aberta independentemente por cada thread concorrente. Isto exige mais memria mas normalmente aumentar o desempenho. Com tabelas ISAM e MyISAM, um descritor extra de arquivo necessrio para o arquivo de dados, para cada cliente que tem a tabela aberta. O descritor de arquivo de ndice compartilhado entre todas as threads.

4.7. Como o MySQL Abre e Fecha as Tabelas


As variveis do servidor table_cache, max_connections e max_tmp_tables afetam o nmero mximo de arquivos que o servidor mantm abertos. Se voc aumentar um ou ambos destes valores, voc pode ir contra um limite imposto pelo seu sistema operacional no nmero de arquivos abertos por processo. Voc pode aumentar o limite de arquivos abertos em muitos sistemas operacionais, embora o mtodo varia muito de um sistema para outro. Consulte a documentao de seu Sistema Operacional para saber como faz-lo, porque o mtodo para alterar o limite varia muito de um sistema para outro.

Otimizao de Banco de dados

44

table_cache relacionado a max_connections. Por exemplo, para 200 conexes concorrentes em execuo, voc deve ter um tamanho de cache de tabela de pelo menos 200 * n, onde n o nmero mximo de tabelas em um join. Voc tambm precisa reservar alguns descritores de arquivos para tabelas e arquivos temporrios. Esteja certo de que o seu sistema operacional pode tratar o nmero de descritores de arquivos abertos definido pelo valor de table_cache. Se table_cache for muito alto, o MySQL pode esgotar os descritores de arquivo e recusar conexes, falhar na execuo de consultas e ser muito instavel. Voc tambm tm que levar em conta que o mecanismo de armazenamento MyISAM precisa de dois descritores de arquivos para cada tabela aberta. Voc pode aumentar o nmero de descritores de arquivo disponveis para o MySQL com a opo de inicializao --open-files-limit. A cache de tabelas abertas ser mantido em um nvel de table_cache entradas. O valor padro 64; isto pode ser alterado com a opo -O table_cache=# do mysqld. Note que o MySQL pode temporariamente abrir mais tabelas para poder se executar consultas. Um tabela no usada fechada e removida da cache de tabelas sob as seguintes circuntncias:

Quando a cache est cheia e um thread tenta abrir uma tabela que no est na cache. Quando a cache contm mais que table_cache entradas e uma thread no est mais usando uma tabela. Quando algum executa mysqladmin refresh ou mysqladmin flush-tables. Quando algum executa uma instruo FLUSH TABLES.

Quando o cache de tabela encher, o servidor usa o seguinte procedimento para encontrar uma entrada de cache para usar:

Tabelas que no estiverem em uso so liberadas, na ordem LRU (least-recentlyused), ou seja, a tabela que foi usada menos rcentemente. Se o cache estiver cheio e nenhuma tabelas pode ser liberada, mas uma nova tabela precisar ser aberta, o cache extendido temporariamente quando necessrio. Se o cache estiver no estado temporariamente extendido e uma tabela vai do estado em-uso para o fora-de-uso, a tabela fechada e liberada do cache.

A table is opened for each concurrent access. This means the table needs to be opened twice if two threads access the same table or if a thread accesses the table twice in the same query (for example, by joining the table to itself). Uma tabela aberta para cada acesso simultneo. Isto significa a tabela precisa ser aberta duas vezes se duas threads acessam a mesma tabela ou se uma thread acessa a tabela duas vezes na mesma consulta (por exemplo, fazendo um join da tabela com ela mesma). A primeira abertura de qualquer tabela exige dois descritores de arquivos; cada uso adicional da tabela exige somente um descritor. O descritor extra para a primeira abertura para o arquivo de ndice: este descritor compartilhado entre todas as threads.

Otimizao de Banco de dados

45

Se voc est abrindo uma tabela com a instruo HANDLER nome_tabela OPEN, uma tabela dedicada alocada para a thread. Este objeto da tabela no compartilhado por outras threads e no ser fechado at que a thread chame HANDLER nome_tabela CLOSE ou seja finalizada. Quando isto acontece, a tabela colocada de volta na cache de tabela (se a cache no estiver cheia). Voc pode conferir se o seu cache de tabela est muito pequeno conferindo a varivel opened_tables do mysqld. Se este valor for muito grande, mesmo se voc no fez vrios FLUSH TABLES, voc deve aumentar o tamanho da sua cache de tabelas.

4.8. Desvantagem em Criar um Nmero Grande de Tabelas no Mesmo Banco de Dados


Se voc possui muitos arquivos em um diretrio, operaes de abrir, fechar e criao ficaro lentos. Se voc executar instrues SELECT em diversas tabelas, existir uma pequena sobrecarga quando o cache de tabela estiver cheio, porque para toda tabela que teve que ser aberta, outra deve ser fechada. Voc pode reduzir esta sobrecarga tornando o cache de tabelas maior.

Otimizao de Banco de dados

46

5. Otimizando o Servidor MySQL


5.1. Sintonia dos Parmetros em Tempo de Sistema/Compilao e na Inicializao 5.2. Parmetros de Sintonia do Servidor 5.3. Como a Compilao e a Ligao Afetam a Velocidade do MySQL 5.4. Como o MySQL Utiliza a Memria 5.5. Como o MySQL Utiliza o DNS 5.6. Sintaxe de SET

5.1. Sintonia dos Parmetros em Tempo de Sistema/Compilao e na Inicializao


Ns iniciamos com o fator do nvel do sistema pois algumas destas decises devem ser feitas bem cedo. Em outros casos uma rpida olhada para esta seo pode satisfazer porque ela no to importante para os grandes ganhos. Entretanto, sempre bom ter ter noes de como voc pode obter melhorias alterando coisas neste nvel. Qual sistema operacional a usar realmente importante! Para obter o melhor uso de mquinas com mltiplas CPUs voc deve utilizar Solaris (porque a sua implemetao das threads funcionam muito bem) ou Linux (porque o kernel 2.2 tem suporte SMP muito bom). Tambm, em Linux mais antigos temos o limite de tamanho de arquivo de 2G por padro. Se voc tem tal kernel e precisa desesperadamente de trabalhar com arquivos maiores que 2G em mquinas intel Linux, voc deve obter o patch LFS para o sistema de arquivos ext2. Outros sistemas de arquivo como ReiserFS e XFS no possuem esta limitao de 2G. Como ainda no temos o MySQL em produo em muitas outras plataformas, ns aconselhamos que voc teste a plataforma pretendida antes de escolhe-la, se possvel. Outras dicas:

Se voc possui RAM suficiente, voc pode remover todos os dispositivos de troca. Alguns sistemas operacionais iro utilizar um disposotico de troca em alguns contextos, mesmo se voc possuir memria livre. Utilize a opo do MySQL --skip-external-locking para evitar locks externos. Perceba que isto no ir afetar a funcionalidade do MySQL se voc estiver executando um nico servidor. Apenas lembre-se de desligar o servidor (ou travar as partes relevantes) antes de executar myisamchk. Em alguns sistemas esta opo obrigatrio porque o lock externo no funcionam em nenhum caso. A opo --skip-external-locking est ligada por padro a partir do MySQL 4.0. Antes disto, era ligada por padro quando compilando com MIT-pthreads, porque flock() no totalmente suportado pelas MIT-pthreads em todas plataformas. tambm o padro para Linux pois o bloqueio de arquivos no Linux no muito seguro.

Otimizao de Banco de dados

47

O nico caso que voc no pode utilizar --skip-external-locking se voc precisa de vrios servidores MySQL (no clientes) acessando os mesmos dados, ou executar myisamchk na tabela sem dizer ao servidor para descarregar e travar as tabelas primeiro Voc pode continuar usando LOCK TABLES/UNLOCK TABLES mesmo se voc estiver utilizando --skip-external-locking.

5.2. Parmetros de Sintonia do Servidor


Voc pode determinar tamanho padro do buffer usados pelo servidor mysqld com este comando:
shell> mysqld --help

Este comando produz uma lista de todas as opes do mysqld e variveis configurveis. A sada inclui os valores padro das variveis e se parece com isto:
back_log bdb_cache_size binlog_cache_size connect_timeout delayed_insert_timeout delayed_insert_limit delayed_queue_size flush_time interactive_timeout join_buffer_size key_buffer_size lower_case_nome_tabelas long_query_time max_allowed_packet max_binlog_cache_size max_connections max_connect_errors max_delayed_threads max_heap_table_size max_join_size max_sort_length max_tmp_tables max_write_lock_count myisam_sort_buffer_size net_buffer_length net_retry_count net_read_timeout net_write_timeout read_buffer_size record_rnd_buffer_size slow_launch_time sort_buffer table_cache thread_concurrency tmp_table_size thread_stack current value: 5 current value: 1048540 current value: 32768 current value: 5 current value: 300 current value: 100 current value: 1000 current value: 0 current value: 28800 current value: 131072 current value: 1048540 current value: 0 current value: 10 current value: 1048576 current value: 4294967295 current value: 100 current value: 10 current value: 20 current value: 16777216 current value: 4294967295 current value: 1024 current value: 32 current value: 4294967295 current value: 8388608 current value: 16384 current value: 10 current value: 30 current value: 60 current value: 131072 current value: 262144 current value: 2 current value: 2097116 current value: 64 current value: 10 current value: 1048576 current value: 131072

Otimizao de Banco de dados

48

wait_timeout

current value: 28800

Se existir um servidor mysqld em execuo, voc pode ver quais valores ele est usando atualmente para as variveis executando esta instruo:
mysql> SHOW VARIABLES;

Voc tambm pode ver algumas estatsticas e indicadores de status para um servidor em execuo executando este comando:
mysql> SHOW STATUS;

Para encontrar uma descrio completa de todas as variveis na seo SHOW VARIABLES neste manual. Variveis de servidor e informao de status tambm pode ser obtido usando mysqladmin:
shell> mysqladmin variables shell> mysqladmin extended-status

O MySQL utiliza algortmos que so muito escalveis, portanto, normalmente voc pode trabalhar com pouca memria. Entretanto, se voc fornecer ao MySQL mais memria, obter um desempenho melhor. Quando estiver ajustando um servidor MySQL, as duas variveis mais importantes que devem ser usadas so key_buffer_size e table_cache. Voc deve se sentir confiante que as duas estejam corretas antes de tentar alterar qualquer outra varivel. Os seguintes exemplos indicam alguns valores tpicos de variveis para diferentes configuraes de tempo de execuo. Os exemplos usam o script mysqld_safe e usam a sintaxe --name=value para definir a varivel name com o valor value. Esta sintaxe est disponvel a partir do MySQL 4.0. Para verses mais antigas do MySQL, tome as seguintes diferenas nas contas:

Use safe_mysqld em vez de mysqld_safe. Configure as variveis usando a sintaxe --set-variable=name=value ou -O name=value Para nomes de variveis que finalizam em _size, voc pode precisar especificlas sem _size. Por exemplo, o nome antigo para sort_buffer_size sort_buffer. O nome antigo para read_buffer_size record_buffer. Para ver quais variveis a verso do seu servidor reconhece, use mysqld --help.

Se voc possui pelo menos 256M de memria e vrias tabelas e deseja obter o melhor desempenho com um nmero moderado de clientes, deve utilizar algo como:
shell> mysqld_safe --key_buffer_size=64M --table_cache=256 \ --sort_buffer_size=4M --read_buffer_size=1M &

Otimizao de Banco de dados

49

Se possui apenas 128M de memria e apenas algumas poucas tabelas, mas ainda deseja realizar vrias ordenaes, voc pode utilizar:
shell> mysqld_safe --key_buffer_size=16M --sort_buffer_size=1M

Se voc possuir pouca memria e tiver muitas conexes, utilize algo como:
shell> mysqld_safe --key_buffer_size=512K --sort_buffer_size=100K \ --read_buffer_size=100K &

ou mesmo isto:
shell> mysqld_safe --key_buffer_size=512K --sort_buffer_size=16K \ --table_cache=32 --read_buffer_size=8K -O net_buffer_length=1K &

Se voc estiver executando um GROUP BY ou ORDER BY em tabelas que so muito maiores que sua memria disponvel voc deve aumentar o valor de record_rnd_buffer_size para acelerar a leitura de registros aps a operao de ordenao. Quando voc tiver instalado o MySQL, o diretrio support-files ir conter alguns arquivos exemplos do my.cnf, my-huge.cnf, my-large.cnf, my-medium.cnf e mysmall.cnf, voc pode us-los como base para otimizar seu sistema. Se voc possui vrias conexes simultneas, ``problemas de trocas'' podem ocorrer a menos que o mysqld tenha sido configurado para usar muito pouca memria para cada conexo. O mysqld tem melhor performance se voc tiver memria suficiente para todas as conexes, claro. Perceba que se voc especifica uma opo na linha de comando para o mysqld, ou mysqld_safe ele permanece em efeito somente para aquela chamada do servidor. Para usar a opo toda vez que o servidor executa, coloque-o em um arquivo de opo. Para ver os efeitos de uma alterao de parmetro, faa algo como:
shell> mysqld --key_buffer_size=32m --help

Tenha certeza que a opo --help seja a ltima do comando; de outra forma o efeito de qualquer opes listadas depois na linha de comando no sero refletidas na sada.

5.3. Como a Compilao e a Ligao Afetam a Velocidade do MySQL


A maioria dos testes seguintes so feitos no Linux com os benchmarks do MySQL, mas eles devem fornecer alguma indicao para outros sistemas operacionais e workloads.

Otimizao de Banco de dados

50

Voc obtm um executvel mais veloz quando ligado com -static. No Linux, voc ir obter o cdigo mais rpido quando compilando com pgcc e 03. Para compilar sql_yacc.cc com estas opes, voc precisa de cerca de 200M de memria porque o gcc/pgcc precisa de muita memria para criar todas as funes em linha. Tambm deve ser configurado o parmetro CXX=gcc para evitar que a biblioteca libstdc++ seja incluida (no necessria). Perceba que com algumas verses do pgcc, o cdigo resultante ir executar somente em verdadeiros processadores Pentium, mesmo que voc utilize a opo do compilador para o cdigo resultante que voc quer, funcionando em todos os processadores do tipo x586 (como AMD). S pelo fato de utilizar um melhor compilador e/ou melhores opes do compilador voc pode obter um aumento de desempenho de 10-30% na sua aplicao. Isto particularmente importante se voc mesmo compila o servidor SQL! Ns testamos ambos os compiladores Cygnus Codefusion e o Fujitsu, mas quando os testamos, nenhum dos dois era suficientemente livre de erros para que o MySQL compilasse com as otimizaes. Quando voc compila o MySQL deve incluir suporte somente para os conjuntos de caracteres que deseja usar. (Opo --with-charset=xxx). As distribuies binrias padro do MySQL so compiladas com suporte para todos os conjuntos de caracteres. Segue uma lista de algumas medidas que temos feito:

Se voc utiliza o pgcc e compila tudo com -O6, o servidor mysqld 1% mais rpido do que com o gcc 2.95.2. Se voc liga dinamicamente (sem -static), o resultado 13% mais lento no Linux. Note que voc ainda pode utilizar uma biblioteca do MySQL dinamicamente ligada sua aplicao cliente. s o servidor que crtico para performance. Se voc corta seu binrio mysqld com strip libexec/mysqld, o binrio gerado pode ficar at 4% mais rpido. Para uma conexo de um cliente para um servidor em execuo na mesma mquina, se voc conecta utilizando TCP/IP em vez de utilizar um arquivo socket Unix, o rendimento 7.5% mais lento no mesmo computador. (Se voc fizer conexo localhost, o MySQL ir, por padro, utilizar sockets). Para conexes TCP/IP de um cliente para um servidor, conectando a um servidor remoto em outra mquina ser 8-11% mais lento que conectando ao servidor local na mesma mquina, mesmo para conexes Ethernet de 100M. Quando executar o nosso teste de benchamrk usando conexes seguras (todos os dados crptografados com suporte interno SSL) ele se torna 55% mais lento. Se voc compilar com --with-debug=full, a maioria das consultas ser 20% mais lentas. Algumas consultas podem demorar muito mais tempo (por exemplo, os benchmarks do MySQL demonstram 35% de perda). Se utilizar --with-debug, a queda ser de apenas 15%. Para uma verso do mysqld compilada com --withdebug=full, voc pode desabilitar a verificao de memria em tempo de execuo iniciando-o com a opo --skip-safemalloc. O resultado final neste caso deve estar prximo de quando compilado com --with-debug. Em um Sun UltraSPARC-IIe, Forte 5.0 4% mais rpido que gcc 3.2.

Otimizao de Banco de dados

51

Em um Sun UltraSPARC-IIe, Forte 5.0 4% mais rpido em modo de 32 bits que em modo de 64 bits. Compilando com gcc 2.95.2 para o ultrasparc com a opo -mcpu=v8 -Wa,xarch=v8plusa melhora a performance em 4%. No Solaris 2.5.1, a MIT-pthreads 8-12% mais lenta do que as threads nativas do Solaris em um nico processador. Com mais carga/CPUs a diferena deve aumentar. Executar com --log-bin deixa o mysqld 1 % mais lento. Compilando no Linux-x86 com gcc sem frame pointers -fomit-frame-pointer ou -fomit-frame-pointer -ffixed-ebp deixa o mysqld 1-4% mais rpido.

A distribuio MySQL-Linux fornecida pela MySQL AB normalmente compilada com pgcc, mas vamos retornar ao uso do gcc pelo fato de um bug no pgcc que gera o cdigo que no executa no AMD. Continuaremos a usar o gcc at que o bug seja resolvido. Neste meio tempo, se voc possui uma mquina que no seja AMD, voc pode ter um binrio mais rpido compilando com o pgcc. O binrio padro do MySQL para Linux ligado estaticamente para conseguir mais desempenho e ser mais portvel.

5.4. Como o MySQL Utiliza a Memria


A lista abaixo indica algumas das maneiras inas quais o servidor mysqld utiliza a memria. Onde aplicvel, o nome da varivel do servidor relevante ao uso de memria fornecido:

O buffer de chave (varivel key_buffer_size) compartilhado por todas as threads; Outros buffers usados pelo servido so alocados quando necessrios. Cada conexo utiliza algum espao especfico da thread: Uma de pilha (padro de 64K, varivel thread_stack), um buffer de conexo (varivel net_buffer_lenght), e um buffer de resultados (varivel net_buffer_lenght). Os buffers de conexes e resultados so aumentados dinamicamente para max_allowed_packet quando necessrio. Quando uma consulta est sendo executada, uma cpia da string da consulta atual tambm alocada. Todas as threads compartilhas a mesma memria base. Somente as tabelas ISAM e MyISAM compactadas so mapeadas em memria. Isto porque o espao de memria de 32-bits de 4GB no grande o bastante para a maioria das grandes tabelas. Quando sistemas com endereamento de 64bits se tornarem comuns poderemos adicionar um suporte gieral para o mapeamento de memria. Cada requisio fazendo uma varredura sequencial em uma tabela aloca um buffer de leitura (varivel read_buffer_size). Ao ler registros na ordem ``randmica'' (por exemplo, depois de uma ordenao) um buffer de leitura randmico alocado para evitar pesquisas em disco. (varivel read_rnd_buffer_size). Todas as joins so feitas em um nico passo, e a maioria delas podem ser feitas mesmo sem usar uma tabela temporria. A maioria das tabelas temporrias so tabelas baseadas em memria (HEAP). Tabelas temporrias com uma grande extenso de registros (calculada como a soma do tamanho de todas as colunas) ou que contenham colunas BLOB so armazenadas em disco.

Otimizao de Banco de dados

52

Um problema nas verses do MySQL anteriores a 3.23.2 que se uma tabela HEAP excede o tamanho de tmp_table_size, voc recebe o erro The table nome_tabela is full. A partir da verso 3.23.2, isto tratado alterando automaticamente a tabela em memria HEAP para uma tabela baseada em disco MyISAM quando necessrio. Para contornar este problema, voc pode aumentar o tamanho da tabela temporria configurando a opo tmp_table_size do mysqld, ou configurando a opo do SQL SQL_BIG_TABLES no progrma cliente. Na verso 3.20 do MySQL, o nmero mximo da tabela temporria record_buffer*16; se voc estiver utilizando esta verso, voc ter que aumentar o valor record_buffer. Voc tambm pode iniciar o mysqld com a opo --big-tables para sempre armazenar as tabelas temporrias em disco. Entretanto isto afetar a velocidade de vrias consultas complicadas.

A maioria das requisies que realizam ordenao alocam um bufer de ordenao e 0-2 arquivos temporrios dependendo do tamanho do resultado. Quase todas as anlises e clculos so feitos em um armazenamento de memria local. Nenhuma sobrecarga de memria necessrio para tens pequenos e a alocao e liberao normal de memria lenta evitada. A memria alocada somente para grandes strings inesperadas; isto feito com malloc() e free(). Cada arquivo de ndice aberto uma vez e o arquivo de dados aberto uma vez para cada thread concorrente. Uma estrutura de tabela, estrutura de coluna para cada coluna e um buffer de tamanho 3 * n alocado para cada thread concorrente. (onde n o maior tamanho do registro, sem levar em considerao colunas BLOB. Uma coluna BLOB utiliza de 5 a 8 bytes mais o tamanho dos dados contidos na mesma. O manipulador de tabelas ISAM/MyISAM iro usar um registro extra no buffer para uso interno. Para cada tabela com colunas BLOB, um buffer aumentado dinamicamente para ler grandes valores BLOB. Se voc ler uma tabela, um buffer do tamanho do maior registro BLOB alocado. Estruturas de manipulaco para todas tabelas em uso so salvos em um cache e gerenciado como FIFO. Normalmente o cache possui 64 entradas. Se uma tabela foi usada por duas threads ao mesmo tempo, o cache ter duas entredas para a tabela. Um comando mysqladmin flush-tables fecha (ou instrues FLUSH TABLES) todas tabelas que no esto em uso e marca todas tabelas em uso para serem fechadas quando a thread atualmente em execuo terminar. Isto ir liberar efetivamente a maioria da memria em uso.

ps e outros programas de informaes do sistema podem relatar que o mysqld usa muita memria. Isto pode ser causado pelas pilhas de threads em diferentes endereos de memria. Por exemplo, a verso do ps do Solaris conta a memria no usada entre as pilhas como memria usada. Voc pode verificar isto conferindo a memria disponvel com swap -s. Temos testado o mysqld com detectores comerciais de perda de memria, portanto tais perdas no devem existir.

Otimizao de Banco de dados

53

5.5. Como o MySQL Utiliza o DNS


Quando um novo cliente conecta ao mysqld, o mysqld extende uma nova thread para lidar com o pedido. Esta thread primeiro confere se o nome da mquina est no cache de nomes de mquinas. Se no, a thread tenta resolver o nome da mquina.

Se o sistema operacional suporta as chamadas seguras com thread gethostbyaddr_r() e gethostbyname_r(), a thread as utiliza para fazer a resoluo do nome mquina. Se o sistema operacional no suporta as chamadas de threads seguras, a thread trava um mutex e chama gethostbyaddr() e gethostbyname(). Perceba que neste caso nenhuma outra thread pode resolver outros nomes de mquinas que no existam no cache de nomes de mquina at que a primeira thread esteja destrave o mutex.

Voc pode desabilitar a procura de nomes de mquinas no DNS iniciando o mysqld com a opo --skip-name-resolve. No entanto, neste caso voc s pode usar nmeros IP nas tabelas de privilgio do MySQL. Se voc possuir um DNS muito lento e vrias mquinas, pode obter mais desempenho desligando a procura de nomes de mquinas usando a opo --skip-nameresolve ou aumentando HOST_CACHE_SIZE (valor padro: 128) e recompilar mysqld. Voc pode desabilitar o cache de nomes de mquinas iniciando o servidor com a opo --skip-host-cache. Para limpar a cache do nome de mquinas, envie uma instru;o FLUSH HOSTS ou execute o comando mysqladmin flush-hosts. Se voc deseja disabilitar as conexes TCP/IP totalmente, inicie o mysqld com a opo --skip-networking.

5.6. Sintaxe de SET


SET [GLOBAL | SESSION] sql_variable=expression, [[GLOBAL | SESSION] sql_variable=expression] ...

SET configura vrias opes que afetam a operao do servidor ou seu cliente. Os seguintes exemplos mostram as diferentes sintaxes que se pode usar para configurar variveis: Em verses antigas do MySQL permitiamos o uso da sintaxe SET OPTION, mas esta sintaxe agora est obsoleta. No MySQL 4.0.3 adicionamos as opes GLOBAL e SESSION e acessamos as variveis de inicializao mais importantes. LOCAL pode ser usado como sinniumo de SESSION.

Otimizao de Banco de dados

54

Se voc define diversas variveis na mesma linha de comando, o ltimo modo GLOBAL | SESSION utilizado
SET sort_buffer_size=10000; SET @@local.sort_buffer_size=10000; SET GLOBAL sort_buffer_size=1000000, SESSION sort_buffer_size=1000000; SET @@sort_buffer_size=1000000; SET @@global.sort_buffer_size=1000000, @@local.sort_buffer_size=1000000;

A sintaxe @@nome_varivel suoprtada para tornar a sintaxe do MySQL compatvel com outros bancos de dados. As diferentes variveis de sistema que podem ser configuradas esto descritas na seo de variveis de sistema deste manual. Se voc estiver usando SESSION (o padro) a opo que voc definir ter efeito at que o sesso atual finalize ou at que vec atribua um valor diferente a esta opo. Se voc estiver usando GLOBAL, que exige o privilgio SUPER, a opo lembrada e usada pelas novas conexes at que o servidor reinicie. Se voc quiser tornar uma opo permanente, voc deve defin-la em um arquivo de opo. Para evitar o uso incorreto, o MySQL exibir um erro se voc usar SET GLOBAL com uma varivel que s pode ser usada com SET SESSION ou se voc no estiver usando SET GLOBAL com uma varivel global. Se voc quiser definir uma varivel SESSION com um valor GLOBAL ou um valor GLOBAL ao valor padro do MySQL, voc pode configur-lo com DEFAULT.
SET max_join_size=DEFAULT;

Isto idntico a:
SET @@session.max_join_size=@@global.max_join_size;

Se voc quiser restringir o valor mximo com o qual uma varivel de servidor pode ser configurado com o comando SET, voc pode especifica-lo usando a opo de linha de comando --maximum-variable-name. Voc pode obter uma lista da maioria das variveis com SHOW VARIABLES. Voc pode obter o valor de uma varivel especfica com a sintaxe @@[global.|local.]variable_name:
SHOW VARIABLES like "max_join_size"; SHOW GLOBAL VARIABLES like "max_join_size"; SELECT @@max_join_size, @@global.max_join_size;

Segue aqui a descrio das variveis que usam uma sintaxe SET no padro e algumas das outras variveis. A definio das outras variveis podem ser encontrados na seo variveis de sistema, entre as opes de inicializao ou na descrio de SHOW VARIABLES.

Otimizao de Banco de dados

55

AUTOCOMMIT= 0 | 1 Se configurado com 1 todas alteraes em uma tabela ser feita de uma vez. Para iniciar uma transao de vrios comandos, deve ser usada a instruo BEGIN. Se configurado com 0 deve ser usado COMMIT/ROLLBACK para aceitar/recusar aquela transao. Note que quando voc altera do modo noAUTOCOMMIT para AUTOCOMMIT, o MySQL ir fazer um COMMIT automtico em quaisquer transaes abertas.

BIG_TABLES = 0 | 1 Se definido com 1, todas as tabelas temporrias so armazenadas no disco em vez de o ser na meria. Isto ser um pouco mais lento, mas voc no ter o erro The table tbl_name is full para grandes operaes SELECT que exigem uma tabela temporria maior. O valor padro para uma nova conexo 0 (isto , usa tabelas temporrias em memria) Esta opo era chamada SQL_BIG_TABLES. No MySQL 4.0 voc normalmente nunca deve precisar deste parmetro j que o MySQL converter automaticamente tabelas em memria para tabelas em disco se isto for necessrio.

CHARACTER SET nome_conjunto_caracteres | DEFAULT Mapeia todas as strings do e para o cliente com o mapa especificado. Atualmente a nica opo para character_set_name cp1251_koi8, mas voc pode adicionar novos mapas editando o arquivo sql/convert.cc na distribuio fonte do MySQL. O mapeamento padro pode ser restaurado utilizando o valor DEFAULT para character_set_name. Perceba que a sintaxe para configurar a opo CHARACTER SET diferente da sintaxe para configurar as outras opes.

DATE_FORMAT = format_str Determina como o servidor converte valores DATE para strings. Esta varivel est disponvel como uma opo global, local ou de linha de comando. format_str pode ser especificado convenientemente usando a funo GET_FORMAT().

DATETIME_FORMAT = format_str Determina como o servidor converte valores DATETIME para string. Esta varivel est disponvel como uma opo global, local ou de linha de comando. format_str pode ser especificada convenientemente usando a funo GET_FORMAT().

INSERT_ID = # Configura o valor que ser usado pelo comando INSERT ou ALTER TABLE seguinte ao inserir um valor AUTO_INCREMENT. Isto usado principalmente com o log de atualizaes.

Otimizao de Banco de dados

56

LAST_INSERT_ID = # Configura o valor a ser retornado de LAST_INSERT_ID(). Ele armazenado no log de atualizaes quando voc utiliza LAST_INSERT_ID() em um comando que atualiza uma tabela.

LOW_PRIORITY_UPDATES = 0 | 1 Se configurado com 1, todas instrues INSERT, UPDATE, DELETE e LOCK TABLE WRITE iro esperar at que no existam SELECT ou LOCK TABLE READ pendentes na tabela afetada. Esta opo era chamada SQL_LOW_PRIORITY_UPDATES.

MAX_JOIN_SIZE = value | DEFAULT No permite que SELECTs que provavelmente necessitem examinar mais que valor combinaes de registros. Configurando este valor, voc pode obter SELECTs onde chaves no so usadas corretamente e que provavelmente gastaro um bom tempo. Configurando-o para um valor diferente do DEFAULT ir definir o atributo SQL_BIG_SELECTS com o padro. Se voc configurar o atributo novamente, a varivel SQL_BIG_SELECTS SQL_MAX_JOIN_SIZE ser ignorada. Voc pode configurar um valor padro para esta varivel iniciando o mysqld com -O max_join_size=#. Esta opo era chamada SQL_MAX_JOIN_SIZE Note que se o resultado da consulta ja estiver na cache de consultas, o verificaio acima no ser feita. O MySQL ir enviar o resultado ao cliente. Uma vez que o resultado da consulta j foi consultado e no ser responsabilidade do servidor enviar o resultado ao cliente.

PASSWORD = PASSWORD('alguma senha') Configura a senha para o usurio atual. Qualquer usurio que no seja annimo pode alterar sua prpria senha!

PASSWORD FOR user = PASSWORD('alguma senha') Configura a senha para um usurio especfico no servidor atual. Somente um usurio com acesso ao banco de dados mysql pode fazer isto. O usurio deve ser fornecido no formato usurio@home_maquina, onde usurio e nome_mquina so exatamente o que esto listados nas colunas User e Host da tabela mysql.user. Por exemplo, se voc possui uma entrada com os campos User e Host com 'bob' e '%.loc.gov', voc escreveria:
mysql> SET PASSWORD FOR 'bob'@'%.loc.gov' = PASSWORD('newpass');

Que equivalente a:
mysql> UPDATE mysql.user SET Password=PASSWORD('newpass') WHERE User='bob' AND Host='%.loc.gov'; ->

Otimizao de Banco de dados

57

mysql> FLUSH PRIVILEGES;

QUERY_CACHE_TYPE = OFF QUERY_CACHE_TYPE = 0 | 1 | 2

ON

DEMAND,

Define a configurao da cache de consultas para esta thread. Set query cache setting for this thread.
Opo 0 ou OFF 1 ou ON 2 ou DEMAND

Descrio No armazena ou recupera resultados. Armazena todos os resultados, exceto consultas SELECT SQL_NO_CACHE .... Armazena apenas consultas SELECT SQL_CACHE ....

SQL_AUTO_IS_NULL = 0 | 1 Se configurado com 1 (padro) o ltimo registro inserido em uma tabela com um regitro auto_incremnto pode ser encontrado com a seguinte construo: WHERE auto_increment_column IS NULL. Isto usado por alguns programas ODBC como o Access.

SQL_BIG_SELECTS = 0 | 1 Se configurado com 0, o MySQL aborta as instrues SELECTs que provavelmente levam muito tempo (isto , instrues para as quais o otimizador estima que o nmero de registros examinados provavelmente ir exceder o valor de MAX_JOIN_SIZE. Isto til quando uma instruo WHERE no aconselhada for utilizado. O valor padro para uma nova conexo 1 (que permitir qualquer instruo SELECT). Se voc definir MAX_JOIN_SIZE com um valor diferente de DEFAULT, SQL_BIG_SELECTS ser definida com 0.

SQL_BUFFER_RESULT = 0 | 1 SQL_BUFFER_RESULT fora para que o resultado das SELECT's seja colocado em tabelas temporrias. Isto ir ajudar o MySQL a liberar mais cedos bloqueios de tabela e ajudaro em casos onde elas ocupam muito tempo para enviar o conjunto de resultados para o cliente.

SQL_SAFE_UPDATES = 0 | 1 Se configurado com 1, o MySQL ir aborar se tentarmos fazer um UPDATE ou DELETE sem utilizar uma chave ou LIMIT na clusula WHERE. Desta forma possvel capturar atualizaes erradas ao criarmos comandos SQL manualmente.

SQL_SELECT_LIMIT = valor | DEFAULT

Otimizao de Banco de dados

58

O nmero mximo de registros para retornar de instrues SELECT. Se uma SELECT tem uma clusula LIMIT, o LIMIT tem precedencia sobre o valor de SQL_SELECT_LIMIT. O valor padro para uma nova conexo ``unlimited'' (ilimitado). Se voc alterou o limite, o valor padro pode ser restaurado atribuindo o valor DEFAULT a SQL_SELECT_LIMIT.

SQL_LOG_OFF = 0 | 1 Se configurado com 1, nenhum registro ser feito no log padro para este cliente, se o cliente tiver o privilgio SUPER.

SQL_LOG_BIN = 0 | 1 Se configurada com 0, nenhum registro feito no log binrio para o cliente, se o cliente tiver o privilgio SUPER.

SQL_LOG_UPDATE = 0 | 1 Se configurado com 0, nenhum registro ser feito no log de atualizaes para o cliente, se o cliente tiver o privilgio SUPPER. Esta varivel est obsoleta a partir da verso 5.0.

SQL_QUOTE_SHOW_CREATE = 0 | 1 Se configurado com 1, SHOW CREATE TABLE ir colocar os nomes de tabela e colunas entre aspas. Est ligado por padro, para que replicao de tabelas com nomes de colunas estranhos funcione.

TIMESTAMP = valor_timestamp | DEFAULT Configura a hora/data para este cliente. usado para obter a hora e data original se voc utiliza o log de atualizaes para restaurar registros. valor_timestamp deve ser um timestamp UNIX Epoch, no um timestamp MySQL.

TIME_FORMAT = format_str Determina como o servidor converte valores TIME para string. Esta varivel est disponvel como uma opo global, local ou de linha de comando. format_str pode ser especificada convenientemente usando a funo GET_FORMAT()

Otimizao de Banco de dados

59

6. Detalhes de Disco
6.1. Utilizando Links Simblicos

Como mencionado acima, pesquisas em disco so o maior gargalo de desempenho. Estes problemas ficam cada vez mais aparentes quando os dados comeam a crescer tanto que efetivo armazenamento em cache se torna impossvel. Para grandes bancos de dados, onde voc acessa dados mais ou menos aleatoriamente, voc pode ter certeza de que precisar de pelo menos uma busca em disco para ler e vrias para gravar os dados. Para minimizar este problema, utilize discos com menor tempo de pesquisa.

Aumente o nmero de eixo de discos disponveis (e ento reduza a sobrecarga da pesquisa) ligando arquivos simbolicamente em diferentes discos ou utilizando striping de discos.

Usando links simblicos Significa que, para tabelas MyISAM, voc liga simbolicamente o ndice e/ou arquivos de dados ao local comum no diretrio de dados em outro disco (que pode tambm ser striped). Isto torna os tempos de pesquisa e leitura melhor (Se os discos no so usados para outras coisas). Striping Striping significa que voc possui vrios discos e coloca o primeiro bloco no primeiro disco, o segundo bloco no segundo disco, e o N-simo no (N mdulo nmero_de_discos) disco, e assim por diante. Isto significa que se o seu tamanho de dados normais menos que o tamanho do bloco (ou perfeitamente alinhado) voc ir obter um desempenho muito melhor. Striping muito dependente do SO e do tamanho do bloco. Portanto mea a performance de sua aplicao com diferentes tamanhos de blocos. Perceba que a diferena de velocidade para striping muito dependente dos parmetros. Dependendo de como voc configura os parmetros do striping e do nmero de discos voc pode obter uma diferena de vrias ordens de grandeza. Note que voc deve escolher a otimizao randmica ou pelo acesso sequencial.

Para confiabilidade voc pode desejar utilizar RAID 0+1 (striping + espelhamento) mas neste caso voc ir precisar de 2*N discos para armazenar N discos de dados. Isto provavelmente a melhor opo se voc possuir dinheiro! Voc pode tambm, entretanto, ter que investir em algum software gerenciador de volumes para lidar com isto eficientemente.

Otimizao de Banco de dados

60

Uma boa opo variar os nveis de RAID de acordo com a importncia do dado. a Por exemplo, ter dados com alguma importncia que podem ser regenerados em um armazenamento RAID 0 enquanto os dados realemtente importantes como informaes de mquinas e logs em um sistema RAID 0+1 ou RAID de N discos. RAID N pode ser um problema se voc tem vrias escritas devido ao tempo para atualizar os bits de paridade.

No Linux, voc pode obter um desempenho muito melhor (cerca de 100% sobre carga pode ser comum) utilizando hdparm para configurar sua interface de disco! O exemplo a seguir deve ser muito til para o MySQL (e provavelmente vrias outras aplicaes):

hdparm -m 16 -d 1

Perceba que o desempenho e confiana ao utilizar o exemplo acima depende de seu hardware, portanto ns sugerimos que voc teste bem seu sistema depois de utilizar hdparm! Por favor consulte a pgina do manual (man) do hdparm para maiores informaes! Se o hdparm no for usado corretamente, poder resultar em corrupo do sistema de arquivos, assim realize backups de tudo antes de experimentar!

Voc pode tambm configurar os parmetros para o sistema de arquivos que o banco de dados usa:

Se voc no precisa saber quando os arquivos foram acessados pela ltima vez (o que realmente til em um servidor de banco de dados), voc pode montar o seu sistema de arquivos com a opo -o noatime. Isto faz com que ele evite a atualizao do ltimo tempo de acesso no inode e com isto tambm evita algumas buscas em disco. Em vrios sistemas operacionais os discos podem ser montados com a opo 'async' para configurar o sistema de arquivos a ser atualizado de modo assncrono. Se o seu computador razoavelmente estvel, isto deve fornecer mais desempenho sem sacrificar a segurana. (Esta opo ligada por padro no Linux.)

Otimizao de Banco de dados

61

6.1. Utilizando Links Simblicos


6.1.1. Utilizando Links Simblicos para Bancos de Dados 6.1.2. Utilizando Links Simblicos para Tabelas 6.1.3. Usando Links Simblicos para Bancos de Dados no Windows

Voc pode mover tabelas e bancos de dados do diretrio de banco de dados para outras localizaes e troc-los por links simblicas para os novos locais. Voc pode fazer isto, por exemplo, para mover um banco de dados para um sistema de arquivos com mais espao livre ou aumentar a velocidade de seu sistema espalhando suas tabelas para discos diferentes. A maneira recomendada de se fazer isto ligar simbolicamente bancos de dados a discos diferentes e s ligar tabelas como ltimo recurso.

6.1.1. Utilizando Links Simblicos para Bancos de Dados


No Unix, a maneira de ligar simbolicamente um banco de dados , primeiramente, criar um diretrio em algum disco onde voc possui espao livre e ento criar uma ligao simblica para ele a partir do diretrio do banco de dados do MySQL.
shell> mkdir /dr1/databases/test shell> ln -s /dr1/databases/test mysqld-datadir

O MySQL no suporta que voc ligue um diretrio a vrios bancos de dados. Trocando um diretrio de banco de dados com uma ligao simblica ir funcionar bem desde que no sejam feitos links simblicos entre os bancos de dados. Suponha que voc tenha um banco de dados db1 sob o diretrio de dados do MySQL, e ento criar uma ligao simblica db2 que aponte para db1.
shell> cd /caminho/para/diretorio/dados shell> ln -s db1 db2

Agora, para qualquer tabela tbl_a em db1, tambm aparecer uma tabela tbl_a em db2. Se uma thread atualizar db1.tbl_a e outra atualizar db2.tbl_a, ocorrero porblemas. Se voc realmente precisar disto, voc deve alterar o cdigo seguinte em mysys/mf_format.c:
if (flag & 32 || (!lstat(to,&stat_buff) && S_ISLNK(stat_buff.st_mode)))

para
if (1)

Otimizao de Banco de dados

62

No Windows voc pode utilizar links simblicos para diretrios compilando o MySQL com -DUSE_SYMDIR. Isto lhe permite colocar diferentes bancos de dados em discos diferentes.

6.1.2. Utilizando Links Simblicos para Tabelas


Antes do MySQL 4.0 voc no deve utilizar tabelas com ligaes simblicas, se voc no tiver muito cuidado com as mesmas. O problema que se voc executar ALTER TABLE, REPAIR TABLE ou OPTIMIZE TABLE em uma tabela ligada simbolicamente, os links simblicos sero removidas e substituidos pelos arquivos originiais. Isto acontece porque o comando acima funcinoa criando um arquivo temporrio no diretrio de banco de dados e quando o comando completo, substitui o arquivo original pelo arquivo temporrio. Voc no deve ligar simbolicamente tabelas em um sistema que no possui uma chamada realpath() completa. (Pelo menos Linux e Solaris suportam realpath() No MySQL 4.0 links simblicos s so suportados completamente por tabelas MyISAM. Para outros tipos de tabelas voc provavelmente obter problemas estranhos ao fazer qualquer um dos comandos mencionados acima. O tratamento de links simblicos no MySQL 4.0 funciona da seguinte maneira (isto mais relevante somente para tabelas MyISAM.

No diretrio de dados voc sempre ter o arquivo de definies das tabelas e os arquivos de ndice e o arquivo de dados. O arquivo de dados e o arquivo de ndice podem ser movidos para qualquer lugar e substituidos no diretorio de dados pelos links simblicos. O arquivo de definio no pode. Voc pode ligar simbolicamente o arquivo ndice e o arquivo de dados para diretrios diferentes, independente do outro arquivo. A ligao pode ser feita partir do sistema operacional (se o mysqld no estiver em execuo) ou usando as opes DATA DIRECTORY ou INDEX DIRECTORY em CREATE TABLE. myisamchk no ir substituir um link simblico pelo ndice/arquivo. Ele funciona diretamente nos arquivos apontados pelos links simblicos. Qualquer arquivo temporrio ser criado no mesmo diretrio que o arquivo de dados/ndice est. Quando voc remove uma tabela que est usando links simblicos, o link e o arquivo para o qual ela aponta so apagados. Esta uma boa razo pela qual voc no deve executar mysqld como root e no deve permitir que pessoas tenham acesso de escrita ao diretrios de bancos de dados do MySQL. Se voc renomear uma tabela com ALTER TABLE RENAME e no deseja alterar o banco de dados, o link simblico para o diretrio de banco de dados ser renomeada corretamente. Se voc utiliza ALTER TABLE RENAME para mover uma tabela para outro banco de dados, ento a tabela ser movida para outro diretrio de banco de

Otimizao de Banco de dados

63

dados e os links simblicos antigos e os arquivos para os quais eles apontam sero removidos. Se voc no utiliza links simblicos, voc deve usar a opo --skip-symlink do mysqld para garantir que ningum pode usar mysqld para apagar ou renomear um arquivo fora do diretrio de dados.

O que ainda no suportado:


ALTER TABLE ignora todas as opes de tabela DATA DIRECTORY e INDEX DIRECTORY. SHOW CREATE TABLE no relata se a tabela possui links simblicos antes do MySQL 4.0.15. Isto tambm verdade para mysqldump que usa SHOW CREATE TABLE para gerar instrues CREATE TABLE. BACKUP TABLE e RESTORE TABLE no respeitam links simblicos. O arquivo frm nunca deve ser um link simblico (como dito anteriormente, apenas os dados e ndices podem ser links simblicos). Fazer isto (por exemplo para fazer sinnimos), produzir resultados errados. Suponha que voc tenha um banco de dados db1 sob o diretrio de dados do MySQL, uma tabela tbl1 neste banco de dados e voc faa um link simblico tbl2 no diretrio db1 que aponmta para tbl1:
cd ln ln ln /path/to/datadir/db1 -s tbl1.frm tbl2.frm -s tbl1.MYD tbl2.MYD -s tbl1.MYI tbl2.MYI

shell> shell> shell> shell>

Agora se uma thread l db1.tbl1 e outra thread atualiza db1.tbl2, haver problemas: a cache de consultas ser enganada (ela acreditar que tbl1 no foi atualizado e retornar resultados desatualizados), o comando ALTER em tbl2 tambm ir falhar.

6.1.3. Usando Links Simblicos para Bancos de Dados no Windows


A partir do MySQL verso 3.23.16, o mysqld-max e servidores mysql-max-nt na distribuio MySQL so compilados com a opo -DUSE_SYMDIR. Isto permite que voc coloque um diretrio de banco de dados em discos diferentes adicionando um link simblico para ele. (Isto parecido com o a com que links simblicos funcionam no Unix, embora o procedimento para configurar o link seja diferente). No Windows, voc cria um link simblico para um banco de dados MySQL criando um arquivo que contem o caminho para o diretrio de destino. Salve o arquivo no diretrio de dados usando o nome de arquivo nome_bd.sym, onde nome_bd o nome do banco de dados. Por exemplo, se o diretrio de dados do MySQL C:\mysql\data e voc precisa ter o banco de dados foo localizado em D:\data\foo, voc deve criar o arquivo C:\mysql\data\foo.sym que contm o caminho D:\data\foo\. Depois disto, todas tabelas criadas no banco de dados foo sero criadas no D:\data\foo. O diretrio D:\data\foo deve existir para ele funcionar. Note tambm que o link simblico no ser usado se um diretrio com o nome do banco de dados existe no diretrio de dados MySQL. Isto significa que se voc j tem um diretrio de banco de dados chamado foo no direorio de

Otimizao de Banco de dados

64

dados, voc deve mov-lo para D:\data antes do link simblico ser efetivado. (Para evitar problemas, o servidor no deve estar executando quando voc mover o diretrio do banco de dados.) Note que devido a penalidade que voc tem na velocidade quando abre todas as tabelas, ns no habilitamos esta opo por padro, mesmo se voc compilar o MySQL com suporte a isto. Para habilitar links simblicos voc deve colocar no seu arquivo my.cnf ou my.ini a seguinte entrada:
[mysqld] symbolic-links

No MySQL 4.0 --simbolic-links est habilitado por padro. Se voc no precisa us-lo voc pode usar a opo skip-symbolic-linkd.

Para mais informaes acesse o site: www.mysql.com

Você também pode gostar