J'ai une application pilotée par PHP/5.2 qui utilise des transactions sous MySQL/5.1 afin de pouvoir annuler plusieurs insertions si une condition d'erreur est remplie. J'ai différentes fonctions réutilisables pour insérer différents types d'articles. Jusqu'ici tout va bien.Transactions rollback avec LOCK TABLES
Maintenant, j'ai besoin d'utiliser le verrouillage de table pour certains des inserts. Comme le suggère le manuel officiel, j'utilise SET autocommit=0
au lieu de START TRANSACTION
alors LOCK TABLES
n'émet pas de commit implicite. Et, comme documenté, déverrouillage des tables engage implicitement une transaction active:
Et est là le problème: si j'évite simplement UNLOCK TABLES
, il arrive que le deuxième appel à LOCK TABLES
engage les modifications en attente!
Il semble que le seul moyen est d'effectuer tous les LOCK TABLES
nécessaires dans une seule instruction. C'est un cauchemar de maintenance.
Ce problème a-t-il une solution de contournement raisonnable?
Voici un petit script de test:
DROP TABLE IF EXISTS test;
CREATE TABLE test (
test_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
random_number INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (test_id)
)
COLLATE='utf8_spanish_ci'
ENGINE=InnoDB;
-- No table locking: everything's fine
START TRANSACTION;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
ROLLBACK;
SELECT * FROM TEST ORDER BY test_id;
-- Table locking: everything's fine if I avoid START TRANSACTION
SET autocommit=0;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
ROLLBACK;
SELECT * FROM TEST ORDER BY test_id;
SET autocommit=1;
-- Table locking: I cannot nest LOCK/UNLOCK blocks
SET autocommit=0;
LOCK TABLES test WRITE;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
ROLLBACK;
UNLOCK TABLES; -- Implicit commit
SELECT * FROM TEST ORDER BY test_id;
SET autocommit=1;
-- Table locking: I cannot chain LOCK calls ether
SET autocommit=0;
LOCK TABLES test WRITE;
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
-- UNLOCK TABLES;
LOCK TABLES test WRITE; -- Implicit commit
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND()));
SELECT * FROM TEST ORDER BY test_id;
-- UNLOCK TABLES;
ROLLBACK;
SELECT * FROM TEST ORDER BY test_id;
SET autocommit=1;
Pourquoi avez-vous besoin de verrouillage? Quel est le vrai problème? –
J'ai besoin d'un verrouillage pour m'assurer qu'un seul processus est capable d'utiliser un numéro de séquence pour l'année en cours et qu'il ne reste aucun espace dans la séquence. Le vrai problème est que MySQL commet des ensembles de données non validés automatiquement lorsque vous essayez d'utiliser une fonctionnalité qui n'est pas consciente de la transaction, comme le verrouillage de table, ce qui annule tout l'intérêt d'utiliser des transactions. –
Vous ne pouvez pas utiliser SELECT .... FOR UPDATE; ? Fonctionne bien dans les transactions, pas de problème du tout. Utilisez un seul enregistrement pour la séquence et mettez à jour cet enregistrement chaque fois. –