Cuando se usan los indices en una consulta con LIKE

Hoy vamos ha hacer una pequeña entrada de tunning.

Muchas veces vemos a la hora de hacer ajustes de rendimiento como las consultas que se ejecutan la base de datos tienen la clausula LIKE, pero su comportamiento es distinto segun como se ejecuta.
¿Cuando se hace un INDEX RANGE y cuando un FULL SCAN? , esto es principalmente lo que veremos hoy

Lo primero,¿ que es lo que hace la claúsula LIKE ?
La clausula LIKE mira si una cadena determinada coincide con un determinado patrón. Mediante el carácter % podemos hacer búsquedas genéricas con esa cadena .
Hemos de destacar que, LIKE contempla la búsqueda de longitud cero, es decir ,que la búsqueda de LIKE ‘PEPE% ‘ incluye tanto PEPESI como PEPE.

Una vez tenemos claro que hace la clausula LIKE, veamos de que manera podemos utilizarlo:

  • CADENA%
  • %CADENA
  • %CADENA%
  • CADENA1%CADENA2

Vamos a ver el comportamiento en cada una de las consultas. Tenemos una tabla llamada Imagen con un campo varchar NOMBRE

SQL> desc imagen
 Name                 Null?    Type
 ------------------ -------- ---------------
 ID                  NOT NULL NUMBER
 NOMBRE                       VARCHAR2(256)
 RUTA                         VARCHAR2(256)

Sobre este campo NOMBRE hemos creado un y un UNIQUE INDEX llamado IMAGEN_NOMBRE_UNI, vamos ha hacer los 4 tipos de búsqueda y comparar los planes:

CADENA%

Si ejecutamos una búsqueda en la que miramos el final de la cadena, la base de datos intentará utilizar primero el índice

set autotrace traceonly;
SQL> select * from  imagen where nombre like 'D80%';
|Id | Operation                  | Name           |Rows |Bytes| 
------------------------------------------------------------------
| 0 | SELECT STATEMENT            |                  | 1 |143|3(0)|
| 1 |  TABLE ACCESS BY INDEX ROWID| IMAGEN           | 1 |143|3(0)|
|*2 |   INDEX RANGE SCAN          | IMAGEN_NOMBRE_UNI| 1 |   |2(0)|
-------------------------------------------------------------------

El optimizador conoce como comienza la cadena, con lo que llevará a cabo un INDEX RANGE SCAN para llevar a cabo la consulta con el menor coste posible.

%CADENA

EN este caso estamos buscando el final de la cadena entre el contenido de nuestra columna NOMBRE, si miramos el resultado del plan de ejecución

set autotrace traceonly;
SQL> select * from  imagen where nombre like '%009';
----------------------------------------------------
| Id  | Operation    | Name   | Rows  | Bytes |
-------------------------------------------------
| 0| SELECT STATEMENT  |       |778 |108K| 93 (2)
|*1|  TABLE ACCESS FULL| IMAGEN|778 |108K| 93 (2)
-------------------------------------------------

El optimizador desconoce como comienza la cadena, con lo que llevará a cabo un TABLE ACCESS FULL de toda la tabla.En este caso no usa el índice ya que no le sirve de nada el indice si debe de comparar la parte final de la cadena

%CADENA%

Que ocurrirá si buscamos la parte central de una cadena?

set autotrace traceonly;
SQL> select * from  imagen where nombre like '%009%';
----------------------------------------------------
| Id  | Operation    | Name   | Rows  | Bytes |
-------------------------------------------------
| 0| SELECT STATEMENT  |       |778 |108K| 93 (2)
|*1|  TABLE ACCESS FULL| IMAGEN|778 |108K| 93 (2)
-------------------------------------------------

El optimizador desconoce como comienza la cadena, con lo que llevará a cabo un TABLE ACCESS FULL de toda la tabla.
En este caso no usa el índice ya que no le sirve de nada el indice si debe de comparar la parte final de la cadena , es el mismo comportamiento que cuando lanzamos la consulta con %CADENA

CADENA1%CADENA2

Que ocurrirá si buscamos la parte central de una cadena?

set autotrace traceonly;
SQL>  select * from imagen where nombre like 'D80_10%G';

|Id | Operation                  | Name           |Rows |Bytes| 
------------------------------------------------------------------
| 0 | SELECT STATEMENT            |                  | 1 |143|3(0)|
| 1 |  TABLE ACCESS BY INDEX ROWID| IMAGEN           | 1 |143|3(0)|
|*2 |   INDEX RANGE SCAN          | IMAGEN_NOMBRE_UNI| 1 |   |2(0)|
-------------------------------------------------------------------

