viernes, 19 de diciembre de 2008

Estas bromeando, ¿afinar sin tener el código fuente?

Es sabido por todo DBA que los problemas de desempeño son en gran medida atribuibles a código ineficiente, pero allí nos tienen recibiendo las quejas por lo lenta que es la base de datos; no nos queda más que encontrar este código y proceder a corregirlo, pero ¿qué podemos hacer si no tenemos siquiera acceso al código fuente? Lo más que podíamos hacer era hacer ajustes a las estadísticas o a algunos parámetros, quizás crear algún índice, pero nada más, afortunadamente con Oracle 10g se nos presenta una poderosa herramienta para situaciones como ésta y como siempre, nada mejor para entender cómo funciona que mediante un ejemplo práctico.

Imaginemos que luego de hacer las inspecciones del caso encontramos que el query problemático es el siguiente:

select e.ename, d.dname 
from emp e, dept d 
where e.deptno = to_number(d.deptno)

--------------------------------------------------------------
| Id | Operation          | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------
|  0 | SELECT STATEMENT   |      |   14 |   308 |     7  (15)|
|* 1 |  HASH JOIN         |      |   14 |   308 |     7  (15)|
|  2 |   TABLE ACCESS FULL| DEPT |    4 |    52 |     3   (0)|
|  3 |   TABLE ACCESS FULL| EMP  |   14 |   126 |     3   (0)|
--------------------------------------------------------------

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

   1 - access("E"."DEPTNO"=TO_NUMBER(TO_CHAR("D"."DEPTNO")))

La tabla dept tiene como clave primaria a la columna deptno, pero el índice asociado no se está usando debido a que se le está aplicando el operador to_number a dicha columna. Si tuviésemos acceso al código procederíamos a remover este operador para que quede algo así:

select e.ename, d.dname 
from emp e, dept d 
where e.deptno = d.deptno

--------------------------------------------------------------------------
| Id | Operation                    | Name    | Rows | Bytes | Cost(%CPU)|
--------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |         |   14 |   308 |     4  (0)|
|  1 |  NESTED LOOPS                |         |   14 |   308 |     4  (0)|
|  2 |   TABLE ACCESS FULL          | EMP     |   14 |   126 |     3  (0)|
|  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |    1 |    13 |     1  (0)|
|* 4 |    INDEX UNIQUE SCAN         | PK_DEPT |    1 |       |     0  (0)|
--------------------------------------------------------------------------

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

   4 - access("E"."DEPTNO"="D"."DEPTNO")

Como no tenemos acceso al código lo más que podríamos hacer es crear un índice funcional para to_number(deptno), pero coincidirán en que es una solución poco elegante y eficiente, más aun cuando Oracle nos ofrece la solución perfecta por medio del nuevo package
dbms_advanced_rewrite, el cual nos permite cambiar una sentencia por otra de forma transparente.

Para empezar a usarlo debemos en primer lugar otorgar los privilegios requeridos al usuario que emplearemos para hacer este mapeo, que para nuestro ejemplo será el usuario scott.

SYS@orcl> grant execute on sys.dbms_advanced_rewrite to scott;

Grant succeeded.

SYS@orcl> grant create materialized view to scott;

Grant succeeded.

A continuación hacemos uso del procedimiento declare_rewrite_equivalence al interior de dbms_advanced_rewrite, debemos proporcionar un nombre único, la sentencia original y la sentencia por la cual deseamos cambiarla.

