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

Niveau d'isolement SERIALIZABLE dans Spring-JDBC

TL;DR :la détection des conflits de sérialisabilité s'est considérablement améliorée dans la page 9.1, donc mettez à niveau.

Il est difficile de comprendre à partir de votre description quel est le SQL réel et pourquoi vous vous attendez à obtenir une restauration. Il semble que vous ayez sérieusement mal compris l'isolement sérialisable, pensant peut-être qu'il teste parfaitement tous les prédicats, ce qui n'est pas le cas, surtout pas dans Pg 8.4.

SERIALIZABLE ne garantit pas parfaitement que les transactions s'exécutent comme si elles étaient exécutées en série - car cela reviendrait à un coût prohibitif du point de vue des performances si cela était possible. Il ne fournit qu'une vérification limitée. Exactement ce qui est vérifié et comment varie d'une base de données à l'autre et d'une version à l'autre, vous devez donc lire la documentation de votre version de votre base de données.

Des anomalies sont possibles, où deux transactions s'exécutant en SERIALIZABLE mode produit un résultat différent si ces transactions étaient réellement exécutées en série.

Lisez la documentation sur l'isolation des transactions dans Pg pour en savoir plus. Notez que SERIALIZABLE a radicalement changé de comportement dans Pg 9.1, alors assurez-vous de lire la version du manuel appropriée pour votre version Pg. Voici la version 8.4 . Lire en particulier 13.2.2.1. Isolation sérialisable contre véritable sérialisabilité . Comparez maintenant cela à la prise en charge de la sérialisation basée sur le verrouillage de prédicat grandement améliorée décrit dans le Documentation de la page 9.1 .

Il semble que vous essayiez d'exécuter quelque chose de logique comme ce pseudo-code :

count = query("SELECT count(*) FROM the_table");
if (count < threshold):
    query("INSERT INTO the_table (...) VALUES (...)");

Si c'est le cas, cela ne fonctionnera pas dans Pg 8.4 lorsqu'il est exécuté simultanément - c'est à peu près la même chose que l'exemple d'anomalie utilisé dans la documentation liée ci-dessus. Étonnamment, cela fonctionne réellement sur Pg 9.1; Je ne m'attendais même pas à ce que le verrouillage des prédicats de la version 9.1 intercepte l'utilisation des agrégats.

Vous écrivez que :

mais 8.4 ne détectera pas que les deux transactions sont interdépendantes, quelque chose que vous pouvez prouver trivialement en utilisant deux psql séances pour le tester. Ce n'est qu'avec les éléments de véritable sérialisabilité introduits dans la version 9.1 que cela fonctionnera - et franchement, j'ai été surpris que cela fonctionne dans la version 9.1.

Si vous voulez faire quelque chose comme appliquer un nombre maximum de lignes dans Pg 8.4, vous devez LOCK le tableau pour empêcher INSERT simultané s, en effectuant le verrouillage manuellement ou via une fonction de déclenchement . Le faire dans un déclencheur nécessitera intrinsèquement une promotion de verrouillage et entraînera donc souvent un blocage, mais fera le travail avec succès. C'est mieux fait dans l'application où vous pouvez émettre le LOCK TABLE my_table IN EXCLUSIVE MODE avant même d'obtenir SELECT à partir de la table, il a donc déjà le mode de verrouillage le plus élevé dont il aura besoin sur la table et ne devrait donc pas avoir besoin d'une promotion de verrouillage sujette aux blocages. L'EXCLUSIVE le mode de verrouillage est approprié car il permet SELECT s mais rien d'autre.

Voici comment le tester en deux sessions psql :

SESSION 1                               SESSION 2

create table ser_test( x text );

BEGIN TRANSACTION 
ISOLATION LEVEL SERIALIZABLE;


                                        BEGIN TRANSACTION 
                                        ISOLATION LEVEL SERIALIZABLE;

SELECT count(*) FROM ser_test ;

                                        SELECT count(*) FROM ser_test ;

INSERT INTO ser_test(x) VALUES ('bob');


                                        INSERT INTO ser_test(x) VALUES ('bob');

 COMMIT;

                                        COMMIT;

Lorsqu'il est exécuté sur Pg 9.1, le st commits succeeds then the second COMMIT` échoue avec :

regress=# COMMIT;
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

mais lorsqu'ils sont exécutés sur 8.4, les deux commits réussissent, car 8.4 n'avait pas tout le code de verrouillage de prédicat pour la sérialisabilité ajouté dans 9.1.