Este caso es muy similar al de CADENA%, el optimizador si que conoce el comienzo de la cadena, con lo que llevará a cabo un INDEX RANGE SCAN para llevar a cabo la consulta en el menor.
Hay que tener en cuenta que, contra mas restrictiva sea la búsqueda mas rápido será y tendrá menos coste para la base de datos. es decir que, aunque en los dos casos use el índice, el coste de buscar CAD%CADENA2 será mayor que el de buscar CADENA1%CADENA2

Problemas con SYSAUX, ocupación de AWRs

Hoy vamos a lidiar con un problema muy común en las bases de datos de Oracle que es el tamaño del tablespace SYSAUX.
El tablespace SYSAUX es uno de los tablespaces especiales de la base de datos con el que no podemos jugar alegremente, la principal característica que tiene de cara a la administración de base de datos es que ,el espacio que le demos se ha perdido, es decir, no va ha haber forma de hacerlo mas pequeño (al menos en mi caso siempre ha habido un «pero» que lo imposibilita).

Como decía al principio es junto con el SYSTEM un tablespace muy especial, y los motivos de su tamaño pueden ser varios,hoy vamos a centrarnos en como vaciar un poco el tamaño debido el AWR.

Lo que, lo primero que tendremos que hacer es ver cual es el componente que esta llenándonos el tablespace.
Para ello tenemos la consulta

set linesize 100;
column OCCUPANT_NAME format a20;
column OCCUPANT_DESC format a60;
column Mb format 999.99
SELECT occupant_name,space_usage_kbytes/1024 Mb,occupant_desc
  FROM V$SYSAUX_OCCUPANTS
  order by space_usage_kbytes  desc;

OCCUPANT_NAME             MB OCCUPANT_DESC
-------------------- ------- ------------------------------------------------------------
SM/AWR                780.06 Server Manageability - Automatic Workload Repository
SM/OPTSTAT            333.00 Server Manageability - Optimizer Statistics History
LOGMNR                 91.63 LogMiner
XDB                    85.50 XDB
SM/ADVISOR             65.56 Server Manageability - Advisor Framework
WM                      6.38 Workspace Manager
SM/OTHER                6.25 Server Manageability - Other Components
SMON_SCN_TIME           5.31 Transaction Layer - SCN to TIME mapping
EXPRESSION_FILTER       3.88 Expression Filter System
JOB_SCHEDULER           2.81 Unified Job Scheduler
EM_MONITORING_USER      2.75 Enterprise Manager Monitoring User
SQL_MANAGEMENT_BASE     1.69 SQL Management Base Schema
PL/SCOPE                1.56 PL/SQL Identifier Collection
XSOQHIST                1.38 OLAP API History Tables
AO                      1.38 Analytical Workspace Object Table
LOGSTDBY                1.38 Logical Standby
STREAMS                 1.00 Oracle Streams
AUTO_TASK                .31 Automated Maintenance Tasks
ORDIM/ORDDATA            .00 Oracle Multimedia ORDDATA Components
ORDIM/ORDPLUGINS         .00 Oracle Multimedia ORDPLUGINS Components
ORDIM/SI_INFORMTN_SC     .00 Oracle Multimedia SI_INFORMTN_SCHEMA Components
HEMA
EM                       .00 Enterprise Manager Repository
TEXT                     .00 Oracle Text
ULTRASEARCH              .00 Oracle Ultra Search
ORDIM                    .00 Oracle Multimedia ORDSYS Components
SDO                      .00 Oracle Spatial
STATSPACK                .00 Statspack Repository
TSM                      .00 Oracle Transparent Session Migration User
XSAMD                    .00 OLAP Catalog
AUDIT_TABLES             .00 DB audit tables
ULTRASEARCH_DEMO_USE     .00 Oracle Ultra Search Demo User

Como podemos ver, son muchas las cosas que se guardan en SYSAUX, pero, en nuestro caso, está bastante claro que, la mayoría de la ocupación es debida a los AWR.

Nuestro siguientes pasos van a ser:

  • Determinar que es lo que nos ocupa el espacio
  • Mirar si la causa esta en la retención
  • Mirar si la causa está en los AWR guardados
  • Mirar si la causa está en las lineas «huerfanas» de las Active Session History

Nuestro objetivo va a ser liberar espacio dentro de SYSAUX , no disminuir el tamaño de este tablespace ya que, como decía al principio esto es prácticamente una misión imposible.

Determinar que es lo que ocupa espacio

