En général, la concurrence est difficile. Surtout avec 200 déclarations (je présume que vous ne faites pas que query = SELECT) ou même des transactions (en fait, chaque déclaration unique est enveloppée dans une transaction si elle n'est pas déjà dans une transaction).
Les concepts de solution générale sont (une combinaison de) ceux-ci:
être conscients que les blocages peuvent se produire, les attraper dans l'application, vérifiez la Error Codes pour class 40
ou 40P01
et essayer de nouveau la transaction.
Verrous de réserve. Utilisez SELECT ... FOR UPDATE
. Eviter les verrous explicites aussi longtemps que possible. Les verrous forceront les autres transactions à attendre la libération du verrou, ce qui nuit à la simultanéité, mais peut empêcher les transactions de se bloquer. Consultez l'exemple des blocages dans le chapitre 13. Surtout celui dans lequel la transaction A attend B et B attend A (le compte bancaire).
Choisissez un Isolation Level différent, par exemple un plus faible comme READ COMMITED
, si possible. Soyez conscient de LOST UPDATE
en mode READ COMMITED
. Empêchez-les avec REPEATABLE READ
.
Ecrivez vos instructions avec des verrous dans le même ordre dans CHAQUE transaction, par exemple par nom de table par ordre alphabétique.
LOCK/USE A -- Transaction 1
LOCK/USE B -- Transaction 1
LOCK/USE C -- Transaction 1
-- D not used -- Transaction 1
-- A not used -- Transaction 2
LOCK/USE B -- Transaction 2
-- C not used -- Transaction 2
LOCK/USE D -- Transaction 2
avec l'ordre de verrouillage général A B C D
. De cette façon, les transactions peuvent s'intercaler dans n'importe quel ordre relatif et avoir encore une bonne chance de ne pas se bloquer (en fonction de vos instructions, vous pouvez avoir d'autres problèmes de sérialisation). Les déclarations des transactions seront exécutées dans l'ordre spécifié par elles, mais il se peut que la transaction 1 exécute leur premier 2, puis xact 2 exécute le premier, puis 1 finit et enfin xact 2 finit. En outre, vous devez réaliser qu'une instruction impliquant plusieurs lignes n'est pas exécutée de manière atomique dans une situation simultanée.En d'autres termes, si vous avez deux déclarations A et B comportant plusieurs lignes, alors ils peuvent être exécutés dans cet ordre:
a1 b1 a2 a3 a4 b2 b3
mais pas comme un bloc d'un est suivi par des années b. La même chose s'applique à une instruction avec une sous-requête. Avez-vous regardé les plans de requête en utilisant EXPLAIN
?
Dans votre cas, vous pouvez essayer
UPDATE BALANCES WHERE ID IN (
SELECT ID FROM some_function() FOR UPDATE -- LOCK using FOR UPDATE
-- other transactions will WAIT/BLOCK temporarily on conc. write access
);
Si possible par ce que vous voulez faire, vous pouvez également utiliser SELECT ... FOR UPDATE SKIP LOCK, qui sautera données déjà verrouillé pour récupérer la concurrence, ce qui est perdu en attendant une autre transaction pour libérer un verrou (FOR UPDATE). Mais cela ne va pas appliquer une mise à jour aux lignes verrouillées, ce qui pourrait nécessiter votre logique d'application. Alors lancez-le plus tard (voir point 1).
lire également LOST UPDATE sur le LOST UPDATE
et SKIP LOCKED à propos SKIP LOCKED
. Une file d'attente peut être une idée dans votre cas, ce qui est parfaitement expliqué dans la référence SKIP LOCKED
, bien que les SGBD relationnels ne soient pas censés être des files d'attente.
HTH
Avez-vous essayé la syntaxe UPDATE ... FROM ...? –
Oui, j'ai essayé UPDATE .... FROM .... SELECT ... POUR LA MISE À JOUR mais pas de changement dans la trace de la pile. Cela rend le problème plus commun. – bbozo