Você está na página 1de 19

Tuning no Oracle - Parte 01

Tuning no Oracle - Parte 01


Pedro Valiati
Introduo Finalmente vamos trocar algumas idias sobre o assunto mais desafiador e interessante, na minha opinio, do Oracle: Tuning. Tentarei redigir este artigo (o primeiro da srie) de forma a ser til s diversas reas de interesse, desde o gerente de T.I., arquiteto de sistemas, Solution Provider, etc... Porm, especialmente til para os desenvolvedores e DBAs. Desde j adianto que no existe uma frmula para um tuning no banco de dados, dificilmente um trabalho de tuning ser igual ao outro. No entanto, todos sero fruto de pesquisa e anlise por isso, neste artigo abordarei apenas exemplos didticos de casos ocorridos, e nos que o sucederem, abordarei conceitos e o estudo de alguns casos prticos, onde o leitor poder, inclusive, acompanhar de um servidor Oracle local. O que tuning? Imaginemos uma viagem de lua-de-mel para o litoral, onde estava no programa levar a sogra, com seu cachorrinho de estimao, e a respectiva acompanhante (do cachorrinho), o sobrinho (que nunca viu o mar) e ainda bagagem para 30 dias (quando o passeio durar 7 dias). Imaginemos tambm que o noivo fique com todo o CUSTO (guarde esta palavra) excessivo dessa lua de mel. Provavelmente, e por motivos bvios, ele comece a cortar gastos. Ele percebe que a sogra, o sobrinho e tanta bagagem so totalmente desnecessrios viagem. Nesse momento ele fez um TUNING de gastos na viagem, ou seja, ele corta todos os CUSTOS desnecessrios para que a meta, no caso a lua-de-mel, seja bem sucedida. Tuning isso, nas mais diversas reas da vida. Focando em banco de dados, nada mais que eliminar todo e qualquer CUSTO desnecessrio na execuo de uma ou mais transaes. Esse custo excessivo pode ser eliminado atravs de diversas solues, tais como: criao e deleo de ndices, alterao de parmetros (inclusive de memria), remodelagem fsica de tabelas, gerao de estatsticas, reescrita de cdigos SQL entre outros. Estes assuntos sero abordados neste artigo ou nos prximos. Planos de execuo Quando, por exemplo, realizamos uma transao de consulta no Oracle, ou mesmo transaes de manipulao de dados, cria-se previamente um plano para a execuo da transao propriamente dita, ou seja, o melhor caminho ou o jeito mais fcil que o otimizador (Nota 1) do Oracle acredita ser para alcanar o resultado da consulta. Nota 1. Otimizador No tentemos entend-lo como uma entidade, mas sim como um conjunto de informaes coletadas, na maioria das vezes, atravs das estatsticas do banco de dados, responsvel pela tomada de deciso do melhor plano de acesso das transaes no Oracle. Claro que o Oracle no perfeito e, s vezes (muito s vezes), no escolhe o melhor plano de acesso para uma determinada transao. neste momento que entra o DBA. Este ao perceber o engano do otimizador poder, por sua prpria conta e risco (e mritos tambm), forar o Oracle a adotar um plano de acesso diferente atravs de uma alternativa chamada HINT, que ser vista posteriormente. Para se obter um plano de execuo, deve-se usar o comando explain plan, cujo exemplo se encontra na Listagem 1.

Listagem 1. Gerao do plano de execuo. SQL> EXPLAIN PLAN FOR 2 SELECT * 3 FROM emp e, dept d 4 WHERE e.deptno = d.deptno 5 AND e.ename = 'SMITH'; Explained. Plan Table -------------------------------------------------------------------------------| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop | -------------------------------------------------------------------------------| SELECT STATEMENT | | | | | | | | NESTED LOOPS | | | | | | | | TABLE ACCESS FULL |EMP | | | | | | | TABLE ACCESS BY INDEX RO|DEPT | | | | | | | INDEX UNIQUE SCAN |PK_DEPT | | | | | | -------------------------------------------------------------------------------8 rows selected. Vamos entender o que a Listagem 1 nos mostra: SELECT STATEMENT: inicialmente o Oracle identifica o tipo de transao; NESTED LOOPS: informa, se houver, o Join Method (ser visto ainda nesse artigo); TABLE ACCESS FULL: mtodo de acesso primeira tabela da consulta (aps a clusula FROM); TABLE ACCESS BY INDEX ROWID: mtodo de acesso segunda tabela da consulta; INDEX UNIQUE SCAN: detalhamento do mtodo de acesso TABLE ACCESS BY INDEX ROWID (percebam a identao), contendo o nome do ndice usado. Na tabela formatada pelo Explain Plan temos tambm: Rows: nmero de linhas selecionadas; Bytes: total de bytes (somatria dos blocos) selecionados; Cost: custo gerado pela etapa da execuo; Pstart / Pstop: implementado na verso 9i e usado em tabelas particionadas, mostra as parties lidas, inicial e final. Claro que temos planos de execuo bem mais complexos, mas a idia foi passada. Vejamos agora os principais mtodos de juno, que so importantssimos para criar uma boa consulta

Tuning no Oracle - Parte 02


Pedro Valiati
Principais mtodos de juno do Oracle Nested loop O Nested Loop o mtodo de juno mais vantajoso para realizar a juno de tabelas as quais retornaro poucos registros e ainda claramente relacionadas atravs de dependncia entre as mesmas tabelas (chaves primria e estrangeira). Desta forma, o otimizador elege uma tabela principal, que servir de base para buscar os registros na tabela dependente. Para cada registro existente na tabela principal, o Oracle buscar os registros na tabela dependente que atendam s condies de filtro (clusula WHERE). Veja um exemplo na Listagem 2.

Listagem 2. Uma consulta simples. 1. 2. 3. 4. SELECT e.nome, e.sobrenome, d.departamento FROM empregados e, departamentos d WHERE e.dep_num = d.de_num AND e.emp_num = 15;

O otimizador dividir este comando em duas consultas menores, sendo a principal representada na Listagem 3. A dependncia entre as tabelas notada claramente na consulta, visto que desnecessrio recuperar informaes na tabela departamentos referente a empregados que no sejam aqueles retornados pela consulta da Listagem 3, ou seja, nada ser retornado da tabela departamentos sobre funcionrios que no possuam emp_num=15. Listagem 3. Consulta principal, aps diviso do otimizador. 1. 2. 3. SELECT e.nome, e.sobrenome FROM empregados e WHERE e.emp_num = 15

