sql >> Base de Datos >  >> RDS >> Oracle

¿Puede ocurrir un interbloqueo con el mismo método de acceso?

El "orden" es determinista desde su perspectiva solo si incluye ORDER BY en su consulta. Si es determinista desde la perspectiva del servidor es un detalle de implementación, no se debe confiar en él.

En cuanto al bloqueo, dos sentencias DML idénticas pueden bloquearse (pero no interbloquearse) entre sí. Por ejemplo:

CREATE TABLE THE_TABLE (
    ID INT PRIMARY KEY
);

Transacción A:

INSERT INTO THE_TABLE VALUES(1);

Transacción B:

INSERT INTO THE_TABLE VALUES(1);

En este punto, la transacción B está detenida. hasta que la Transacción A se comprometa o retroceda. Si A se compromete, B falla debido a una violación de PRIMARY KEY. Si A retrocede, B tiene éxito.

Se pueden construir ejemplos similares para UPDATE y DELETE.

El punto importante es que el bloqueo no dependerá del plan de ejecución; no importa cómo elija Oracle optimizar su consulta, siempre tendrá el mismo comportamiento de bloqueo. Es posible que desee leer acerca de los bloqueos automáticos en operaciones DML para obtener más información.

En cuanto a muerto -bloqueos, son posibles de lograr con múltiples declaraciones. Por ejemplo:

A: INSERT INTO THE_TABLE VALUES(1);
B: INSERT INTO THE_TABLE VALUES(2);
A: INSERT INTO THE_TABLE VALUES(2);
B: INSERT INTO THE_TABLE VALUES(1); -- SQL Error: ORA-00060: deadlock detected while waiting for resource

O, posiblemente, con declaraciones que modifican más de una fila en un orden diferente y en un momento muy desafortunado (¿alguien podría confirmar esto?).

--- ACTUALIZAR ---

En respuesta a la actualización de su pregunta, permítame hacer una observación general:si los subprocesos de ejecución simultáneos bloquean los objetos en el orden consistente , los interbloqueos son imposibles. Esto es cierto para cualquier tipo de bloqueo, ya sean mutexes en su programa promedio de subprocesos múltiples (por ejemplo, vea los pensamientos de Herb Sutter sobre Jerarquías de bloqueo) o bases de datos. Una vez que cambia el orden de tal manera que se "invierten" dos bloqueos cualesquiera, se introduce la posibilidad de bloqueos mutuos.

Sin escanear el índice, está actualizando (y bloqueando ) filas en un orden y con el índice en otro. Entonces, esto es probablemente lo que sucede en tu caso:

  • Si desactiva el escaneo de índice para ambas transacciones simultáneas , ambos bloquean las filas en el mismo orden [X], por lo que no es posible ningún interbloqueo.
  • Si habilita el escaneo de índice para una sola transacción , ya no bloquean las filas en el mismo orden, por lo que existe la posibilidad de un interbloqueo.
  • Si habilita el análisis de índice para ambas transacciones , luego, de nuevo, ambos están bloqueando filas en el mismo orden, y un punto muerto es imposible (continúe e intente alter session set optimizer_index_cost_adj = 1; en ambas sesiones y verás).

[X] Aunque no confiaría en que los escaneos completos de la tabla tengan un orden garantizado, podría ser simplemente cómo funciona el Oracle actual en estas circunstancias específicas, y algún Oracle futuro o circunstancias diferentes podrían producir un comportamiento diferente.

Entonces, la presencia de índice es incidental:el verdadero problema es ordenar. Da la casualidad de que los pedidos en UPDATE pueden verse influenciados por un índice, pero si pudiéramos influir en los pedidos de otra manera, obtendríamos resultados similares.

Dado que ACTUALIZAR no tiene ORDEN POR, realmente no puede garantizar el orden de bloqueo solo con ACTUALIZAR. Sin embargo, si separa bloqueando la actualización, entonces puede garantizar el orden de bloqueo:

SELECT ... ORDER BY ... FOR UPDATE;

Si bien su código original provocó interbloqueos en mi entorno de Oracle 10, el siguiente código no:

Sesión 1:

declare
    cursor cur is select * from deadlock_test where a > 0 order by a for update;
begin
    while true loop
        for locked_row in cur loop
            update deadlock_test set a = -99999999999999999999 where current of cur;
        end loop;
        rollback;
    end loop;
end;
/

Sesión 2:

alter session set optimizer_index_cost_adj = 1;

declare
    cursor cur is select * from deadlock_test where a > 0 order by a for update;
begin
    while true loop
        for locked_row in cur loop
            update deadlock_test set a = -99999999999999999999 where current of cur;
        end loop;
        rollback;
    end loop;
end;
/