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

Modificar el editor por defecto de SQLplus en Unix

Vamos con una nota rápida y sencilla, pero bastante útil.

SQLPlus tiene por defecto el editor ed , sinceramente, el ed no es el editor mas usado del mundo, de hecho, no conozco a nadie que lo tenga como su primera opción, la pregunta ahora es, ¿Como hacemos para modificar este editor por defecto?

muy sencillo:


define_editor='vi'

Para no tener que estar añadiendo esta línea cada vez que entramos, podemos añadirla al fichero profile de sqlplus genérico que se encuentra en


$ORACLE_HOME/sqlplus/admin/glogin.sql

Recuperacion con RMAN desde Dataprotector desde linea de comandos

Muchas veces tenemos el backup integrado por scripts propietarios del software de backup.
Esta integración nos garantiza el pode recuperar con «botón derecho», pero , puede darse el caso de querer recuperar manualmente, bien por que queremos tener el control total sobre el proceso o bien por que es en otra maquina o por que queramos hacer una recuperación mas especifica del RMAN que la que nos ofrezcan los botones del software de backup.

En este caso vamos a hacer una recuperación total de una base de datos que se ha copiado con dataprotector. Entre las cosas que necesitaremos son:

  • Init.ora de la base de datos, deberíamos de hacer una copia del mismo junto con el backup,con lo que podemos sacarlo de ahi
  • DBID de la base de datos, este DBID aparece en el log de rman, con lo que podremos sacarlo del ultimo log del backup
  • Cadena de configuración de la cinta. Esta en las propiedades avanzadas de la política de backup que usamos para copiar nuestra base de datos

Además, necesitaremos ser capaces de llegar al log de la ultima copia de rman, esto se hace desde dataprotector, mirando en las siguientes pestañas

Internal Database
     -> "log del backup"  (tiene el formato fecha/backup)
           -> Propierties (boton derecho)
               --> Messages (el log completo del rman)

Para clarificar un poco los logs, tendremos en este caso:

  • instancia=pruebas
  • Servidor=serveroracle.pamplona.name
  • DBID=3751694031 (obtenido del log del backup desde dataprotector)

Si no tuviésemos el init.ora podríamos recuperarlo también del backup ya que la 11g hace copia del init.ora con el controlfile autobackup, pero, es una buena practica el tener una copia del init.ora en modo texto, ya que, nos evita uno de los pasos mas engorrosos.
Con estas 3 cosas, podemos comenzar la recuperación de la base de datos.

Lo primero que recuperaremos será el controlfile, para ello haremos un script al que llamaremos restore_controlfile.cmd tal que

startup nomount;
set DBID=3751694031
run {
allocate channel 'dev_0' type 'sbt_tape'
 parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=Online Diaria)';
restore controlfile from autobackup;
}

Al que llamaremos con

rman target / cmdfile restore_controlfile.cmd

En este punto, podemos llevarnos la sorpresa de que obtenemos un RMAN-06172

Al igual que vimos en el post RMAN-06172: no AUTOBACKUP found or specified handle is not a valid copy or piece tendremos que decirle exactamente cual es el nombre del controlfile que queremos recuperar, para ello, nos iremos al dataprotector y exploramos el log hasta la última línea en la que encontremos la palabra controlfile

[Normal] From: OB2BAR_DMA@serveroracle.pamplona.name "pruebas"  Time: 06/03/2013 21:21:19
	Starting OB2BAR Backup:serveroracle.pamplona.name: pruebas DP Managed Control File Backup "Oracle8"

La línea que estamos buscando es pruebas DP Managed Control File Backup «Oracle8», que es el fichero dentro de dataprotector donde se encuentra nuestro controlfile.
Así pues, modificaremos el script de backup y ahora será:

startup nomount;
set DBID=3751694031
run {
allocate channel 'dev_0' type 'sbt_tape'
 parms 'ENV=(OB2BARTYPE=Oracle8,OB2APPNAME=pruebas,OB2BARLIST=Online Diaria)';
restore controlfile from 'pruebas DP Managed Control File Backup "Oracle8"';
}

Con esto conseguiremos tener nuestro controlfile restaurado. Ahora, ya tenemos una restauracion standard de RMAN típica de manual.

Monitorizar el alert.log desde una sql remota

