sql >> Base de Datos >  >> RDS >> Mysql

mysql - haciendo un mecanismo similar a las secuencias de Oracle

El siguiente es un ejemplo simple con FOR UPDATE intention lock . Un bloqueo a nivel de fila con el motor INNODB. El ejemplo muestra cuatro filas para las próximas secuencias disponibles que no sufrirán la conocida Anomalía de espacio de INNODB (el caso en el que se producen espacios después de un uso fallido de AUTO_INCREMENT).

Esquema:

-- 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);

Código de muestra:

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

Idealmente, no tiene una Line3 o código hinchado que retrasaría a otros clientes en una espera de bloqueo. Es decir, obtenga su próxima secuencia para usar, realice la actualización (la parte incremental) y COMMIT , lo antes posible .

Lo anterior en un procedimiento almacenado:

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 ;

Prueba:

set @myNum:= -1;
call getNextSequence('Carburetor',@myNum);
+------------+
| yourSeqNum |
+------------+
|          4 |
+------------+
select @myNum; -- 4

Modifique el procedimiento almacenado según sus necesidades, como tener solo 1 de los 2 mecanismos para recuperar el número de secuencia (ya sea el parámetro OUT o el conjunto de resultados). En otras palabras, es fácil deshacerse del OUT concepto de parámetro.

Si no cumple con el lanzamiento ASAP de LOCK (que obviamente no es necesario después de la actualización) y continúa con la ejecución del código que consume mucho tiempo antes del lanzamiento, entonces puede ocurrir lo siguiente después de un período de tiempo de espera para otros clientes que esperan una secuencia número:

ERROR 1205 (HY000):Se excedió el tiempo de espera de bloqueo; intente reiniciar la transacción

Esperemos que esto nunca sea un problema.

show variables where variable_name='innodb_lock_wait_timeout';

Página del manual de MySQL para innodb_lock_wait_timeout .

En mi sistema en este momento tiene un valor de 50 (segundos). Una espera de más de uno o dos segundos probablemente sea insoportable en la mayoría de las situaciones.

También de interés durante las TRANSACCIONES es esa sección de la salida del siguiente comando:

SHOW ENGINE INNODB STATUS;