Gestor de recursos. A la caza del bloqueo I

Hoy  vamos a abordar los problemas de bloqueos desde otro punto de vista.

A pesar de que los bloqueos sonun problema propio de  la aplicación y debe de ser desde esta donde se solucione el evitar sesiones bloqueantes, un código de  aplicacion defectuoso  puede llegar a tirar abajo nuestra instancia , con lo que deberemos de acabar con este tipo de sesiones antes de que afecten a la disponibilidad del sistema.

Oracle nos da la opcion de limitar el tiempo en el que un usuario está IDLE a la vez que  bloquea otra sesion con el parámetro NEW_MAX_IDLE_BLOCKER.

Esta es la primera de un grupo de entradas en las que utilizaremos el gestor de recursos de Oracle para llevar acabo workarrounds que nos solucionen este problema.

Lo primero que haremos es crear un plan al que llamaremos «NO_LOCKS», para ello lo mas sencilllo es copiar el DEFAULT_PLAN desde el EM, si por el contrario queremos hacerlo desde linea de sqlplus tenemos que:

  1. Crear una pending area (si lo hacemos desde sqlplus)
  2. Crear el plan de recursos
  3. Validar el pending area
  4. Enviar/presentar  (submit) esta pending area
BEGIN
dbms_resource_manager.clear_pending_area();
dbms_resource_manager.create_pending_area();
dbms_resource_manager.create_plan( plan => 'NO_LOCKS',
 comment => 'Plan para eliminar bloqueos desde el gestor de recursos, copiado de DEFAULT_PLAN'
);
dbms_resource_manager.create_plan_directive(
    plan => 'NO_LOCKS',
    group_or_subplan => 'SYS_GROUP',   
    comment => 'Asignamos 75% de cpu al grupo SYS ',
    mgmt_p1 => 75
);
dbms_resource_manager.create_plan_directive(
    plan => 'NO_LOCKS',
    group_or_subplan => 'DEFAULT-CONSUMER_GROUP',   
    comment => 'Asignaremos el 90% de CPU al grupo de consumidores por defecto ',
    mgmt_p2 => 90
);
dbms_resource_manager.create_plan_directive(
    plan => 'NO_LOCKS',
    group_or_subplan => 'OTHER_GROUPS',   
    comment => 'Este grupo debe de aparecer ',
    mgmt_p2 => 5
);
dbms_resource_manager.create_plan_directive(
    plan => 'NO_LOCKS',
    group_or_subplan => 'ORA$DIAGNOSTICS',   
    comment => 'Asignamos 5% de cpu al grupo ORA$DIAGNOSTICS en el segundo nivel ',
    mgmt_p2 => 5
 );
 DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA();
DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA();
END;

Con esto tenemos  un nuevo plan llamado «NO_LOCKS» que es similar al DEFAULT_PLAN.

Lo que vamos ha hacer ahora es modificar el subplan que se aplica a todos los usuarios (DEEFAULT_CONSUMER_GROUP) para que no los deje bloquear recursos mas de 5 segundos.

BEGIN
dbms_resource_manager.clear_pending_area();
dbms_resource_manager.create_pending_area();
dbms_resource_manager.update_plan_directive(
    plan => 'NO_LOCKS',
    group_or_subplan => 'DEFAULT_CONSUMER_GROUP',   
    new_comment => 'Subplan para bloqueadores, similar a DEFAULT_CONSUMER_GROUP pero  limitado',
    new_max_idle_blocker_time=>5
    );
DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA;
DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA;
END;

Si miramos los cambios desde el EM podremos ver como   esta el parámetro

Plan NO_LOCK

Ahora solamente nos quedará activar el nuevo plan y probarlo

Esto lo comprobamos con

SQL>ALTER SYSTEM SET RESOURCE_MANAGER_PLAN = 'NO_LOCKS';

SQL> SELECT VALUE FROM V$PARAMETER  WHERE name = 'resource_manager_plan';

VALUE
--------------------------------------------------------------------
NO_LOCKS

Ahora vamos ha hacer la prueba de que funciona

Veamos en que grupo de consumidores está el usuario HR

SQL> select initial_rsrc_consumer_group from dba_users where username='HR';

INITIAL_RSRC_CONSUMER_GROUP
------------------------------
DEFAULT_CONSUMER_GROUP

 