Para cada registro retornado pela consulta principal, o otimizador aplicar a consulta dependente, definida na Listagem 4, para gerar o resultado final. A consulta da Listagem 4 poder ser chamada de consulta dependente pois, como vimos, diretamente dependente da consulta anterior. Listagem 4. Consulta dependente, aps diviso do otimizador. 1. 2. 3. SELECT d.departamento FROM departamentos d WHERE e.dep_num = d.dep_num

Merge Join Tambm chamado de SORT_MERGE_JOIN, um MERGE JOIN basicamente classifica todas as linhas relevantes na primeira tabela pela chave JOIN (Nota 2), bem como as linhas relevantes na segunda tabela tambm pela chave JOIN, e funde ento estas linhas classificadas. Exemplo! Em uma venda de garagem voc pode comprar 400 livros. O negcio comprar tudo ou nada. Voc resolve comprar tudo. Agora, voc tem que encontrar os livros que voc j tem em casa. Como voc faria isso? Provavelmente, voc faria um MERGE JOIN, ou seja, primeiramente voc classifica os livros que tem em casa pela chave primria (autor, ttulo), ento voc classifica os 400 livros por sua chave primaria (autor, ttulo). Agora voc comea pelo alto de ambas as pilhas. Se o valor da chave primaria da pilha esquerda for mais elevado, ento retira um livro da pilha direita e vice-versa. Quando ambos os valores das pilhas so iguais, voc encontrou as duplicidades. exatamente este o processo que o Oracle executa nos Merge Joins. Nota 2. Chave JOIN Nada mais do que a condio na clusula WHERE da consulta onde duas tabelas so relacionadas atravs do sinal de = (igual), entre dois campos em comum. Hash Join O otimizador do Oracle usa (ou pelo menos deveria usar) este mtodo para realizar a juno entre tabelas quando um grande nmero de linhas ser retornado para gerar o resultado. Normalmente ele usa esse mtodo de juno de tabelas quando so encontradas as estatsticas para as mesmas, no entanto, ele no usar o Hash Join quando o operador no for de igualdade. O otimizador escolhe a menor tabela para construir uma tabela em memria utilizando os

campos que compem a chave da juno descrita na consulta SQL. A chave de juno composta pelos campos utilizados para relacionar as tabelas envolvidas na operao. Em seguida, o otimizador busca na outra tabela da consulta, relacionada pela chave da juno, recuperar os registros que atendem s condies e compara com os dados da tabela criada em memria, tambm conhecida como TABELA HASH. OBS: Na verdade, o otimizador do Oracle 9i adora o Hash Join, j perdi a conta das vezes em que precisei adicionar HINT para forar o Oracle a usar outro mtodo de juno. Veremos mais frente, mas s para adiantar, HINT uma espcie de coelho tirado da cartola para forar o otimizador do Oracle a executar um plano de execuo diferente do que ele vem realizando. Sort Merge O Sort Merge costuma ser a ltima opo do otimizador. Quando no existe relao de dependncia entre as tabelas o otimizador no usar o Nested Loop. Se o otimizador perceber que as tabelas j esto ordenadas, ento ele (provavelmente) tambm no usar o Hash Join. Outra situao a qual o Sort Merge deve ser utilizado quando os operadores so <, <=, > ou >= pois, utilizando este mtodo, o otimizador organiza as duas tabelas pelos campos da juno da consulta (obviamente se as mesmas no estiverem organizadas) e posteriormente, simplesmente as une. Analisando um estudo de caso Inicialmente vamos criar a nossa VIEW de visualizao dos planos de execuo. Execute o script que se encontra no portal da SQL Magazine conforme Listagem 5. Listagem 5. Criao da Plan View e execuo da mesma. SQL> @<<nome_do_script>> SQL> View Criada SQL> Select * from plan_view; Temos na Listagem 5 a criao da plan_view e consequente execuo da mesma, atentando para os schemas de execuo e criao. Quem preferir tambm pode usar o comando: select * from table(DBMS_XPLAN.DISPLAY); Mos obra Vamos analisar uma consulta problemtica (obviamente modificada por motivos de segurana) (Listagem 6). Nesta consulta trs tabelas so consultadas e unidas atravs da chave JOIN. Listagem 6. Consulta problemtica. SELECT object_status.object_status_id, reg1_sec_20051201.account_id, reg1_sec_20051201.create_dttm, reg1_sec_20051201.record_type, reg1_sec_20051201.recordaction, reg1_sec_20051201.billpublisherid, reg1_sec_20051201.proccenterid, reg1_sec_20051201.billerid, reg1_sec_20051201.alt_acct_num, reg1_sec_20051201.eb_activity_amt, reg1_sec_20051201.eb_amount_due, reg1_sec_20051201.amount_due, reg1_sec_20051201.amount_due_flag, reg1_sec_20051201.eb_bal_amt, reg1_sec_20051201.bal_amt, reg1_sec_20051201.bal_amt_flag, reg1_sec_20051201.eb_min_due, reg1_sec_20051201.eb_prev_bal, reg1_sec_20051201.eb_ebillid, reg1_sec_20051201.eb_duedate_tms, reg1_sec_20051201.eb_opening_tms, reg1_sec_20051201.ref_text, reg1_sec_20051201.eb_tms, reg1_sec_20051201.eb_closing_tms, reg1_sec_20051201.eb_url, reg1_sec_20051201.eb_tlnk_url, reg1_sec_20051201.eb_tad_url, reg1_sec_20051201.eb_ttext, reg1_sec_20051201.fi_tran_tms, reg1_sec_20051201.eb_mag_type, reg1_sec_20051201.errorcode, reg1_sec_20051201.errormessage, status_code.status_code_name, object_status.status_user_name,

object_status.status_process_name FROM reg1_sec_20051201, object_status, status_code WHERE object_status.object_status_id = reg1_sec_20051201.object_status_id ---Olha a Chave JOIN!! AND object_status.status_code_id = status_code.status_code_id; ---E Outra aqui!! Vamos agora observar seu plano de execuo (Listagem 7). Listagem 7. Plano de execuo da consulta da Listagem 6. SELECT * FROM PLAN_VIEW; Plan Table ---------------------------------------------------------------------------------------------| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop | ---------------------------------------------------------------------------------------------| SELECT STATEMENT | | 16M| 9G|2993642| | | | HASH JOIN | | 16M| 9G|2993642| | | | TABLE ACCESS FULL |STATUS_CODE | 87| 1K| 2| | | | MERGE JOIN | | 16M| 9G|2976102| | | | TABLE ACCESS BY INDEX R | reg1_sec_20051201 | 16M| 9G|2429116| | INDEX FULL SCAN | reg1_sec_20051201_ | 16M| | 37772| | | SORT JOIN | | 19M| 792M| 546986| | | | TABLE ACCESS FULL |OBJECT_STATUS | 19M| 792M| 34032| | ----------------------------------------------------------------------------------------------