El siguiente paso será el buscar la información del AWR, para ello el propio Oracle tiene una utilidad que podemos llamar desde sqlplus

 SQL> @$ORACLE_HOME/rdbms/admin/awrinfo.sql

Este sql nos genera un informe con unos apartados muy interesantes que nos servirán para encontrar de donde liberar espacio.

Mirar si la causa esta en la retención

************************************                                                                         
(2) Size estimates for AWR snapshots                                                                          
*************************************                                                                                                                                                                                    
| Estimates based on 60 mins snapshot INTERVAL:                                                               
|    AWR size/day                           31.0 MB (1,324 K/snap * 24 snaps/day)                             
|    AWR size/wk                           217.2 MB (size_per_day * 7) per instance                                                                                                                                     
| Estimates based on 24 snaps in past 24 hours:                                                               
|    AWR size/day                           31.0 MB (1,324 K/snap and 24 snaps in past 24 hours)              
|    AWR size/wk                           217.2 MB (size_per_day * 7) per instance                           
|

Aqui podemos ver el tamaño estimado que ocupa nuestra información de los AWR, en función del periodo de retencion que tengamos de los AWR tendremos la ocupación en la base de datos.
Cuando sabemos cuanto ocupa cada AWR y cuanto hacemos al día, tenemos que saber cuantos dias guardamos para ver si la ocupación es correcta, para ello haremos :

SQL> SELECT retention FROM dba_hist_wr_control;

RETENTION
---------------------------------------------------------------------------
+00007 00:00:00.0

En mi caso por ejemplo, la retencion es de 7 días.

Mirar si la causa está en los AWR guardados