Nos conectamos con el usuario HR y bloquearemos la tabla REGIONS

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'HH24:MI:SS';
Session altered.
SQL>  select sysdate from dual;
SYSDATE
--------
12:08:13
SQL> select * from  regions where  REGION_ID=1 for update;
 REGION_ID REGION_NAME
---------- -------------------------
         1 Europe
SQL>  select sysdate from dual;
SYSDATE
--------
12:30:18

En el segundo select sysdate vemos como,  pesar de que la tabla esta bloqueada, y que han pasado 15 segundos el gestor de recursos no nos ha hechado.¿habremos hecho algo mal?

La respuesta es que no hemos hecho nada mal, lo que está ocurriendo es que la consulta que hemos ejecutado esta bloqueando algo, pero no esta bloqueando  a nadie .

Que ocurre si intentamos ejecutar una consulta en otra sesion que  se vea bloqueada por esta?

En el momento en l que el gestor de recursos detecta que nuestra primera sesion esta bloqueando a otra empieza a contar los segundos que nuestra primera consulta esta «idle».Al llegar a los 5 segundos matará nuestra consulta bloqueante y liberará sus recursos.

Si tras esos 5 segundos  volvemos a recargar la ventana del EM  veremos que no hay bloqueos, y  si nos vamos a la ventana donde hemos ejecutado nuestra consulta bloquante nos encontraremos con el error:

ORA-02396: ha excedido el tiempo máximo de inactividad, vuelva a conectarse

Con lo que , de manera algo brusca y desde la base de datos hemos conseguido acabar con todos los bloqueos de usuario que estén inactivas mas de 5 segundos.

 


LLegados a este punto tenemos a TODOS los usuarios de la base de datos sin poder llevar a cabo un bloqueo de mas de 5 segundos en cualquier hora del día.

Esto nos va a generar muchos problemas a la hora de los procesos batch e incluso en alguna transaccion pesada, con lo que lo mejor que podemos hacer el volverlo atrás .

El único valor de esta entrada es como ejercicio didactico, asi que, mas nos valdría volver a activar en la base de datos el DEFAULT_PLAN o el INTERNAL_PLAN

En las siguientes entradas afinaremos este plan NO_LOCKS para limitarlo por esquemas y por franja horaria, haciendo de la limitación NEW_MAX_IDLE_BLOCKER_TIME  algo util.

Sqlplus para dummies III ( Bloqueos )

Otra de las cosas que mas amenudo tenemos que hacer es el encontrar quien nos está bloqueando y que bloquea.

Para ello, la consulta mas sencilla que podemos hacer es:

SELECT
     a.session_id , username ,type , mode_held, mode_requested 
     lock_id1,lock_id2
FROM
     sys.v_$session b,
     sys.dba_blockers c,
     sys.dba_lock a
WHERE
     c.holding_session=a.session_id AND
     c.holding_session=b.sid

Pero,  seguramente queramos saber mas cosas, como que tipo de bloqueos y que objetos están implicados, para ello podemos usar esta consulta en la que aparecen  datos de la sesión, del proceso tanto en el server como en el cliente y de los objetos y tipo de bloqueo que lleva a cabo

 

SELECT 
substr(to_char(l.sid),1,4) "SID", 
s.serial# "SERIAL",
P.spid "Server  PID", 
s.USERNAME,
s.type, 
s.process "Client  PID", 
MACHINE, 
l.type, 
DECODE(L.TYPE,'MR','File_ID: '||L.ID1, 
'TM', LO.NAME, 
'TX','USN: '||to_char(TRUNC(L.ID1/65536))||' RWO: '||nvl(RWO.NAME,'None'),L.ID1) LOCK_ID1, 
decode(l.lmode, 
0, 'None', 
1, 'Null', 
2, 'Row-S (SS)', 
3, 'Row-X (SX)', 
4, 'Share', 
5, 'S/Row-X (SSX)', 
6, 'Exclusive', 
substr(to_char(l.lmode),1,13)) "Locked Mode", 
decode(l.request, 
0, 'None', 
1, 'Null', 
2, 'Row-S (SS)', 
3, 'Row-X (SX)', 
4, 'Share', 
5, 'S/Row-X (SSX)', 
6, 'Exclusive', 
substr(to_char(l.request),1,13)) "Requested", 
l.ctime, 
l.block ,
s.logon_time
FROM v$process P, 
v$session S, 
v$lock l, 
sys.obj$ lo, 
sys.obj$ rwo 
WHERE l.sid = S.sid (+) 
AND S.paddr = P.addr (+) 
AND LO.OBJ#(+) = L.ID1 
AND RWO.OBJ#(+) = S.ROW_WAIT_OBJ#
and S.type!='BACKGROUND'
order by SID;