| | |

Perceba que temos duas ocorrncias de FULL TABLE SCAN (linha TABLE ACCESS FULL). Neste evento, o Oracle varre todos os blocos de uma tabela para selecionar apenas os blocos solicitados na consulta. Ser que um ndice seria interessante nesta consulta? Se o filtro nesta tabela no for muito seletivo ou se a tabela for considerada pequena, o Oracle acredita (e na maioria das vezes tem razo) que o TABLE ACCESS FULL ser mais rpido, mesmo se a coluna da tabela pesquisada tiver um ndice. No entanto, estatsticas falsas (Nota 3) tambm podem contribuir para uma m escolha do otimizador no acesso tabela. Vamos ento descobrir o porqu do TABLE ACCESS FULL. Inicialmente vejamos o nmero de linhas nas tabelas. A primeira no tem nem graa. Como o prprio plano de execuo mostra, ela possui apenas 87 linhas. J a segunda possui quase 20 milhes de registros (19M). Este o ponto a ser melhorado! Analisando a seletividade, vamos verificar a quantidade de linhas retornadas nesta consulta (Listagem 8). Nota 3. Estatsticas Rapidamente, podemos dizer que as estatsticas do Oracle (e em outros bancos tambm) so um conjunto de informaes colhidas do prprio banco de dados para ajudar o otimizador na tomada de deciso. As estatsticas no Oracle podem ser obtidas atravs dos comandos Analyze e do comando DBMS_STATS. Para conceituarmos estatsticas no Oracle teramos que ser bem mais amplos, daria com certeza para escrevermos um artigo sobre o assunto. O que voc acha? Caso positivo, sinta-se vontade para me enviar um e-mail. Listagem 8. Verificando o nmero de registros na tabela. SELECT count(object_status.object_status_id) FROM sis2070_rcd_obj, object_status, status_code WHERE object_status.object_status_id = sis2070_rcd_obj.object_status_id AND object_status.status_code_id = status_code.status_code_id

count(object_status.object_status_id) -------------------------------------------16734839 A seletividade est muito alta (Nota 4), cerca de 17 milhes de linhas selecionadas de uma tabela de 20 milhes (usando frmula: linhas da tabela retornadas na consulta/linhas da tabela * 100 teremos 80% da tabela). por isso que o otimizador jamais usaria um ndice mesmo se a coluna tivesse um (e tem!). Nestes casos, perguntamos ao DA (Data Architect Arquiteto de dados) ou ao desenvolvedor se ele realmente precisa deste nmero de linhas selecionadas. Se o mesmo disser que no precisa de todos os dados, ento solicitamos a ele que construa um filtro melhor para a consulta, no entanto, se cada linha retornada for necessria teremos de verificar outras formas para torn-la mais rpida. Inicialmente usaremos uma ferramenta do Oracle chamada HINT. Nota 4. Seletividade de uma consulta Podemos chamar seletividade como sendo a relao estabelecida entre a quantidade de linhas de uma tabela retornadas por uma consulta com a quantidade total de linhas da mesma tabela. exatamente atravs da anlise dessa seletividade que o Oracle decide entre usar um ndice ou varrer todos os blocos de uma tabela. Por exemplo, imaginemos uma tabela com 1 milho de linhas: se uma consulta na mesma retorna 900 mil linhas muito mais rpido o Oracle varrer a tabela toda do que utilizar um ndice. Apenas para termos um parmetro, qualquer seletividade acima de 10% do valor total de linhas de uma tabela considerada alta, e dificilmente o Oracle utilizar um ndice nessa consulta. Supondo que no campo status_code_id tenhamos o ndice OBJECT_STATUS_NK1_X e no campo object_status_id tenhamos o ndice OBJECT_STATUS_NK2_X. Com o HINT de ndice, a nossa consulta ficaria conforme Listagem 9. Atentando que, para consultas com alta seletividade, o uso de ndices no aconselhado, portanto estamos apenas ilustrando uma situao. Listagem 9. A consulta problemtica com uma HINT forando a utilizao de um ndice. SELECT /*+ index(object_status OBJECT_STATUS_NK1_X OBJECT_STATUS_NK2_X) */ object_status.object_status_id, reg1_sec_20051201.account_id, ... ... FROM reg1_sec_20051201, object_status, status_code WHERE object_status.object_status_id = reg1_sec_20051201.object_status_id AND object_status.status_code_id = status_code.status_code_id;E o plano de execuo seria. Plan Table -----------------------------------------------------------------------------------------------| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop | -----------------------------------------------------------------------------------------------| SELECT STATEMENT | | 16M| 9G|3266651 | | | | HASH JOIN | | 16M| 9G|3266651 | | | | TABLE ACCESS FULL |STATUS_CODE | 87| 1K| 2| | | | MERGE JOIN | | 16M| 9G|3249111 | | | | TABLE ACCESS BY INDEX R |reg1_sec_20051201 | 16M| 9G|2429116 | | | | INDEX FULL SCAN |reg1_sec_20051201_NK1_X | 16M| | 37772 | | | | SORT JOIN | | 19M| 792M| 819995 | | | | TABLE ACCESS BY INDEX |OBJECT_STATUS | 19M| 792M| 306989 | | | | INDEX FULL SCAN |OBJECT_STATUS_NK2_X | 19M| | 68177 | | | ------------------------------------------------------------------------------------------------

Acabamos de comprovar a obedincia do Oracle, ou seja, at mesmo quando solicitamos a ele uma manobra absurda, ele segue a risca o solicitado, ou seja, ele contrariou totalmente o otimizador (que estava coberto de razo) e passou a usar ndice na tabela OBJECT_STATUS. Alm disso, podemos verificar se o tempo de resposta seria melhor se mudssemos todos os mtodos de juno para Hash Join. Neste caso a nossa consulta ficaria conforme Listagem 10. Listagem 10. A consulta problemtica com uma HINT forando um HASH JOIN. SELECT /*+ USE_HASH (object_status status_code reg1_rcd_obj) */ object_status.object_status_id, reg1_sec_20051201.account_id, ... ... FROM reg1_sec_20051201, object_status, status_code WHERE object_status.object_status_id = reg1_sec_20051201.object_status_id AND object_status.status_code_id = status_code.status_code_id; Plan Table -----------------------------------------------------------------------------------| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop | -----------------------------------------------------------------------------------| SELECT STATEMENT | | 161K| 10M| 560145 | | | | HASH JOIN | | 161K| 10M| 560145 | | | | TABLE ACCESS FULL | STATUS_CODE | 87| 1K| 2| | | | HASH JOIN | | 161K| 8M| 560124 | | | | TABLE ACCESS FULL | reg1_sec_20051201 | 161K| 7M| 246024 | | | TABLE ACCESS FULL | OBJECT_STATUS | 19M| 151M| 34035 | | ------------------------------------------------------------------------------------