El fichero de alertas de oracle alert.log es uno de los ficheros que tradicionalmente revisamos en busca de errores de la base de datos.
Hasta la versión 11g esta monitorización se hacía por medio del scripts que interactuaban con ficheros del sistema operativo, lo que nos llevaba a tener que mantener estos scripts para las distintas versiones de shells de sistema, o tener que instalar algún tipo de interprete en el sistema que nos lo gestionara de manera multiplataforma.

Una de las grandes ventajas de la 11g es que Oracle nos ha obsequiado con una tabla de base de datos que mapea esta información del alert.log dentro de la base de datos . Estamos hablando de la tabla x$dbgalertext;

Mediante la tabla x$dbgalertext podemos obtener toda la informacion que está en el alert.log, su contenido es:

SQL> desc X$DBGALERTEXT

           Name                            Null?    Type
           ------------------------------- -------- -------------------------
    1      ADDR                                     RAW(4)
    2      INDX                                     NUMBER
    3      INST_ID                                  NUMBER
    4      ORIGINATING_TIMESTAMP                    TIMESTAMP(3) WITH TIME ZONE
    5      NORMALIZED_TIMESTAMP                     TIMESTAMP(3) WITH TIME ZONE
    6      ORGANIZATION_ID                          VARCHAR2(64)
    7      COMPONENT_ID                             VARCHAR2(64)
    8      HOST_ID                                  VARCHAR2(64)
    9      HOST_ADDRESS                             VARCHAR2(16)
   10      MESSAGE_TYPE                             NUMBER
   11      MESSAGE_LEVEL                            NUMBER
   12      MESSAGE_ID                               VARCHAR2(64)
   13      MESSAGE_GROUP                            VARCHAR2(64)
   14      CLIENT_ID                                VARCHAR2(64)
   15      MODULE_ID                                VARCHAR2(64)
   16      PROCESS_ID                               VARCHAR2(32)
   17      THREAD_ID                                VARCHAR2(64)
   18      USER_ID                                  VARCHAR2(64)
   19      INSTANCE_ID                              VARCHAR2(64)
   20      DETAILED_LOCATION                        VARCHAR2(160)
   21      PROBLEM_KEY                              VARCHAR2(64)
   22      UPSTREAM_COMP_ID                         VARCHAR2(100)
   23      DOWNSTREAM_COMP_ID                       VARCHAR2(100)
   24      EXECUTION_CONTEXT_ID                     VARCHAR2(100)
   25      EXECUTION_CONTEXT_SEQUENCE               NUMBER
   26      ERROR_INSTANCE_ID                        NUMBER
   27      ERROR_INSTANCE_SEQUENCE                  NUMBER
   28      VERSION                                  NUMBER
   29      MESSAGE_TEXT                             VARCHAR2(2048)
   30      MESSAGE_ARGUMENTS                        VARCHAR2(128)
   31      SUPPLEMENTAL_ATTRIBUTES                  VARCHAR2(128)
   32      SUPPLEMENTAL_DETAILS                     VARCHAR2(128)
   33      PARTITION                                NUMBER
   34      RECORD_ID                                NUMBER

Ahora bien, ¿como accedemos a ella?

La tabla no puede ser accedida directamente desde un usuario que no sea sys, así que, lo que haremos será el crear una vista sobre esta tabla (a la que llamaremos por ejemplo ficheroalert ) y permitirle que lo vea a nuestro usuario de monitorizacion.


create view ficheroalert as select  * from sys.x$dbgalertext;
grant select on sys.ficheroalert to MONITORIZACION;

A partir de aquí, solamente tenemos que jugar con los campos descritos arriba y podremos obtener la informacion que deseemos.
En mi caso , por ejemplo, me gustaría saber si ha habido algún mensaje ORA- o ERROR en los ultimos 5 munitos.

La consulta que ejecutaré para obtenerlo es:


select to_char(ORIGINATING_TIMESTAMP, 'dd-mon-yyyy hh24:mi:ss'),
      substr(MESSAGE_TEXT, 1, 300) message_text
    from sys.ficheroalert
    where (MESSAGE_TEXT like '%ORA-%'
            or upper(MESSAGE_TEXT) like '%ERROR%')
     and 
           cast(ORIGINATING_TIMESTAMP as DATE) > sysdate - 5/1440;
            - X/1440 es la X en minutos 

Entrada en ingles en Monitoring the alert.log from a remote sql