Tablas que desaparecen en el export

Hola

Vamos hoy con un pequeño expediente X. tenemos una base de datos 11g de la que queremos mover los datos a una base de datos de test. Lo primero que se nos ocurre es hacer un export de la misma con el comando exp  y llevarla a el entorno de test.

Sinembargo, al llegar a allí nos damos cuenta de que faltan objetos.

¿como es posible que nuestro export de toda la vida no haya sacado todas las tablas del esquema?

La respuesta es sencilla : por haber usado nuestro export de toda la vida

Aunque muchas veces sea mas comodo el uso del exp que de el expdp  ( especialmente por no tener que crear un directorio en la instancia),  el uso del exdp debería de ser obligatorio en nuestro día a dia,  ya que nos salvará de quebraderos de cabeza como este.

Pero,  seguramente  os estaréis preguntando a que es debido este problema.

 

Oracle 11g viene con la nueva funcionalidad deferred_segment-creation=TRUE activada por defecto.  Esto provoca que, al crear los objetos del esquema de la aplicacion en la base de datos no cree todos los segmentos de los mismos, sino que solamente creee los segmentos que contienen datos.

Nuestro «export de toda la vida» no es capaz de detectar esto,  exportandonos «solamente» los segmentos exsistentes en la base de datos,  sin embargo,  el nuevo expdp es mas listo,  y es capaz de exportar todos los objetos,  independientemente de que contengan datos  o no.

¿Como saber si tengo este tipo de tablas?

Podemos ver que objetos no están creados con la columna SEGMENT_CREATED  de las vistas del USER_TABLES, USER_INDEXES o USER_LOBS.

pregunta a ver que tablas  no tienen segmentos, si estásen una 11g seguramente te lleves una sorpresa.

select * from user_TABLES where  SEGMENT_CREATED='N'

Y en lo que se refiere a esta nueva funcionalidad de la 11g, cuidadito con ella,  por que,   seguramente nos traera algún que otro susto mas.

 

Bloqueos: TX – Allocate ITL Entry

Vamos a ver un evento de espera de fila que no es muy usual.

El TX – Allocate ITL Entry es un evento de espera que suele ir asociado no tanto a problemas del desarrollo sino a problemas de la arquitectura de la base de datos. Este evento se da cuando ha varias transacciones DML compitiendo por el mismo bloque de datos.

La solución para minimizar este tipo de eventos pasa por aumentar el valor del INITRANS del objeto.

Hay que recordar que, un aumento del initrans del obejto no es retroactivo, con lo que , en la mayoria de los casos habrá que  recnonstruir el objeto (a no ser que podamos asumir que solamente se lleve a cabo el cambio en los nuevos registros).

Si sigue apareciendo este evento, habría que aumentar el PCTFRE  del objeto (recordar que este ultimo aumento tendrá consecuencias sobre el uso del espacio de la base de datos).

 

Al final , como siempre , todo esto está perfectamente explicadito en el metalink ,concretamente en  «Enq: TX – Allocate ITL Entry [ID 1472175.1]»

Catalogos privados virtuales en rman

 

Una de las mejoras que ha imlementado Oracle en la gestión del catálogo es la cracion de catalogos privados virtuales . Esta funcionalidad prermite tener en la misma instancia de catálogo (normalment rcat) los catalogos de distintos entornos de manera totalmente estanca.

Para hacer este tipo de catálogo es necesario:

  1. Desde sqlplus crear un esquema en rcat para cada catalogo  (usuario virtual)
  2. Desde sqlplus damos permisos RECOVERY_CATALOG_OWNER al usuario virtual
  3. Desde el RMAN en el catalogo general y sin conectarse al target damos los permisos «GRANT REGISTER DATABASE TO» al usuario virtual
  4. Desde RMAN conectamos con el usuario virutal y ejecutamos «create virtual catalog;»
  5. Desde RMAN registramos la base de datos.

Los catálogos privados virtuales es una funcionalidad de la 11g, así pues, si quieres registrar una base de datos anterior a la 11g el paso 4 deberá de cambiarse por

SQL> CONNECT usuario_virtual/pass@rcat
SQL> exec owner_general.dbms_rcvcat.create_virtual_catalog;