Oracle
 sql >> Base de données >  >> RDS >> Oracle

Un interblocage peut-il se produire avec la même méthode d'accès ?

L'"ordre" est déterministe de votre point de vue uniquement si vous incluez ORDER BY dans votre requête. Si c'est déterministe du point de vue du serveur est un détail de mise en œuvre, sur lequel il ne faut pas se fier.

En ce qui concerne le verrouillage, deux instructions DML identiques peuvent se bloquer (mais pas se bloquer). Par exemple :

CREATE TABLE THE_TABLE (
    ID INT PRIMARY KEY
);

Transaction A :

INSERT INTO THE_TABLE VALUES(1);

Transaction B :

INSERT INTO THE_TABLE VALUES(1);

À ce stade, la transaction B est bloquée jusqu'à ce que la transaction A soit validée ou annulée. Si A s'engage, le B échoue en raison d'une violation de PRIMARY KEY. Si A revient en arrière, B réussit.

Des exemples similaires peuvent être construits pour UPDATE et DELETE.

Le point important est que le blocage ne dépendra pas du plan d'exécution - quelle que soit la manière dont Oracle choisit d'optimiser votre requête, vous aurez toujours le même comportement de blocage. Vous voudrez peut-être lire sur les verrous automatiques dans les opérations DML pour plus d'informations.

Quant aux morts -locks, ils sont possibles à réaliser avec plusieurs instructions. Par exemple :

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

Ou, éventuellement avec des instructions qui modifient plus d'une ligne dans un ordre différent et un timing très malchanceux (quelqu'un pourrait-il le confirmer ?).

--- MISE À JOUR ---

En réponse à la mise à jour de votre question, permettez-moi de faire une observation générale :si des threads simultanés d'exécution verrouillent des objets dans l'ordre cohérent , les blocages sont impossibles. Cela est vrai pour tout type de verrouillage, qu'il s'agisse de mutex dans votre programme multithread moyen (par exemple, voir les réflexions de Herb Sutter sur les hiérarchies de verrouillage) ou de bases de données. Une fois que vous modifiez l'ordre de manière à ce que deux verrous soient "renversés", le risque de blocage est introduit.

Sans analyser l'index, vous mettez à jour (et verrouillez ) lignes dans un ordre, et avec l'index dans un autre. Donc, c'est probablement ce qui se passe dans votre cas :

  • Si vous désactivez l'analyse d'index pour les deux transactions simultanées , ils verrouillent tous les deux les lignes dans le même ordre [X], donc aucun interblocage n'est possible.
  • Si vous activez l'analyse d'index pour une seule transaction , ils ne verrouillent plus les lignes dans le même ordre, d'où le risque d'interblocage.
  • Si vous activez l'analyse d'index pour les deux transactions , puis à nouveau les deux verrouillent les lignes dans le même ordre, et un blocage est impossible (allez-y et essayez alter session set optimizer_index_cost_adj = 1; dans les deux sessions et vous verrez).

[X] Bien que je ne compte pas sur des analyses de table complètes ayant un ordre garanti - cela pourrait simplement être la façon dont Oracle actuel fonctionne dans ces circonstances spécifiques, et certains futurs Oracle ou des circonstances différentes pourraient produire un comportement différent.

Ainsi, la présence d'index est accessoire - le vrai problème est de commander. Il se trouve que le classement dans UPDATE peut être influencé par un index, mais si nous pouvions influencer le classement d'une autre manière, nous obtiendrions des résultats similaires.

Étant donné que UPDATE n'a pas ORDER BY, vous ne pouvez pas vraiment garantir l'ordre de verrouillage par UPDATE seul. Cependant, si vous séparez verrouillage de la mise à jour, alors vous pouvez garantir l'ordre de verrouillage :

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

Alors que votre code d'origine a provoqué des blocages dans mon environnement Oracle 10, le code suivant ne le fait pas :

Séance 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;
/

Séance 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;
/