PL/SQL MÁGICO

Manejando Transacciones Bloqueadas en SQL

Objetivos

  • Describir las Secciones de Oracle.
  • Consultar las secciones activas.
  • Conocer los métodos para terminar y/o desconectar las sesiones activas.
  • Ver y terminar los bloqueos de Transacciones.

Las Sesiones de Oracle

Una sesión es la conexión establecida entre una aplicación y la Base de Datos Oracle, junto con los recursos y objetos asociados a esa conexión.

Es posible consultar y obtener información sobre las sesiones activas mediante las vistas V$SESSION y GV$SESSION. Estas vistas contienen, entre otros, los campos SID, SERIAL# e INST_ID, que son útiles para identificar y finalizar (kill) sesiones específicas cuando sea necesario.

SELECT  vs.sid
        , vs.serial#
        , vs.*
FROM    sys.v$session vs;
SELECT  gvs.sid
        , gvs.serial#
        , gvs.inst_id
        , gvs.*
FROM    sys.gv$session gvs;

Los Bloqueos en Transacciones

Los bloqueos en transacciones son mecanismos de control de concurrencia que utiliza el sistema de base de datos para proteger la integridad de los datos cuando múltiples sesiones intentan acceder o modificar los mismos recursos al mismo tiempo.

Un bloqueo ocurre cuando una transacción impide temporalmente que otra transacción lea o modifique un dato, hasta que la primera transacción finalice mediante COMMIT o ROLLBACK. Esto evita inconsistencias y conflictos en los datos.

Oracle proporciona varias vistas dinámicas que permiten monitorear los bloqueos de transacciones dentro de la base de datos. Una de las más utilizadas es V$LOCK, que muestra los bloqueos activos entre sesiones.

Sin embargo, esta vista no proporciona información directa sobre los objetos bloqueados (como el nombre de la tabla o el recurso específico). Por esta razón, suele complementarse con otras vistas como DBA_BLOCKERS, DBA_WAITERS, V$SESSION y V$ACCESS, las cuales permiten obtener una visión más completa de las sesiones involucradas y del origen del conflicto.

   1. Conectado con HR ejecutamos la consulta:

SELECT employee_id
        , first_name
        , last_name
        , email
FROM    hr.employees
WHERE   employee_id =   100;

2. Actualizamos el campo email, no ejecutamos COMMIT alguno y re ejecutamos la anterior consulta:

UPDATE  hr.employees
    SET email   =   email||'@company.com'
WHERE   employee_id =   100;

 3. Conectado con un segundo usuario (SYS en mi caso) ejecutamos los mismos script anteriores, notamos que la consulta no muestra la actualización del campo EMAIL y el update no concluye el procesamiento.

4. Ahora conectado con un tercel usuario (SYSTEM en mi caso) ejecutamos la siguiente consulta:

SELECT
        (
            SELECT  s.username
            FROM    sys.v$session s
            WHERE   s.sid=bker.sid
        )           AS  usuario_bloqueador,
        bker.SID    AS  sid_bloqueador,
        (
            SELECT  s.username
            FROM    sys.v$session s
            WHERE   s.sid=bked.sid
        )           AS  usuario_bloqueado,
        bked.SID    AS  sid_bloqueado
FROM    sys.v$lock bker
        JOIN    sys.v$lock bked
            ON  (
                bker.id1 = bked.id1
                AND     bker.id2 = bked.id2)
WHERE   bker.block = 1
AND     bked.request > 0;

La imagen muestra el resultado de una consulta que identifica sesiones bloqueadoras y sesiones bloqueadas en la base de datos.

En este caso específico:

USUARIO_BLOQUEADORSID_BLOQUEADORUSUARIO_BLOQUEADOSID_BLOQUEADO
HR742SYS1107

Esto indica que:

  • La sesión 742, perteneciente al usuario HR, está bloqueando un recurso.
  • La sesión 1107, perteneciente al usuario SYS, está esperando ese mismo recurso y por lo tanto se encuentra bloqueada.

La consulta funciona comparando registros de V$LOCK para encontrar sesiones que poseen un bloqueo (block = 1) y otras que están solicitando ese mismo recurso (request > 0), relacionándolas mediante los identificadores internos ID1 y ID2, que representan el recurso bloqueado. Luego consulta V$SESSION para obtener los nombres de los usuarios asociados a cada SID.