Otro de los problemas que podemos tener es que el número de AWR que mantenemos guardados por razones históricas en la instancia, en el informe que acabamos de obtener también aparece un apartado con las últimas 50 snapshots de la base de datos, el eliminar imágenes antiguas también nos ayudará a liberar espacio.

  • Mirar si la causa está en las lineas «huerfanas» de las Active Session History
  • Algunas veces el Active Session History (ASH) guarda filas que no pertenecen a ningún AWR,esto hace que el ASH ocupe gran parte del espacio dedicado al AWR, la forma de saber si esto nos está sucediendo es mirar nuestro informe en el apartado

    
    **********************************
    (3a) Space usage by AWR components (per database)
    **********************************
    
    COMPONENT        MB  % AWR  KB_PER_SNAP MB_PER_DAY MB_PER_WEEK TABLE% : INDEX%                                
    --------- --------- ------ ------------ ---------- ----------- ----------------                               
    FIXED         131.5   50.6          670       15.7       109.9    52% : 48%                                   
    EVENTS         37.9   14.6          193        4.5        31.7    43% : 57%                                   
    SQLPLAN        25.0    9.6          127        3.0        20.9    72% : 28%                                   
    ASH            18.3    7.0           93        2.2        15.3    89% : 11%                                   
    SQL            11.0    4.2           56        1.3         9.2    60% : 40%                                   
    SPACE          10.3    4.0           53        1.2         8.6    61% : 39%                                   
    SQLTEXT         5.2    2.0           26        0.6         4.3    96% : 4%                                    
    SQLBIND         0.8    0.3            4        0.1         0.6    50% : 50%                                   
    RAC             0.6    0.2            3        0.1         0.5    50% : 50%                                   
    

    Si la línea de ASH ocupa mas de un 1% probablemente sea la causa de nuestra ocupación.
    Mediante la siguiente consulta podemos saber si es nuestro caso.

    SELECT COUNT(*) huerfanas
    FROM wrh$_active_session_history a
    WHERE NOT EXISTS
      (SELECT *
      FROM wrm$_snapshot
      WHERE snap_id       = a.snap_id
      AND dbid            = a.dbid
      AND instance_number = a.instance_number
      );
    
    
     HUERFANAS
    ----------
           116
    
    

    Si aparece alguna línea podemos borrarlas con

    delete  
    FROM wrh$_active_session_history a
    WHERE NOT EXISTS
      (SELECT *
      FROM wrm$_snapshot
      WHERE snap_id       = a.snap_id
      AND dbid            = a.dbid
      AND instance_number = a.instance_number
      );
    

    Con estas tres acciones, si elproblema de laocupación del SYSAUX es debido a el AWR tendremos que haberpodido liberar el espacio suficiente para que la base de datos pueda seguir trabajando sin problemas de espacio.

    Aún así, el tema de la ocupación del SYSAUX es un tema bastante complejo, con lo que, como siempre, el mejor sitio para buscar soluciones es soporte de Oracle, aquí tenéis un pequeño listado de algunas de las notas que puede que os aplique y cuyo contenido tenéis en la página de soporte

    • AWR Data Uses Significant Space in the SYSAUX Tablespace [ID 287679.1]
    • Suggestions if your SYSAUX Tablespace grows rapidly or too large [Document 1292724.1]
    • General Guidelines for SYSAUX Space Issues [Document 552880.1]
    • SYSAUX Grows Because Optimizer Stats History is Not Purged [Document 1055547.1]
    • Space issue in Sysaux tablespace due to Unexpected AWR size [Document 1218413.1]
    • Space Management In Sysaux Tablespace with AWR in Use [Document 287679.1]
    • SYSAUX Tablespace Grows Heavily Due To AWR [Document 852028.1]

    O alguna entrada muy interesante en algunos blogs como es

    Why is my SYSAUX Tablespace so Big? Statistics_level=ALL

    ORA-20005 Estadisticas bloqueadas

    Hoy vamos a ver la causa de un problema que pude darse en sistemas en los que hay varias personas administrando o con privilegios sobre distintos objetos de la base de datos. Una de las situaciones que se pueden dar, es que , uno de los usuarios intente lanzar las estadisticas del planificador de los objetos sobre los que tiene privilegios y se encuentre con un :

    ORA-20005: object statistics are locked (stattype = ALL) 
    ORA-06512: at "SYS.DBMS_STATS", line 13182 
    ORA-06512: at "SYS.DBMS_STATS", line 13202 
    ORA-06512: at line 2
    

    ¿Por que sucede esto?
    Sencillamente, por que, como bien dice el error de oracle, por que alguien ha bloqueado las estadísticas.
    Ante todo, el bloqueo de las estadisticas no es algo automático, es una opción deliberada que ha de aplicarse sobre determinados objetos, con lo que, si está así es por que ,alguien con privilegios ha decidido que así sea.

    Los segundo que nos preguntamos es ¿por que están bloqueadas?
    Hay distintas razones por la que podemos querer bloquear las estadísticas de algunos objetos, pro las principales pueden ser :

    • Por razones de rendimiento: Hay entornos en los que los objetos de la base de datos varian mucho durante su periodo de explotación, el dba puede elegir obtener un juego de estadisticas válido durante un periodo tipo, y aplicar este juego de estadisticas indistintamente del periodo de explotacion en el que se encuentre.
    • Por problemas de ventana, ya que, la generación de estadisticas de esos objetos puede ser un proceso demasiado pesado, con lo que , el dba ha decidido bloqearlas y lanzarlas especificamente en una ventana aparte
    • El proceso de cálculo de estadísticas es un proceso bastante pesado , probablemente el administrador del sistema haya decidido bloquearlas para evitar que cualquier otro usuario con permisos decida lanzarlas

    En todo caso, si obtenemos este error ORA-20005, necesitaremos comprobar si realmente están bloqueadas, mediante esta consulta podremos ver que objetos de la base de datos tienen las estadisticas bloqueadas.

    select owner, table_name, stattype_locked
    from dba_tab_statistics
    where stattype_locked is not null; 
    

    Lo siguiente que querremos saber, es de cuando son el juego de estadísticas que estamos utilizando, para eso ampliaremos la consulta a:

     select table_name,last_analyzed  from dba_tables 
       where table_name in 
              (select table_name 
                     from dba_tab_statistics 
                         where stattype_locked is not null 
                               and OWNER='ESQUEMAPRUEBAS)
         order by last_analyzed asc 
    

    Para desbloquear todas las estadísticas de un esquema ESQUEMAPRUEBAS podemos usar el metascript

      select 
      'exec dbms_stats.unlock_table_stats('
       ||chr(39)
       ||'ESQUEMAPRUEBAS'
       ||chr(39)||
        ','
       ||chr(39)
       ||TABLE_NAME
       ||chr(39)||' );'
          from dba_tab_statistics where
               stattype_locked is not null 
                  and OWNER='ESQUEMAPRUEBAS';
    

    y ver los distintos objetos, o hacerlo directamente con la llamada :

    exec dbms_stats.unlock_schema_stats('ESQUEMAPRUEBAS');
    

    Como nota final, hay que tener cuidado con el desbloqueo de estadísticas ya que hay algunas estadísticas del sistema que pueden estar bloqueadas con lo que no conviene desbloquear todas las estadísticas de todos los esquemas.

    Para mas información, como siempre esta metalink, allí tenemos la notas relacionadas

    • «ORA-38029 «Object Statistics Are Locked» – Possible Causes [ID 433240.1]»
    • Preserving Statistics using DBMS_STATS.LOCK_TABLE_STATS [ID 283890.1]

    Encontrar el proceso que se come la CPU en windows

    Una de las principales diferencias de oracle para Unix y Windows radica en que, debido a el tipo de sistema operativo, en Windows tenemos un proceso monolítico oracle.exe, y no la multitud de procesos que nos encontramos en los sistemas Unix. Así pues, cuando queremos saber cual es el proceso que se nos come la CPU, siempre vamos a tener una misma respuesta oracle.exe y, además de eso, probablemente no podamos enlazarlo con los procesos de sistema operativo.

    ¿Como solucionamos este problema?

    Para empezar, mi recomendación es tener en el servidor uno de estos dos programas

    • Pprocess explorer
    • QSlice

    Los dos programas son gratuitos y se pueden descargar desde soporte de microsoft, y nos permitirán ver con mayor facilidad el origen de nuestro problema.

    Si abrimos el process explorer , veremos algo similar a esto:
    process explorer

    Aquí podemos ver como uno de los procesos oracle se esta comiendo el 100% de la CPU , si hacemos boton derecho «propiedades», el process explorer nos indicará en una ventana independiente la informacion de este proceso, si vamos a la pestaña «threads» y ordenamos por CPU, tendremos:
    proceso oralce.exe

    Aqui vemos como los treads que mas CPU están consumiendo son

    • 3076 con el 23%
    • 4976 con el 19,95%

    Ahora, teniendo estos dos número de thread, si que podremos ir a nuestra ventana de sql y enlazar este numero de thread con el proceso/sesion de Oracle que está causando la carga

    
    select proc.spid ThreadNO,  
    sess.username Usuario,  
    sess.osuser OSUser,
    sess.machine Maquina,  
    sess.status Estado,  
    sess.sid SessionID,  
    sess.program Program  
    from v$process proc, v$session sess, v$bgprocess bg  
    where sess.paddr = proc.addr  
    and bg.paddr(+) = proc.addr  
    and proc.spid in (3076)
    
    

    Esta informacion tambien puede obtenerse con qslice.exe, solamente que la información del thread está en exadecimal, y habremos de pasarla a decimal, por otra parte, la ventaja del qslice.exe es que es más ligero que el process explorer, con lo que, como decía al principio, mi recomendación es tener los dos en el servidor

    Eventos de espera en disco I

    Hoy vamos a hablar un poquito mas de ajuste de bases de datos.

    Aplicando la lógica mas pura, si queremos que todo vaya fluido, lo primero que tendremos que prestar atencion a que es lo que nos está haciendo esperar. Así pues, una de las primeras cosas que tenemos que mirar para ajustar una base de datoslos eventos de espra que mas se producen.

    Si generalizamos hasta lo evidente, podemos decir que Oracle puede tener problemas de cpu,memoria o disco (esperemos que sea un «o» en ved de un un «y» ) . En esta entrada vamos a dar una pequeña explicación de cuales son los eventos de espera en disco mas comunes.

    • log file sync
    • log file parallel write
    • db file scattered read
    • db file sequential read

    log file sync

    Cuando una sesion de usuario hace un commit, la información del redo de esta sesion debe de ser llevada al redo log file, este evento nos indica que estamos esperando a que el LGWR nos indique que esta información está guardada correctament en el redo log.
    La aparicion de este evento en el «top ten» puede indicarnos un alto número de commits de aplicación.

    log file parallel write

    Este evento es muy similar al anterior, solamente que es parte normal del funcionamiento de oracle que lleva la informacion la informacion del redo a el redo log (el anterior lo causaba un commit)

    db file sequential read

    Este evento nos indica que la instancia está esperando una peticion de entrada/salida,esta lectura se está haciendo de bloques contiguos de disco (de ahí lo de sequential).
    Por lo general suelen ser lecturas de bloques sueltos, y suele estar asociada a lecturas de filas indexadas

    db file scattered read

    Es similar a la anterior excepto que en este caso está a la espera de multiples bloques.
    Habitualmente está asociada a Full scan tables. Este evento puede tener relaccion con el parámtro DB_FILE_MULTIBLOCK_READ_COUNT en el que indicamos a oracle el numero de bloques consecutivos a leer en una operacion de I/O para un full scan.

    Una vez visto lo que quiere decirnos cada uno de estos 4 eventos,es facil entender el porque de que algunos de ellos esten en la parte alta de los eventos de espera de los informes de AWR .