terça-feira, 29 de junho de 2010

Otimizando uma consulta sem alterar o código fonte

Objetivo:


Estamos realizando um trabalho para melhorar o desempenho de uma aplicação e identificamos que existe uma consulta que é a maior responsável pelo baixo desempenho desta, analisando a consulta verificamos que é possivel melhorar a performace, mas para isso precisamos alterar o código da aplicação. Essa aplicação é um pacote de um fornecedor e não temos acesso ao código fonte, uma possível solução para esse impasse seria a utilização do pacote “DBMS_ADVANCED_REWRITE” que permite a alteração de uma consulta sem alteração do código fonte da aplicação.


Ambiente:


SO: Linux 2.6.9-67.0.0.0.1.EL i686 i386 GNU/Linux
Base: Oracle Database 10.2.0.5.0 com archive log habilitado


Overview:


O pacote DBMS_ADVANCED_REWRITE permite interceptar instruções SQL específicas e substituí-las com instruções SQL alternativa. Este recurso pode ser muito útil quando precisamos modificar uma instrução SQL visando melhoria de performace e não dispomos do código fonte da aplicação. Infelizmente este recurso tem algumas limitações e em alguns casos não podemos utilizá-lo:

  • Recurso disponível a partir da versão Oracle 10g;
  • Não funciona com instrução DML, somente com instrução SELECT;
  • Não funciona com “Bind Variables” (Metalink DOC ID: 392214.1);

Tópicos:

  1. Previlégios necessários para o usuário;
  2. Criar uma tabela de teste;
  3. Plano de execução da consulta original;
  4. Plano de execução da consulta otimizada;
  5. Utilizar o pacote para estabelecer a equivalência;
  6. Verificar se equivalência esta funcionando;


1. Previlégios necessários para o usuário

SQL>
SQL> conn sys/oracle@lab1 as sysdba;
Conectado.
SQL> GRANT EXECUTE ON DBMS_ADVANCED_REWRITE TO appl02;

Concessão bem-sucedida.

SQL> GRANT CREATE MATERIALIZED VIEW TO appl02;

Concessão bem-sucedida.

SQL>


2. Criar uma tabela de teste

SQL>
SQL> conn appl02/oracle@lab1;
Conectado.
SQL> create table db_objetos as select * from all_objects;

Tabela criada.

SQL> create index db_obj_idx on db_objetos(OBJECT_NAME);

Índice criado.

SQL>


3. Plano de execução da consulta original

Agora vamos verificar o plano de execução de uma consulta que supostamente estaria causando grande impacto de performace na base de dados:

SQL>
SQL> select OWNER, OBJECT_NAME, OBJECT_TYPE
from db_objetos
where upper(OBJECT_NAME) = upper('all_objects');

OWNER OBJECT_NAME OBJECT_TYPE
------------------------------ ------------------------------ -----------------
SYS ALL_OBJECTS VIEW
PUBLIC ALL_OBJECTS SYNONYM

SQL> Explain plan for
2 select OWNER, OBJECT_NAME, OBJECT_TYPE
3 from db_objetos
4 where upper(OBJECT_NAME) = upper('all_objects');

Explicado.

SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 440180848

--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 270 | 133 (3)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| DB_OBJETOS | 6 | 270 | 133 (3)| 00:00:02 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(UPPER("OBJECT_NAME")='ALL_OBJECTS')

Note
-----
- dynamic sampling used for this statement

17 linhas selecionadas.
SQL>


4. Plano de execução da consulta otimizada

Vamos supor que após uma analise na tabela “DB_OBJETOS” verificamos que o campo “OBJECT_NAME” possui somente textos com caracteres maiusculos, neste caso a consulta abaixo forneceria o mesmo resultado e utilizaria o indice desse campo, melhorando a performace da consulta:

SQL>
SQL> select OWNER, OBJECT_NAME, OBJECT_TYPE
from db_objetos
where OBJECT_NAME = upper('all_objects');

OWNER OBJECT_NAME OBJECT_TYPE
------------------------------ ------------------------------ -------------------
SYS ALL_OBJECTS VIEW
PUBLIC ALL_OBJECTS SYNONYM

SQL> Explain plan for
2 select OWNER, OBJECT_NAME, OBJECT_TYPE
3 from db_objetos
4 where OBJECT_NAME = upper('all_objects');

Explicado.

SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash value: 1912010479

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 90 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DB_OBJETOS | 2 | 90 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | DB_OBJ_IDX | 2 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("OBJECT_NAME"='ALL_OBJECTS')

Note
-----
- dynamic sampling used for this statement

18 linhas selecionadas.
SQL>


5. Utilizar o pacote para estabelecer a equivalência

Agora vamos utilizar o pacote DBMS_ADVANCED_REWRITE para estabelecer a equivalência de forma que sempre que a consulta original for acionada a base Oracle utilize a consulta otimizada para execução:

SQL>
SQL> ALTER SESSION SET QUERY_REWRITE_INTEGRITY = TRUSTED;

Sessão alterada.

SQL> BEGIN
2 sys.dbms_advanced_rewrite.declare_rewrite_equivalence (
3 name => 'test_rw1',
4 source_stmt =>
5 'select OWNER, OBJECT_NAME, OBJECT_TYPE
6 from db_objetos
7 where upper(OBJECT_NAME) = upper(''all_objects'')',
8 destination_stmt =>
9 'select OWNER, OBJECT_NAME, OBJECT_TYPE
10 from db_objetos
11 where OBJECT_NAME = upper(''all_objects'')',
12 validate => false,
13 rewrite_mode => 'text_match');
14 END;
15 /

Procedimento PL/SQL concluído com sucesso.


SQL>


6. Verificar se equivalência esta funcionando

Na execução abaixo podemos constatar que apesar de acionar-mos a consulta original o plano de execução utilizado pela base Oracle é da consulta otimizada:

SQL>
SQL> Explain plan for
2 select OWNER, OBJECT_NAME, OBJECT_TYPE
3 from db_objetos
4 where upper(OBJECT_NAME) = upper('all_objects');

Explicado.

SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash value: 1912010479

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 90 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| DB_OBJETOS | 2 | 90 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | DB_OBJ_IDX | 2 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("OBJECT_NAME"='ALL_OBJECTS')

Note
-----
- dynamic sampling used for this statement

18 linhas selecionadas.
SQL>

Nenhum comentário:

Postar um comentário