Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

mysql - fazendo um mecanismo semelhante às sequências do Oracle


Veja a seguir um exemplo simples com um FOR UPDATE bloqueio de intenção . Um bloqueio em nível de linha com o mecanismo INNODB. A amostra mostra quatro linhas para as próximas sequências disponíveis que não sofrerão com a conhecida anomalia INNODB Gap (o caso em que as lacunas ocorrem após o uso com falha de um 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 amostra:
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, você não tem uma Line3 ou código inchado, o que atrasaria outros clientes em um Lock Wait. Ou seja, obtenha sua próxima sequência para usar, execute a atualização (a parte de incremento) e COMMIT , O MAIS CEDO POSSÍVEL .

O acima em um procedimento armazenado:
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 ;

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

Modifique o procedimento armazenado de acordo com suas necessidades, como ter apenas 1 dos 2 mecanismos para recuperar o número de sequência (o parâmetro OUT ou o conjunto de resultados). Em outras palavras, é fácil abandonar o OUT conceito de parâmetro.

Se você não aderir ao lançamento o mais rápido possível do LOCK (o que obviamente não é necessário após a atualização) e continuar executando o código demorado, antes do lançamento, o seguinte poderá ocorrer após um período de tempo limite para outros clientes aguardando uma sequência número:

ERRO 1205 (HY000):Tempo limite de espera de bloqueio excedido; tente reiniciar a transação

Espero que isso nunca seja um problema.
show variables where variable_name='innodb_lock_wait_timeout';

Página de manual do MySQL para innodb_lock_wait_timeout .

No meu sistema no momento tem um valor de 50 (segundos). Uma espera de mais de um ou dois segundos é provavelmente insuportável na maioria das situações.

Também de interesse durante as TRANSAÇÕES é a seção da saída do seguinte comando:
SHOW ENGINE INNODB STATUS;