| |

Perfeito, conforme o HINT utilizado, o mtodo de juno para todas as tabelas foi o HASH JOIN. Para melhorar ainda mais, vamos paralelizar a consulta, ou seja, vamos mandar o Oracle utilizar um nmero maior de processadores na consulta (Listagem 11). Lembrando que esta mudana no plano de execuo no ir diminuir o custo da consulta, ou seja, o esforo que o Oracle realizou para executar a consulta. A vantagem no paralelismo que teremos mais de um processador lendo blocos de dados ao mesmo tempo. S para exemplificar, como ter dois carros tendo que percorrer 50 KM cada, ao invs de apenas um carro tendo que percorrer 100 Km. Listagem 11. A consulta problemtica paralelizada. SELECT /*+ PARALLEL(object_status, 5) */ object_status.object_status_id, reg1_sec_20051201.account_id, ... ... FROM reg1_sec_20051201, object_status, status_code WHERE object_status.object_status_id = reg1_sec_20051201.object_status_id AND object_status.status_code_id = status_code.status_code_id; Neste caso, o Oracle usar cinco processadores na varredura ou FULL SCAN da tabela OBJECT_STATUS. Pra finalizar, vamos juntar todas as HINTS e verificar o plano de execuo, inclusive a no aconselhada na Listagem 9. Como dissemos, neste momento o que importa conhecermos as possibilidades. (Listagem 12). Listagem 12. A consulta problemtica aps o tuning. SELECT /*+ index(object_status OBJECT_STATUS_NK1_X OBJECT_STATUS_NK2_X) USE_HASH(object_status status_code reg1_sec_20051201) PARALLEL(object_status, 5)

*/

object_status.object_status_id, reg1_sec_20051201.account_id, reg1_sec_20051201.create_dttm, reg1_sec_20051201.record_type, reg1_sec_20051201.recordaction, reg1_sec_20051201.billpublisherid, reg1_sec_20051201.proccenterid, reg1_sec_20051201.billerid, reg1_sec_20051201.alt_acct_num, reg1_sec_20051201.eb_activity_amt, reg1_sec_20051201.eb_amount_due, reg1_sec_20051201.amount_due, reg1_sec_20051201.amount_due_flag, reg1_sec_20051201.eb_bal_amt, reg1_sec_20051201.bal_amt, reg1_sec_20051201.bal_amt_flag, reg1_sec_20051201.eb_min_due, reg1_sec_20051201.eb_prev_bal, reg1_sec_20051201.eb_ebillid, reg1_sec_20051201.eb_duedate_tms, reg1_sec_20051201.eb_opening_tms, reg1_sec_20051201.ref_text, reg1_sec_20051201.eb_tms, reg1_sec_20051201.eb_closing_tms, reg1_sec_20051201.eb_url, reg1_sec_20051201.eb_tlnk_url, reg1_sec_20051201.eb_tad_url, reg1_sec_20051201.eb_ttext, reg1_sec_20051201.fi_tran_tms, reg1_sec_20051201.eb_mag_type, reg1_sec_20051201.errorcode, reg1_sec_20051201.errormessage, status_code.status_code_name, object_status.status_user_name, object_status.status_process_name FROM reg1_sec_20051201, object_status, status_code WHERE object_status.object_status_id = reg1_sec_20051201.object_status_id AND object_status.status_code_id = status_code.status_code_id;

Plan Table ------------------------------------------------------------------------------------------| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop | ------------------------------------------------------------------------------------------| SELECT STATEMENT | | 16M| 9G| 714M| | | | HASH JOIN | | 16M| 9G| 714M| | | | TABLE ACCESS FULL |STATUS_CODE | 87| 1K| 2| | | | HASH JOIN | | 16M| 9G| 714M| | | | TABLE ACCESS BY INDEX R |OBJECT_STATUS | 19M| 792M| 306989 | | | INDEX FULL SCAN |OBJECT_STATUS_NK2_X | 19M| | 68177 | | | | TABLE ACCESS FULL |reg1_sec_20051201 | 16M| 9G| 246024 | | | -------------------------------------------------------------------------------------------

Notemos tudo conforme solicitado na consulta, ou seja, o mtodo de juno HASH JOIN, bem como os acessos via ndice. interessante e muito til percebermos que podemos montar o nosso plano de acesso conforme as nossas necessidades. O domnio dessa tcnica multiplica nossas alternativas de soluo em problemas de tuning em banco de dados Oracle. Para que todos possam visualizar se o impacto dessas mudanas foram positivas ou negativas, se o custo e, consequentemente, o tempo de execuo aumentou ou diminuiu, seria necessria a reproduo do ambiente dessa consulta, mas ainda no o momento (alm de ser invivel, neste caso). A inteno foi realmente demonstrar as possibilidades e alternativas possveis alterando o plano de execuo. Nos prximos artigos ns enviaremos os scripts necessrios para a criao do ambiente, bem como as caractersticas de banco de dados, para que todos possamos acompanhar no somente os efeitos no plano de execuo mas tambm os ganhos obtido em cada mudana da maneira mais prxima possvel. Concluso Essa foi apenas a primeira parte do nosso bate papo sobre tuning no Oracle. Conhecemos alguns conceitos sobre planos de execuo e tivemos uma noo bastante interessante sobre HINTS. Na prxima edio continuaremos com mais alguns conceitos e com um estudo mais prtico.

Tuning no Oracle - Parte 03


