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

mysql - créer un mécanisme similaire aux séquences d'Oracle

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;