SCOTT@orcl> begin
  2  sys.dbms_advanced_rewrite.declare_rewrite_equivalence(
  3    name => 'use_index',
  4    source_stmt => 'select e.ename, d.dname from emp e, dept d 
  5      where e.deptno = to_number(d.deptno)',
  6    destination_stmt => 'select e.ename, d.dname from emp e, dept d 
  7      where e.deptno = d.deptno');
  8  end;
  9  /

PL/SQL procedure successfully completed.

Deben tener en cuenta que ambas sentencias deben coincidir en la cantidad de columnas y en los tipos de datos o de otra forma tendrian un error similar a este:

ERROR at line 1:
ORA-30390: the source statement is not equivalent to the destination statement
ORA-06512: at "SYS.DBMS_ADVANCED_REWRITE", line 29
ORA-06512: at "SYS.DBMS_ADVANCED_REWRITE", line 185
ORA-06512: at line 2

Pues bien, veamos ahora si es verdad tanta belleza, para lo cual ejecutamos nuevamente el código original.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d
  2  where e.deptno = to_number(d.deptno);

ENAME      DNAME
---------- --------------
SMITH      RESEARCH
ALLEN      SALES
WARD       SALES
JONES      RESEARCH
MARTIN     SALES
BLAKE      SALES
CLARK      ACCOUNTING
SCOTT      RESEARCH
KING       ACCOUNTING
TURNER     SALES
ADAMS      RESEARCH
JAMES      SALES
FORD       RESEARCH
MILLER     ACCOUNTING

14 rows selected.

--------------------------------------------------------------
| Id | Operation          | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------
|  0 | SELECT STATEMENT   |      |   14 |   308 |     7  (15)|
|* 1 |  HASH JOIN         |      |   14 |   308 |     7  (15)|
|  2 |   TABLE ACCESS FULL| DEPT |    4 |    52 |     3   (0)|
|  3 |   TABLE ACCESS FULL| EMP  |   14 |   126 |     3   (0)|
--------------------------------------------------------------

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

   1 - access("E"."DEPTNO"=TO_NUMBER(TO_CHAR("D"."DEPTNO")))

Como diría Condorito: ¡exijo una explicación! Simple, el parámetro query_rewrite_integrity controla las posibilidades de reemplazar una sentencia por otra, y por defecto tiene el valor enforced y para nuestro caso es requerido que sea trusted. Hagamos el cambio y reintentemos con nuestro query anterior.

SCOTT@orcl> alter session set query_rewrite_integrity=trusted;

Session altered.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d 
  2  where e.deptno = to_number(d.deptno);

ENAME      DNAME
---------- --------------
SMITH      RESEARCH
ALLEN      SALES
WARD       SALES
JONES      RESEARCH
MARTIN     SALES
BLAKE      SALES
CLARK      ACCOUNTING
SCOTT      RESEARCH
KING       ACCOUNTING
TURNER     SALES
ADAMS      RESEARCH
JAMES      SALES
FORD       RESEARCH
MILLER     ACCOUNTING

14 rows selected.

--------------------------------------------------------------------------
| Id | Operation                    | Name    | Rows | Bytes | Cost(%CPU)|
--------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |         |   14 |   308 |     4  (0)|
|  1 |  NESTED LOOPS                |         |   14 |   308 |     4  (0)|
|  2 |   TABLE ACCESS FULL          | EMP     |   14 |   126 |     3  (0)|
|  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |    1 |    13 |     1  (0)|
|* 4 |    INDEX UNIQUE SCAN         | PK_DEPT |    1 |       |     0  (0)|
--------------------------------------------------------------------------

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

   4 - access("E"."DEPTNO"="D"."DEPTNO")

¡Finalmente funcionó!, vemos como Oracle ha reemplazado un query por otro y ahora sí se usa la clave primaria de la tabla dept. Pero qué tal si éste no es el único código con problemas sino que hay otros similares, ¿se hará o no la transformación? pues hagamos la prueba.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d 
  2  where e.deptno = to_number(d.deptno)
  3  and e.ename = 'KING';

ENAME      DNAME
---------- --------------
KING       ACCOUNTING

---------------------------------------------------------------
| Id | Operation          | Name | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------
|  0 | SELECT STATEMENT   |      |     1 |    22 |     7  (15)|
|* 1 |  HASH JOIN         |      |     1 |    22 |     7  (15)|
|* 2 |   TABLE ACCESS FULL| EMP  |     1 |     9 |     3   (0)|
|  3 |   TABLE ACCESS FULL| DEPT |     4 |    52 |     3   (0)|
---------------------------------------------------------------

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

   1 - access("E"."DEPTNO"=TO_NUMBER(TO_CHAR("D"."DEPTNO")))
   2 - filter("E"."ENAME"='KING')

¡Ya no funciona! ¿Quiere decir que tendremos que crear tantos mapeos como sentencias hayan? Afortunadamente no, es cuestión de crear el mapeo indicándole a Oracle que no sea tan estricto, para ello usamos el parámetro rewrite_mode cambiándolo de su valor por defecto text_match por el valor general. Primero eliminemos el mapeo que habíamos creado y lo creamos nuevamente con el ajuste requerido.

SCOTT@orcl> execute sys.dbms_advanced_rewrite.drop_rewrite_equivalence('use_index')

PL/SQL procedure successfully completed.

SCOTT@orcl> begin
  2  sys.dbms_advanced_rewrite.declare_rewrite_equivalence(
  3    name => 'use_index',
  4    source_stmt => 'select e.ename, d.dname from emp e, dept d
  5      where e.deptno = to_number(d.deptno)',
  6    destination_stmt => 'select e.ename, d.dname from emp e, dept d
  7      where e.deptno = d.deptno',
  8    rewrite_mode => 'GENERAL');
  9  end;
 10  /

PL/SQL procedure successfully completed.

Ahora reintentamos con el query anterior.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d
  2  where e.deptno = to_number(d.deptno)
  3  and e.ename = 'KING';

ENAME      DNAME
---------- --------------
KING       ACCOUNTING

---------------------------------------------------------------------------
| Id | Operation                    | Name    | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |         |    1 |    22 |     4   (0)|
|  1 |  NESTED LOOPS                |         |    1 |    22 |     4   (0)|
|* 2 |   TABLE ACCESS FULL          | EMP     |    1 |     9 |     3   (0)|
|  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |    1 |    13 |     1   (0)|
|* 4 |    INDEX UNIQUE SCAN         | PK_DEPT |    1 |       |     0   (0)|
---------------------------------------------------------------------------

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

   2 - filter("E"."ENAME"='KING')
   4 - access("E"."DEPTNO"="D"."DEPTNO")

¡Bien! Las cosas vuelven a la normalidad, Oracle hace nuevamente el cambio aún cuando el código proporcionado no es idéntico al indicado durante el mapeo, pero vayamos un poco más allá, hagamos la prueba usando bind variables, después de todo estamos conscientes de que usar literales usualmente no es una buena práctica, ¿seguirá Oracle haciendo el cambio?

SCOTT@orcl> variable ename varchar2(14)
SCOTT@orcl> execute :ename := 'KING'

PL/SQL procedure successfully completed.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d 
  2  where e.deptno = to_number(d.deptno)
  3  and e.ename = :ename;

ENAME      DNAME
---------- --------------
KING       ACCOUNTING

---------------------------------------------------------------------------
| Id | Operation                    | Name    | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |         |    1 |    22 |     4   (0)|
|  1 |  NESTED LOOPS                |         |    1 |    22 |     4   (0)|
|* 2 |   TABLE ACCESS FULL          | EMP     |    1 |     9 |     3   (0)|
|  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |    1 |    13 |     1   (0)|
|* 4 |    INDEX UNIQUE SCAN         | PK_DEPT |    1 |       |     0   (0)|
---------------------------------------------------------------------------

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

   2 - filter("E"."ENAME"=:ENAME)
   4 - access("E"."DEPTNO"="D"."DEPTNO")

¡Perfecto! Todo está saliendo a pedir de boca, pero sigamos forzando la situación y démosle a Oracle un nuevo query para ponerlo a prueba.

SCOTT@orcl> variable empno number
SCOTT@orcl> execute :empno := 7839

PL/SQL procedure successfully completed.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d 
  2  where e.deptno = to_number(d.deptno)
  3  and e.ename = :ename
  4  and e.empno = :empno;

ENAME      DNAME
---------- --------------
KING       ACCOUNTING

--------------------------------------------------------------------------
| Id | Operation                    | Name   | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |        |    1 |    26 |     4   (0)|
|  1 |  NESTED LOOPS                |        |    1 |    26 |     4   (0)|
|* 2 |   TABLE ACCESS BY INDEX ROWID| EMP    |    1 |    13 |     1   (0)|
|* 3 |    INDEX UNIQUE SCAN         | PK_EMP |    1 |       |     0   (0)|
|* 4 |   TABLE ACCESS FULL          | DEPT   |    1 |    13 |     3   (0)|
--------------------------------------------------------------------------

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

   2 - filter("E"."ENAME"=:ENAME)
   3 - access("E"."EMPNO"=TO_NUMBER(:EMPNO))
   4 - filter("E"."DEPTNO"=TO_NUMBER(TO_CHAR("D"."DEPTNO")))

¿Qué pasó? Esto ya parece una telenovela pero afortunadamente la explicación es bastante sencilla: para que la transformación pueda realizarse, se debe hacer referencia a las columnas que están en la sentencia original y si observamos con detenimiento, la columna empno no aparece en ella. Hagamos entonces un mapeo en el que si aparezca, y es más haremos que no sea explícito sino implícito.

SCOTT@orcl> execute sys.dbms_advanced_rewrite.drop_rewrite_equivalence('use_index')

PL/SQL procedure successfully completed.

SCOTT@orcl> begin
  2  sys.dbms_advanced_rewrite.declare_rewrite_equivalence (
  3    name => 'use_index',
  4    source_stmt => 'select e.*, d.dname from emp e, dept d
  5      where e.deptno = to_number(d.deptno)',
  6    destination_stmt => 'select e.*, d.dname from emp e, dept d 
  7      where e.deptno = d.deptno',
  8    rewrite_mode => 'GENERAL');
  9  end; 
 10  /

PL/SQL procedure successfully completed.

Ahora sí, al indicar e.* allí estará desde luego la columna empno, hagamos nuevamente la prueba.

SCOTT@orcl> select e.ename, d.dname from emp e, dept d 
  2  where e.deptno = to_number(d.deptno)
  3  and e.ename = :ename
  4  and e.empno = :empno;

ENAME      DNAME
---------- --------------
KING       ACCOUNTING

---------------------------------------------------------------------------
| Id | Operation                    | Name    | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |         |    1 |    26 |     2   (0)|
|  1 |  NESTED LOOPS                |         |    1 |    26 |     2   (0)|
|* 2 |   TABLE ACCESS BY INDEX ROWID| EMP     |    1 |    13 |     1   (0)|
|* 3 |    INDEX UNIQUE SCAN         | PK_EMP  |    1 |       |     0   (0)|
|  4 |   TABLE ACCESS BY INDEX ROWID| DEPT    |    4 |    52 |     1   (0)|
|* 5 |    INDEX UNIQUE SCAN         | PK_DEPT |    1 |       |     0   (0)|
---------------------------------------------------------------------------

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

   2 - filter("E"."ENAME"=:ENAME)
   3 - access("E"."EMPNO"=TO_NUMBER(:EMPNO))
   5 - access("E"."DEPTNO"="D"."DEPTNO")

Simplemente ¡magnífico!. No tuvimos que escribir en nuestro query e.* sino que escogimos algunas de las columnas de emp como son e.ename y e.empno y Oracle no encontró problemas en hacer la conversión.

Qué lástima, justo cuando la cosa se ponía interesante llegamos al final de esta demostración, pero espero que les haya motivado a investigar más sobre el tema y desde luego agreguen a dbms_advanced_rewrite a su arsenal de armas en la eterna guerra contra el código ineficiente, ¡suerte en la batalla!

Posts Relacionados:
Siga leyendo >>

¿Te pareció interesante este artículo?, ¿te quedaron algunas dudas?, ¿quieres sugerirme un tema a tratar?, pues déjame tus comentarios o envíame un email y para que NO te pierdas ningún Post, suscríbete por email ahora mismo!

martes, 2 de diciembre de 2008

Nuevas formas de rastrear el código

Una de las tareas habituales del DBA es hacer seguimiento al código, para ello normalmente se identificaba la sesión deseada y se habilitaba el trace para la misma, pero ahora que prevalecen las aplicaciones web, ¿cómo hacer seguimiento cuando existe un pool de conexiones que es compartido por todos los usuarios? Pues si hemos tomado la precaución de instrumentar nuestro código, tal como les recomendara en un Post previo, la tarea aún es posible y nada complicada, como veremos a continuación.

Primero estableceremos el escenario que nos servirá para demostrar cómo hacer seguimiento, para ellos asumiremos la existencia de un usuario de base de datos llamado "appweb", tendremos 2 sesiones en sql*plus desde las cuales ejecutaremos un par de scripts que contienen el código que nos interesa capturar.

script1.sql
declare
   v_dummy number;
begin
   dbms_session.set_identifier( client_id => 'juan.perez' );
   dbms_application_info.set_module(
      module_name => 'Agregacion',
      action_name => 'Empleados');

   select count(*)
     into v_dummy
     from emp;

   dbms_application_info.set_module(
      module_name => 'Agregacion',
      action_name => 'Departamentos');

   select count(*)
     into v_dummy
     from dept;

   dbms_application_info.set_module(
      module_name => NULL,
      action_name => NULL );
   dbms_session.set_identifier( client_id => NULL );
end;
/

script2.sql
declare
   v_dummy varchar2(20);
begin
   dbms_session.set_identifier( client_id => 'john.doe' );
   dbms_application_info.set_module(
      module_name => 'Primeros',
      action_name => 'Empleados');

   select ename
     into v_dummy
     from emp
    where rownum < 2;

   dbms_application_info.set_module(
      module_name => 'Primeros',
      action_name => 'Departamentos');

   select dname
     into v_dummy
     from dept
    where rownum < 2;

   dbms_application_info.set_module(
      module_name => NULL,
      action_name => NULL );
   dbms_session.set_identifier( client_id => NULL );
end;
/

El uso de dbms_application_info ya nos es conocido, si no es así te recomiendo leer primero este Post en el cual lo detallo; la novedad viene a ser el uso de dbms_session.set_identifier, que sirve para asignar una cadena de hasta 64 caracteres y que es visible en v$session bajo la columna client_identifier.

SYS@orcl > execute dbms_session.set_identifier( 'Juan.Perez' )

PL/SQL procedure successfully completed.

SYS@orcl > select username, client_identifier 
  2  from v$session where client_identifier is not null;

USERNAME CLIENT_IDENTIFIER
-------- -----------------
SYS      Juan.Perez

En una aplicación web, cada vez que un usuario desea interactuar con la base de datos, se solicita una conexión disponible del pool de conexiones y una vez obtenida se ejecuta el código SQL, pues bien, para poder identificar al usuario, es necesario agregar una llamada a dbms_session.client_identifier, tal como pueden ver en el código de script1 y script2.

Si deseamos hacer trace al usuario 'juan.perez', que es un usuario de aplicación, no un usuario de la base de datos, podemos hacer uso del package dbms_monitor. Este package aparece con 10g y ha venido a reemplazar a dbms_system y dbms_support, por lo que si aún los usan les recomiendo dejar de hacerlo.

SYS@orcl > execute dbms_monitor.client_id_trace_enable( 'juan.perez' )

PL/SQL procedure successfully completed.

Ahora simularemos la interacción de una aplicación web, ejecutando los scripts desde dos sesiones establecidas con el usuario appweb, que vendría a ser el usuario del pool de conexiones.

APPWEB(1)@orcl > @script1

PL/SQL procedure successfully completed.

APPWEB(2)@orcl > @script2

PL/SQL procedure successfully completed.

Ya que Oracle ha recibido la instrucción de hacer seguimiento al código que sea ejecutado por 'juan.perez', conforme esto ocurra se iran generando trace files en el directorio señalado en user_dump_dest. Aquí no podemos usar directamente tkprof, debemos ejecutar un paso previo que consiste en llamar a trcsess.

[udump]$ trcsess
oracle.ss.tools.trcsess.SessTrcException: SessTrc-00002: Session Trace Usage error: Wrong parameters passed.
trcsess [output=<output file name>]  [session=<session ID>] [clientid=<clientid>] [service=<service name>] [action=<action name>] [module=<module name>] <trace file names>

output=<output file name> output destination default being standard output.
session=<session Id> session to be traced.
Session id is a combination of session Index & session serial number e.g. 8.13.
clientid=<clientid> clientid to be traced.
service=<service name> service to be traced.
action=<action name> action to be traced.
module=<module name> module to be traced.
<trace_file_names> Space separated list of trace files with wild card '*' supported.

Vemos que trcess nos permite separar de entre muchos trace files las sentencias que vamos filtrando con los parámetros disponibles. Empezaremos por filtrar las sentencias ejecutadas por 'juan.perez'.

[udump]$ ls *trc
orcl_ora_6839.trc  orcl_ora_6901.trc
[udump]$ trcsess output=juan.perez.trc clientid='juan.perez' *trc
[udump]$ tkprof juan.perez.trc juan.perez.txt sys=no

TKPROF: Release 10.2.0.4.0 - Production on Mon Dec 1 08:10:21 2008

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

juan.perez.txt
SELECT COUNT(*)
FROM
 EMP
SELECT COUNT(*)
FROM
 DEPT

El objetivo se ha cumplido, hemos logrado aislar las sentencias que corresponden a 'juan.perez', procedamos ahora a solicitar el seguimiento de las sentencias generadas por el módulo 'Primeros'.

SYS@orcl > execute dbms_monitor.serv_mod_act_trace_enable(-
> service_name => 'orcl.oracle.com',-
> module_name => 'Primeros' );

PL/SQL procedure successfully completed.

Ahora simularemos nuevamente la interacción con la base de datos y obtendremos de los trace files aquellas sentencias que nos interesan por corresponder a dicho módulo.

APPWEB(1)@orcl > @script1

PL/SQL procedure successfully completed.

APPWEB(2)@orcl > @script2

PL/SQL procedure successfully completed.

[udump]$ trcsess output=Primeros.trc module='Primeros' *trc
[udump]$ tkprof Primeros.trc Primeros.txt sys=no

TKPROF: Release 10.2.0.4.0 - Production on Mon Dec 1 08:37:37 2008

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Primeros.txt
SELECT ENAME
FROM
 EMP WHERE ROWNUM < 2

SELECT DNAME
FROM
 DEPT WHERE ROWNUM < 2

Las cosas marchan como se esperaba, trcess logró separar las sentencias que corresponden al módulo 'Primeros'. De manera similar lo podemos solicitar a nivel de acción, veamos el ejemplo para 'Departamentos'.

[udump]$ trcsess output=Departamentos.trc action='Departamentos' *trc
[udump]$ tkprof Departamentos.trc Departamentos.txt sys=no

TKPROF: Release 10.2.0.4.0 - Production on Mon Dec 1 08:41:19 2008

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Departamentos.txt
SELECT DNAME
FROM
 DEPT WHERE ROWNUM < 2

SELECT COUNT(*)
FROM
 DEPT

Hasta ahora todo bien y muy simple, veamos ahora un ejemplo final en el que se combinan varios criterios de filtrado.

[udump]$ trcsess output=john.doe.Empleados.trc clientid='john.doe' action='Empleados' *trc

[udump]$ tkprof john.doe.Empleados.trc john.doe.Empleados.txt sys=no

TKPROF: Release 10.2.0.4.0 - Production on Mon Dec 1 08:44:07 2008

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

john.doe.Empleados.txt
SELECT ENAME
FROM
 EMP WHERE ROWNUM < 2

A estas alturas ya habrán notado la enorme ventaja que nos representa haber instrumentado las aplicaciones, así como el poder de dbms_monitor complementado con trcess. Ahora bien, conforme vamos agregando criterios de seguimiento, estos se van registrando en la base de datos y están disponibles con la vista dba_enabled_traces.

SYS@orcl > select trace_type, primary_id, qualifier_id1
  2  from dba_enabled_traces;

TRACE_TYPE       PRIMARY_ID        QUALIFIER_ID1
---------------- ----------------- -------------
CLIENT_ID        juan.perez
SERVICE_MODULE   orcl.oracle.com   Primeros

Recuerden que hacer seguimiento consume recursos, por lo que es conveniente que consulten dba_enabled_traces regularmente para ver que no esté activo algún seguimiento que ya no necesitan y de ser así procedan a desactivarlo.

SYS@orcl > execute dbms_monitor.client_id_trace_disable('juan.perez')

PL/SQL procedure successfully completed.

SYS@orcl > execute dbms_monitor.serv_mod_act_trace_disable(-
> service_name => 'orcl.oracle.com',-
> module_name => 'Primeros' );

PL/SQL procedure successfully completed.

En conclusión, para estos tiempos en que las aplicaciones web con sus pools de conexiones son la regla, la labor de seguimiento del código se puede complicar si no hemos tomado la previsión de instrumentar nuestras aplicaciones, de forma que sea fácil identificar el origen y ejecutante de las sentencias. Cuesta un poco de trabajo, pero deben planteárselo como un estándar a seguir para todo nuevo desarrollo. Aún me sorprende encontrar muchas instalaciones donde esta medida no se ha tomado y hacer segumiento resulta más complicado de lo que debiera.

No olviden tampoco que deben usar dbms_monitor, sencillamente no tiene reemplazo y junto con trcsess y tkprof son nuestros aliados en el seguimiento del código. Pero esto no es todo, en un siguiente Post veremos otros beneficios que podemos obtener si instrumentamos las aplicaciones, estén atentos.

Posts Relacionados:
Siga leyendo >>

¿Te pareció interesante este artículo?, ¿te quedaron algunas dudas?, ¿quieres sugerirme un tema a tratar?, pues déjame tus comentarios o envíame un email y para que NO te pierdas ningún Post, suscríbete por email ahora mismo!