Pedro Valiati
Introduo Continuando o nosso estudo sobre tuning no Oracle, vamos finalmente trabalhar em um estudo de caso, sem no entanto, abandonarmos os conceitos. O nosso exemplo a ser utilizado bastante simples, eu confesso, mas nos dar uma viso interessante sobre recursos disponveis para otimizao, bem como a resposta do Oracle nestas situaes. Estudo de caso Nosso estudo ser baseado em testes de mtrica de tempo e custo. Executaremos a mesma consulta com diversas variantes na execuo como: a utilizao de mtodos de juno diferentes, a utilizao ou no de ndices, chaves primrias e chaves de juno e, as alteraes no plano de execuo causadas pela gerao ou no de estatsticas de banco de dados, bem como o parmetro DB_FILE_MULTIBLOCK_READ_COUNT (Nota 1). Nota 1. Parmetro DB_FILE_MULTIBLOCK_READ_COUNT O parmetro DB_FILE_MILTIBLOCK_READ_COUNT informa para o otimizador quantos blocos o Oracle consegue ler de uma s vez. Ambiente utilizado Hardware: Um servidor com processador Pentium 4 3.0 ghz; 1 Gb de memria ram; Disco serial ATA. Software: Windows XP; Oracle 9.2 Definies de banco de dados: Criao das tablespaces EXAMPLE e INDX com gerenciamento local e extents com tamanho uniforme de 20 Mb e 1 Gb de tamanho cada uma; Otimizador configurado como CHOOSE. Esta configurao, como o prprio nome diz, condiciona o mtodo de otimizao das transaes no Oracle existncia ou no de estatsticas nas tabelas consultadas. Caso tenhamos as tabelas analisadas, ento o mtodo escolhido ser o ALL_ROWS, caso contrrio, o mtodo de otimizao aplicado ser o RULES, ou mais comumente chamado de regra. Na Parte 3 do artigo ns falaremos um pouco mais sobre os mtodos de otimizao. Parmetro DB_FILE_MULTIBLOCK_READ_COUNT = 8 (ver Nota 1). Toda a configurao deste ambiente se encontra no script da seo Scripts para execuo. Scripts para execuo Primeiramente criaremos a estrutura de armazenamento, ou seja, as tablespaces (Listagem 1). Aps a criao das tablespaces, criaremos o esquema com todos os objetos. Perceba na Listagem 2 que adicionei alguns comentrios no decorrer do script para facilitar o entendimento. Esta listagem apresenta parte do script, sua verso na ntegra pode ser obtida no portal da SQL Magazine. Listagem 1. Scripts para criao das tablespaces. CREATE TABLESPACE EXAMPLE DATAFILE 'C:\ORACLE\ORADATA\EXAMPLE01.DBF' SIZE 1024M; LOGGING

ONLINE PERMANENT EXTENT MANAGEMENT LOCAL UNIFORM SIZE 20M SEGMENT SPACE MANAGEMENT AUTO; CREATE TABLESPACE INDX DATAFILE 'C:\ORACLE\ORADATA\INDX01.DBF' SIZE 600M; LOGGING ONLINE PERMANENT EXTENT MANAGEMENT LOCAL UNIFORM SIZE 20M SEGMENT SPACE MANAGEMENT AUTO;

Listagem 2. Scripts para criao do esquema SQL_MAG_USER. CREATE USER SQL_MAG_USER PROFILE DEFAULT IDENTIFIED BY sqlmag DEFAULT TABLESPACE EXAMPLE TEMPORARY TABLESPACE TEMP ACCOUNT UNLOCK; GRANT CREATE SESSION TO SQL_MAG_USER; -- Um pouco de segurana. (Ver Nota 2) -- Concesso de permisso nas Tablespaces ALTER USER SQL_MAG_USER QUOTA UNLIMITED ON EXAMPLE QUOTA UNLIMITED ON INDX; ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=8; -- (Ver Nota 1) CREATE TABLE SQL_MAG_USER.DEPARTAMENTO (DEP_ID NUMBER(2) NOT NULL, DEP_NOME VARCHAR2(20) NOT NULL ) TABLESPACE EXAMPLE; -- Insero de dados na tabela departamento insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values insert into SQL_MAG_USER.DEPARTAMENTO values COMMIT;

(1,'Presidencia'); (2,'Diretoria'); (3,'Gerencia'); (4,'Vendas'); (5,'Informatica'); (6,'Apoio'); (7,'Secretaria'); (8,'Limpeza');

CREATE TABLE SQL_MAG_USER.FUNCIONARIO (FUNC_ID NUMBER(10) NOT NULL, FUNC_NOME VARCHAR2(100) NOT NULL, FUNC_DAT_CONTRATACAO DATE NOT NULL, FUNC_DAT_NASCIMENTO DATE NOT NULL, FUNC_EST_CIVIL VARCHAR2(2) NOT NULL, FUNC_SEXO CHAR(1) NOT NULL, DEP_ID NUMBER(2) NOT NULL, CAR_ID NUMBER(2) NOT NULL) TABLESPACE EXAMPLE;

-- Insero de dados na tabela funcionario. INSERT INTO SQL_MAG_USER.FUNCIONARIO VALUES (1 ,'Luis' ,TO_DATE('01/jan/2003', 'dd-Mon-yyyy HH:MI:SS AM') ,TO_DATE('01/mar/ 1950', 'dd-Mon-yyyy HH:MI:SS AM'), 'Ca' ,'M',1,1 ); ... INSERT INTO SQL_MAG_USER.FUNCIONARIO VALUES (5 ,'Marcio' ,TO_DATE('01/jan/2003', 'dd-Mon-yyyy HH:MI:SS AM') ,TO_DATE('01/mar/1950', 'dd-Mon-yyyy HH:MI:SS AM') , 'Ca' ,'M',2,3 ); commit; -- Bloco PL/SQL para insero de dados na tabela funcionario. DECLARE i PLS_INTEGER := 5; BEGIN WHILE i < 2000 LOOP i := i + 1; INSERT INTO SQL_MAG_USER.Funcionario VALUES (i,'Pedro' ,TO_DATE('01/jan/05', 'dd-Mon-yyyy HH:MI:SS AM') ,TO_DATE('08/set/77', 'dd-Mon-yyyy HH:MI:SS AM'), 'Ca' ,'M',3,4 ); commit; END LOOP; END; / ... / -- Criao da tabela cargo. CREATE TABLE SQL_MAG_USER.CARGO (CAR_ID NUMBER(2) NOT NULL, CAR_NOME VARCHAR2(50) NOT NULL) TABLESPACE EXAMPLE ; -- Insero de dados na tabela cargo. insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values insert into SQL_MAG_USER.CARGO values COMMIT;

(1,'Presidente'); (2,'Vice-Presidente'); (3,'Diretor'); (4,'Gerente'); (5,'Vendedor'); (6,'Analista'); (7,'Suporte'); (8,'Secretario'); (9,'Faxineiro');