La Vista V$LOCKED_OBJECT

Oracle proporciona la vista dinámica V$LOCKED_OBJECT, la cual permite identificar con mayor detalle las transacciones que mantienen bloqueos en la base de datos. A diferencia de otras vistas, esta muestra el objeto específico que está bloqueado, lo que facilita determinar qué tabla u objeto está involucrado en el conflicto.

La siguiente consulta muestra las mismas sesiones bloqueadas más el objeto que contiene el bloqueo:

SELECT
        o.owner||'.'||o.object_name     AS  objeto
        ,o.object_type                  AS  tipo_bojeto
        ,s.username                     AS  usuario_conexion
        ,s.sid
        ,s.serial#
        ,s.status
        , DECODE(s.status, 'INACTIVE', 'BLOQUEADOR', 'ACTIVE', 'BLOQUEDADO', 'DESCONOCIDO') AS  DESR_USUARIO
FROM
        sys.gv$locked_object bo
        JOIN    sys.gv$session s
            ON  ( s.sid = bo.session_id)
        JOIN    sys.dba_objects o
            ON  (bo.object_id    =   o.object_id
                    AND bo.inst_id      =   s.inst_id);

Eliminando Bloqueos de Base Datos

Oracle ofrece varios métodos para desbloquear una transacción. Puede utilizar las sentencias:

ALTER SYSTEM KILL SESSION 'SID, SERIAL#' [IMMEDIATE];

La anterior sentencia revierte las transacciones en curso, libera los bloqueos y recupera parcialmente los recursos utilizados; si no incluimos la clausula IMMEDIATE la sesión es marcada como ‘marked for kill’ para que ésta se elimine tan pronto como le sea posible, teniendo en cuenta que dicho proceso puede tomar varios minutos en completarse.

ALTER SYSTEM DISCONNECT SESSION 'SID, SERIAL#' [POST_TRANSACTION IMMEDIATE];

La pasada sentencia es un método alternativo para eliminar transacciones bloqueadas. Mientras que KILL SESSION solicita a la sesión que se auto elimine automáticamente, DISCONNECT SESSION termina el proceso del servidor dedicado. Lo que es igual a terminar el proceso desde el sistema operativo.

La cláusula POST_TRANSACTION espera a que se completen las transacciones en curso para luego desconectar la sesión, la cláusula IMMEDIATE desconecta la sesión y las transacciones en curso se revierten inmediatamente.

Tener pendiente que en la sintaxis [ ]  indica que ambas cláusulas son opcionales, pero esto no es así, se deben especificar una o ambas, de lo contrario recibiría un error. Si ambas cláusulas son incluidas IMMEDIATE seria ignorada.

Ejemplos:

ALTER SYSTEM KILL SESSION '742, 58463' IMMEDIATE;
ALTER SYSTEM DISCONNECT SESSION '742, 58463' IMMEDIATE;

Las dos sentencias anteriores muestran como terminar la sesión bloqueadora.

SET SERVEROUTPUT    ON
DECLARE
    CURSOR  cur_sessions    IS
        SELECT
                vs.sid
                , vs.serial#
        FROM    sys.v$session vs
        WHERE   sid <>   SYS_CONTEXT('USERENV', 'SID')
        ;

BEGIN
    FOR i   IN  cur_sessions    LOOP
    BEGIN
        EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION '''||i.sid||', '||i.serial#||''' IMMEDIATE';

        DBMS_OUTPUT.PUT_LINE('SESSION '||i.sid||'-'||i.serial#||' KILLED');

    EXCEPTION
    WHEN OTHERS THEN
        BEGIN
            EXECUTE IMMEDIATE 'ALTER SYSTEM DISCONNECT SESSION '''||i.sid||', '||i.serial#||''' IMMEDIATE';

            DBMS_OUTPUT.PUT_LINE('SESSION '||i.sid||'-'||i.serial#||' DISCONNECTED');

        EXCEPTION
            WHEN OTHERS THEN
                NULL;
        END;
    END;
    END LOOP;
END;

El bloque PL/SQL anterior consulta todas las sesiones de base de datos diferentes a la sesión actual y utiliza las sentencias KILL SESSION y DISCONNECT SESSION para eliminarlas o desconectarlas.