Voici un exemple simple avec un FOR UPDATE verrou d'intention . Un verrou au niveau de la ligne avec le moteur INNODB. L'exemple montre quatre lignes pour les prochaines séquences disponibles qui ne souffriront pas de l'anomalie d'écart INNODB bien connue (le cas où des écarts se produisent après l'échec de l'utilisation d'un AUTO_INCREMENT).
Schéma :
-- drop table if exists sequences;
create table sequences
( id int auto_increment primary key,
sectionType varchar(200) not null,
nextSequence int not null,
unique key(sectionType)
) ENGINE=InnoDB;
-- truncate table sequences;
insert sequences (sectionType,nextSequence) values
('Chassis',1),('Engine Block',1),('Brakes',1),('Carburetor',1);
Exemple de code :
START TRANSACTION; -- Line1
SELECT nextSequence into @mine_to_use from sequences where sectionType='Carburetor' FOR UPDATE; -- Line2
select @mine_to_use; -- Line3
UPDATE sequences set nextSequence=nextSequence+1 where sectionType='Carburetor'; -- Line4
COMMIT; -- Line5
Idéalement vous n'avez pas de Line3
ou du code gonflé qui retarderait d'autres clients sur une attente de verrouillage. Ce qui signifie, obtenez votre prochaine séquence à utiliser, effectuez la mise à jour (la partie d'incrémentation) et COMMIT
, Dès que possible .
Ce qui précède dans une procédure stockée :
DROP PROCEDURE if exists getNextSequence;
DELIMITER $$
CREATE PROCEDURE getNextSequence(p_sectionType varchar(200),OUT p_YoursToUse int)
BEGIN
-- for flexibility, return the sequence number as both an OUT parameter and a single row resultset
START TRANSACTION;
SELECT nextSequence into @mine_to_use from sequences where sectionType=p_sectionType FOR UPDATE;
UPDATE sequences set nextSequence=nextSequence+1 where sectionType=p_sectionType;
COMMIT; -- get it and release INTENTION LOCK ASAP
set [email protected]_to_use; -- set the OUT parameter
select @mine_to_use as yourSeqNum; -- also return as a 1 column, 1 row resultset
END$$
DELIMITER ;
Test :
set @myNum:= -1;
call getNextSequence('Carburetor',@myNum);
+------------+
| yourSeqNum |
+------------+
| 4 |
+------------+
select @myNum; -- 4
Modifiez la procédure stockée en fonction de vos besoins, par exemple en n'ayant qu'un seul des 2 mécanismes de récupération du numéro de séquence (soit le paramètre OUT, soit le jeu de résultats). En d'autres termes, il est facile d'abandonner le OUT
notion de paramètre.
Si vous ne respectez pas la version ASAP du LOCK (qui n'est évidemment pas nécessaire après la mise à jour) et que vous procédez à l'exécution d'un code fastidieux avant la publication, les événements suivants peuvent se produire après un délai d'attente pour les autres clients en attente d'une séquence numéro :
ERREUR 1205 (HY000) :délai d'attente de verrouillage dépassé ; essayez de redémarrer la transaction
Espérons que ce ne sera jamais un problème.
show variables where variable_name='innodb_lock_wait_timeout';
Page du manuel MySQL pour innodb_lock_wait_timeout .
Sur mon système en ce moment, il a une valeur de 50 (secondes). Une attente de plus d'une seconde ou deux est probablement insupportable dans la plupart des situations.
Cette section de la sortie de la commande suivante est également intéressante pendant TRANSACTIONS :
SHOW ENGINE INNODB STATUS;