Nota 2. Privilgio de sistema Create Session Na maioria das vezes o privilgio de sistema create session suficiente para um usurio de aplicao no banco de dados. comum criarmos usurios no Oracle concedendo a ele as roles connect e resource, mas precisamos analisar se realmente isso preciso, veja por que: A role connect consiste em : - CREATE VIEW (Nvel de risco alto) - CREATE TABLE (Nvel de risco altssimo - ALTER SESSION (Nvel de risco altssimo) - CREATE CLUSTER (Nvel de risco alto) - CREATE SESSION - CREATE SYNONYM - CREATE SEQUENCE - CREATE DATABASE LINK (Nvel de risco NEM PENSAR!)

J a role Resource consiste em: - CREATE TYPE - CREATE TABLE - CREATE CLUSTER - CREATE TRIGGER - CREATE OPERATOR - CREATE SEQUENCE - CREATE INDEXTYPE - CREATE PROCEDURE - UNLIMITED TABLESPACE

(Nvel de risco altssimo)

(Nvel de risco altssimo) (Nvel de risco altssimo)

Tuning no Oracle Parte 04


Pedro Valiati
Vamos inicialmente para a nossa historinha Imaginemos uma empresa com 10 mil funcionrios (uma empresa grande correto?), seguida da tabela de funcionrios, cargos e departamentos, criadas acima. Um gerente pediu a um desenvolvedor distrado que gerasse um relatrio de nomes e cargos de todos os funcionrios do sexo feminino. Ento o desenvolvedor disparou uma consulta na base de dados Oracle. Para fins de anlise, vamos executar alguns comandos antes da query para capturar informaes que nos sero teis no tuning (Listagem 3). Listagem 3. Consulta disparada pelo desenvolvedor distrado. SQL> SET TIMING ON -- Para verificarmos o tempo de execuo SQL> SET AUTOTRACE TRACEONLY -- Para verificarmos o plano de execuo logo aps a execuo SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where func_sexo='F'; 576000 linhas selecionadas. Decorrido: 00:00:04.04 Plano de Execuo ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 NESTED LOOPS 2 1 NESTED LOOPS 3 2 TABLE ACCESS (FULL) OF 'CARGO' 4 2 TABLE ACCESS (FULL) OF 'FUNCIONARIO' 5 1 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' 576000 rows processed Analisando o resultado da consulta temos uma pergunta: como 576000 linhas processadas se a maior tabela possui 8000 linhas? A explicao simples, como a chave de juno no existe, o Oracle processa cada linha por cada tabela envolvida na consulta em uma operao cartesiana, realizando assim, um CARTESIAN JOIN. Vejamos na prtica: Tabela funcionrio = 8000 linhas Tabela cargo = 9 linhas Tabela departamento = 8 linhas 8000 x 9 x 8 = 576000 linhas processadas.

Interessante no? Mas o desenvolvedor distrado, percebendo o engano, reescreveu a consulta e a passou para o DBA distrado que no gerou estatsticas, no criou ndices e no sugeriu chaves estrangeiras. Vamos ver o que ocorreu na Listagem 4. Listagem 4. Consulta corrigida pelo desenvolvedor distrado e executada pelo DBA distrado. SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.DEP_id=f.DEP_id 4 and c.CAR_id=f.CAR_id 5 and func_sexo='F'; 8000 linhas selecionadas. Decorrido: 00:00:00.01 Plano de Execuo ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 MERGE JOIN 2 1 SORT (JOIN) 3 2 MERGE JOIN 4 3 SORT (JOIN) 5 4 TABLE ACCESS (FULL) OF 'CARGO' 6 3 SORT (JOIN) 7 6 TABLE ACCESS (FULL) OF 'FUNCIONARIO' 8 1 SORT (JOIN) 9 8 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' 8000 rows processed Perceba que de 4 segundos, o tempo da consulta caiu para 0,01 segundos, que faz uma bela diferena, e que a nmero de linhas processadas tambm caiu, apenas 8000 linhas. Bem melhor, no? Mas digamos que o DBA distrado resolveu gerar estatsticas. Vamos analisar agora as Listagens 5 e 6. Na gerao das estatsticas utilizaremos o comando ANALYZE. Ele no to completo quanto o pacote DBMS_STATS do Oracle, mas para o que iremos aplicar suficiente (alm de que planejo escrever um artigo s sobre DBMS_STATS no futuro). Listagem 5. Gerao de estatsticas. ANALYZE ANALYZE ANALYZE ANALYZE ANALYZE ANALYZE TABLE SQL_MAG_USER.FUNCIONARIO COMPUTE STATISTICS; TABLE SQL_MAG_USER.DEPARTAMENTO COMPUTE STATISTICS; TABLE SQL_MAG_USER.CARGO COMPUTE STATISTICS; INDEX SQL_MAG_USER.CARGO_IDX COMPUTE STATISTICS; INDEX SQL_MAG_USER.DEPARTAMENTO_IDX COMPUTE STATISTICS; INDEX SQL_MAG_USER.FUNCIONARIO_IDX COMPUTE STATISTICS;

Listagem 6. Nova execuo da mesma consulta. SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.DEP_id=f.DEP_id 4 and c.CAR_id=f.CAR_id 5 and func_sexo='F';

8000 linhas selecionadas. Decorrido: 00:00:00.01 Plano de Execuo -----------------------------------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=36 Card=5000 Bytes=120000) 1 0 HASH JOIN (Cost=36 Card=5000 Bytes=120000) 2 1 TABLE ACCESS (FULL) OF 'CARGO' (Cost=4 Card=9 Bytes=99) 3 1 HASH JOIN (Cost=31 Card=5000 Bytes=65000) 4 3 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' (Cost=4 Card=8 Bytes=16) 5 3 TABLE ACCESS (FULL) OF 'FUNCIONARIO' (Cost=26 Card=5000 Bytes=55000) 8000 rows processed Perceba a linha 1 do plano de execuo: Hash Join? Mas no era Nested Loop? A explicao para a mudana no mtodo de juno simples, como o otimizador esta configurado para o modo CHOOSE (escolha), caso a tabela j tenha estatstica gerada ele se utiliza do modo ALL_ROWS, que tem como objetivo retornar todas as linhas da consulta com o menor tempo possvel baseado nas informaes das estatsticas. No entanto, como no nosso primeiro caso ainda no tnhamos a estatstica gerada, ele usou o modo RULES (um conjunto de regras - quinze ao todo) e estas regras definem o plano de execuo. No nosso exemplo, o modo RULES achou que o mtodo de juno NESTED LOOP era melhor, j o modo ALL_ROWS achou que o mtodo de juno HASH JOIN era melhor. Um comentrio importante: a nossa consulta muito singela, por isso o mtodo de juno no fez diferena. Em consultas mais complexas, o mtodo de juno pode ser a diferena entre o sucesso e o fracasso da consulta no tocante ao tempo de execuo. E se crissemos ndices, chaves primrias e chaves estrangeiras, veramos diferena na nossa consulta? Vejamos nas Listagens 7 e 8. Os scripts abaixo criam ndices nicos, as respectivas chaves primrias e estrangeiras. Listagem 7. Criando ndices e chaves. CREATE UNIQUE INDEX SQL_MAG_USER.CARGO_IDX ON SQL_MAG_USER.CARGO (CAR_ID) LOGGING TABLESPACE INDX NOPARALLEL; CREATE UNIQUE INDEX SQL_MAG_USER.DEPARTAMENTO_IDX ON SQL_MAG_USER.DEPARTAMENTO (DEP_ID) LOGGING TABLESPACE INDX NOPARALLEL; CREATE UNIQUE INDEX SQL_MAG_USER.FUNCIONARIO_IDX ON SQL_MAG_USER.FUNCIONARIO (FUNC_ID) LOGGING TABLESPACE INDX NOPARALLEL; ALTER TABLE SQL_MAG_USER.CARGO ADD ( CONSTRAINT CAR_PK PRIMARY KEY (CAR_ID)); ALTER TABLE SQL_MAG_USER.DEPARTAMENTO ADD (

CONSTRAINT DEP_PK PRIMARY KEY (DEP_ID)); ALTER TABLE SQL_MAG_USER.FUNCIONARIO ADD ( CONSTRAINT FUNC_DEP_PK PRIMARY KEY (FUNC_ID)); ALTER TABLE SQL_MAG_USER.FUNCIONARIO ADD (CONSTRAINT CAR_FK FOREIGN KEY(CAR_ID) REFERENCES SQL_MAG_USER.CARGO(CAR_ID)) ; ALTER TABLE SQL_MAG_USER.FUNCIONARIO ADD (CONSTRAINT DEP_FK FOREIGN KEY(DEP_ID) REFERENCES SQL_MAG_USER.DEPARTAMENTO(DEP_ID)) ; Listagem 8. Executando novamente a consulta. SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.DEP_id=f.DEP_id 4 and c.CAR_id=f.CAR_id 5 and func_sexo='F'; 8000 linhas selecionadas. Decorrido: 00:00:00.01 Plano de Execuo ----------------------------------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=31 Card=5000 Bytes=120000) 1 0 NESTED LOOPS (Cost=31 Card=5000 Bytes=120000) 2 1 HASH JOIN (Cost=31 Card=5000 Bytes=110000) 3 2 TABLE ACCESS (FULL) OF 'CARGO' (Cost=4 Card=9 Bytes=99) 4 2 TABLE ACCESS (FULL) OF 'FUNCIONARIO' (Cost=26 Card=5000 Bytes=55000) 5 1 INDEX (UNIQUE SCAN) OF 'DEPARTAMENTO_IDX' (UNIQUE) 8000 rows processed Perceba na linha 1 do plano de execuo que o otimizador voltou a utilizar o NESTED LOOP e que, na linha 5, o ndice na tabela Departamento foi utilizado. Neste caso, o otimizador preferiu utilizar o ndice desconsiderando (ou no dando a devida importncia) a seletividade da consulta. Porm, tendo a cincia de que se tratava de uma tabela pequena, pois a mesma (a tabela) possua estatsticas

Tuning no Oracle Parte - 05


Pedro Valiati
Mais um teste: se quisermos forar a no utilizao do ndice, como faramos? Uma das formas e talvez a mais simples ser com um Hint (dica). Veja um exemplo na Listagem 9. Listagem 9. Utilizao de Hint. Executando novamente a consulta. SQL> select /*+ NO_INDEX(d DEPARTAMENTO_IDX ) */ FUNC_NOME,car_nome, dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.DEP_id=f.DEP_id

4 5

and c.CAR_id=f.CAR_id and func_sexo='F';

8000 linhas selecionadas. Decorrido: 00:00:00.01 Plano de Execuo ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=9 Card=5000 Bytes=120000) 1 0 HASH JOIN (Cost=9 Card=5000 Bytes=120000) 2 1 TABLE ACCESS (FULL) OF 'CARGO' (Cost=2 Card=9 Bytes=99) 3 1 HASH JOIN (Cost=6 Card=5000 Bytes=65000) 4 3 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' (Cost=2 Card=8 Bytes=16) 5 3 TABLE ACCESS (FULL) OF 'FUNCIONARIO' (Cost=3 Card=5000 Bytes=55000) 8000 rows processed Conforme planejvamos. O hint NO_INDEX d a dica ao otimizador de no utilizar determinado ndice. Esta tcnica pode trazer timos benefcios (quando temos total certeza do que estamos fazendo) ou grandes tragdias (como neste caso). Veja que o otimizador voltou a utilizar um Full Table Scan na tabela Departamento. claro que, neste exemplo, o impacto no to grande, mas imagine em um modelo mais complexo. Tal modelo mais complexo, inclusive, demonstrando o impacto de uma modelagem mal arquitetada, ser visto na parte 3 do artigo com direito a tabelas bem mais populadas, cerca de 10 milhes de registros e melhorias bem significantes no tocante ao tempo de execuo das consultas. Vamos para o nosso segundo exemplo. Primeiramente, vamos excluir toda a estrutura (Listagem 10) para que no existam ndices, estatsticas ou qualquer outro fator que possa mascarar nossa analise (o comando DROP USER ir excluir o esquema, incluindo todos os objetos, estatsticas, etc). Aps esta etapa, basta recriar o ambiente (Listagem 2). Listagem 10. Excluindo o esquema SQL_MAG_USER. DROP USER SQL_MAG_USER CASCADE; Testaremos uma consulta que retorna apenas uma linha, inicialmente sem ndices, chaves ou estatsticas nas tabelas e analisaremos o comportamento da mesma nos diversos estados (Listagem 11). Listagem 11. Consulta que deveria retornar apenas uma linha. SQL> select FUNC_NOME, car_nome, dep_nome 2 from sql_mag_user.departamento d, sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where func_nome='Luis'; 72 linhas selecionadas. Decorrido: 00:00:00.01 Plano de Execuo ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 NESTED LOOPS 2 1 NESTED LOOPS 3 2 TABLE ACCESS (FULL) OF 'CARGO' 4 2 TABLE ACCESS (FULL) OF 'FUNCIONARIO'

TABLE ACCESS (FULL) OF 'DEPARTAMENTO'

72 rows processed Rodamos a consulta sem as chaves de juno, e o que temos? 72 linhas retornadas. Isso por que o Oracle realizou um CARTESIAN JOIN com o nico campo que interessava na tabela funcionrio. Analisemos: Tabela funcionrio = 1 linha processada Tabela cargo = 9 linhas processadas Tabela departamento = 8 linhas processadas Ou seja, 1 x 9 x 8 = 72. Vamos analisar agora o comportamento do otimizador executando a mesma consulta com as chaves de juno (Listagem 12). Listagem 12. Utilizando as chaves de juno. SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.dep_id=f.dep_id 4 and c.car_id=f.car_id 5 AND func_nome='Luis'; Decorrido: 00:00:00.00 Plano de Execuo ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 MERGE JOIN 2 1 SORT (JOIN) 3 2 MERGE JOIN 4 3 SORT (JOIN) 5 4 TABLE ACCESS (FULL) OF 'CARGO' 6 3 SORT (JOIN) 7 6 TABLE ACCESS (FULL) OF 'FUNCIONARIO' 8 1 SORT (JOIN) 9 8 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' 1 rows processed Bem diferente... O ponto chave foi a utilizao do Merge Join como mtodo de juno. Esses exemplos so interessantes para percebermos que poderemos alterar o mtodo de juno, bem como o plano de execuo de consultas fazendo alteraes no cdigo da consulta sem lanar mo de Hints ou outro artifcio. Vamos gerar as estatsticas nas tabelas, executar a consulta e analisar seu comportamento. Primeiramente, execute o script da Listagem 5 para gerar as estatsticas e, aps isso, v para a Listagem 13. Listagem 13. Utilizando as chaves de juno aps gerao das estatsticas (Listagem 5). SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.dep_id=f.dep_id 4 and c.car_id=f.car_id 5 AND func_nome='Luis';

Decorrido: 00:00:00.01 Plano de Execuo --------------------------------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=9 Card=1111 Bytes=35552) 1 2 3 4 5 0 1 1 3 3 HASH JOIN (Cost=9 Card=1111 Bytes=35552) TABLE ACCESS (FULL) OF 'CARGO' (Cost=2 Card=9 Bytes=99) HASH JOIN (Cost=6 Card=1111 Bytes=23331) TABLE ACCESS (FULL) OF 'DEPARTAMENTO' (Cost=2 Card=8 Bytes=88) TABLE ACCESS (FULL) OF 'FUNCIONARIO' (Cost=3 Card=1111 Bytes=11110)

Com as informaes do otimizador, o plano de execuo alterou o mtodo de juno para HASH JOIN, ou seja, como o otimizador no achou o ndice, e tinha a informao de que se tratava de um nmero relativamente grande de linhas para tratar ele achou que o Hash Join seria melhor do que o Merge Join. Vamos criar os ndices e chaves e executar a consulta novamente. Para criar os ndices e chaves, execute o script da Listagem 7 e, aps isso, o script da Listagem 14. Listagem 14. Execuo da consulta aps a criao dos ndices (Listagem 7). SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d,sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.dep_id=f.dep_id 4 and c.car_id=f.car_id 5 AND func_nome='Luis'; Decorrido: 00:00:00.01 Plano de Execuo --------------------------------------------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=88 Card=1111 Bytes=35552) 1 0 HASH JOIN (Cost=88 Card=1111 Bytes=35552) 2 1 TABLE ACCESS (FULL) OF 'CARGO' (Cost=3 Card=9 Bytes=99) 3 1 HASH JOIN (Cost=84 Card=1111 Bytes=23331) 4 3 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' (Cost=3 Card=8 Bytes=88) 5 3 TABLE ACCESS (BY INDEX ROWID) OF 'FUNCIONARIO' (Cost=80 Card=1111 Bytes=11110) 6 5 INDEX (FULL SCAN) OF 'FUNCIONARIO_IDX' (UNIQUE) (Cost=21 Card=10000) Conforme esperado, o otimizador notou a presena do ndice e percebeu que pela seletividade da tabela funcionrio, a qual seleciona apenas uma linha, era mais interessante utiliz-lo. Mas ainda resta mais um teste, o parmetro DB_FILE_MULTIBLOCK_READ_COUNT. Vamos ver se ele capaz de alterar um plano de execuo. Esse parmetro encontra-se no valor de 8, vamos alter-lo para 32 e ver o que acontece (Listagem 15). Listagem 15. Alterando o parmetro DB_FILE_MULTIBLOCK_READ_COUNT e executando a consulta novamente. ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=32; SQL> select FUNC_NOME,car_nome,dep_nome 2 from sql_mag_user.departamento d, sql_mag_user.funcionario f, sql_mag_user.cargo c 3 where d.dep_id=f.dep_id

4 5

and c.car_id=f.car_id AND func_nome='Luis';

Decorrido: 00:00:00.00 Plano de Execuo ----------------------------------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=25 Card=1111 Bytes=35552) 1 0 HASH JOIN (Cost=25 Card=1111 Bytes=35552) 2 1 TABLE ACCESS (FULL) OF 'CARGO' (Cost=3 Card=9 Bytes=99) 3 1 HASH JOIN (Cost=21 Card=1111 Bytes=23331) 4 3 TABLE ACCESS (FULL) OF 'DEPARTAMENTO' (Cost=3 Card=8 Bytes=88) 5 3 TABLE ACCESS (FULL) OF 'FUNCIONARIO' (Cost=17 Card=1111 Bytes=11110) 1 row processed Mais uma forma de alterarmos um plano de execuo atravs de parmetros do Oracle. Ora, se o parmetro DB_FILE_MULTIBLOCK_READ_COUNT diz para o otimizador a quantidade de blocos que o Oracle consegue ler de uma s vez e esse parmetro esta relativamente alto (32), o otimizador poder perfeitamente pensar se eu posso ler uma quantidade alta de blocos de uma s vez, pra que eu quero um ndice? muito mais rpido ler estes blocos.... Faz sentido no acha? Concluso No prximo artigo, veremos mais dois estudos de caso e alguma teoria, mas desta vez com tabelas bem maiores e com erros de modelagem (tudo pela didtica). Analisando sempre impactos e alternativas, s vezes sob o lema dos males o menor. Veremos tambm o impacto de um ambiente criado de maneira errada e veremos como aliviar os problemas gerados.