Os campos de incremento automático do MySQL são redefinidos por si só

12

Temos uma tabela MySQL que possui um campo de incremento automático definido como INT (11). Esta tabela praticamente armazena uma lista de trabalhos que estão sendo executados em um aplicativo. A qualquer momento durante a vida útil do aplicativo, a tabela pode conter milhares de entradas ou ficar completamente vazia (ou seja, tudo terminou).

O campo não tem chave estrangeira para mais nada.

O incremento automático parece se redefinir aleatoriamente para zero, embora nunca tenhamos sido capazes de interceptar a redefinição.

O problema se torna evidente porque vemos o campo de incremento automático chegar a, digamos, 600.000 registros ou mais e, pouco tempo depois, o campo de incremento automático parece estar em execução nos 1000s baixos.

É quase como se o incremento automático se redefinisse se a tabela estivesse vazia.

Isso é possível e, se for, como faço para desativá-lo ou alterar a maneira como ele é redefinido?

Se não for, alguém tem uma explicação de por que pode estar fazendo isso?

Obrigado!

Hooligancat
fonte
Qual versão do MySQL? As transações ou replicação estão em uso?
thinice

Respostas:

26

O contador de incremento automático é armazenado apenas na memória principal, não no disco.

http://dev.mysql.com/doc/refman/4.1/en/innodb-auto-increment-handling.html

Por isso, quando o serviço (ou servidor) reiniciar, acontecerá o seguinte:

Após a inicialização do servidor, para a primeira inserção em uma tabela t, o InnoDB executa o equivalente a esta instrução: SELECT MAX (ai_col) FROM t FOR UPDATE;

O InnoDB incrementa em um o valor recuperado pela instrução e o atribui à coluna e ao contador de incremento automático da tabela. Se a tabela estiver vazia, o InnoDB utilizará o valor 1.

Portanto, em inglês simples, após o início do serviço MySQL, ele não tem idéia do valor do incremento automático para sua tabela. Portanto, quando você insere uma linha pela primeira vez, ela encontra o valor máximo do campo que usa incremento automático, adiciona 1 a esse valor e usa o valor resultante. Se não houver linhas, começará em 1.

Isso foi um problema para nós, pois estávamos usando o recurso de incremento automático da tabela e do mysql para gerenciar ordenadamente os IDs em um ambiente multithread em que os usuários estavam sendo redirecionados para um site de pagamento de terceiros. Portanto, tivemos que garantir que o ID que o terceiro recebesse e nos enviasse de volta fosse único e continuasse assim (e, é claro, existe a possibilidade de o usuário cancelar a transação após o redirecionamento).

Então, estávamos criando uma linha, obtendo o valor de incremento automático gerado, excluindo a linha para manter a tabela limpa e encaminhando o valor para o site de pagamento. O que acabamos fazendo para corrigir o problema da maneira como o InnoDB lida com os valores de IA foi o seguinte:

$query = "INSERT INTO transactions_counter () VALUES ();";
mysql_query($query);
$transactionId = mysql_insert_id();
$previousId = $transactionId - 1;
$query = "DELETE FROM transactions_counter WHERE transactionId='$previousId';";
mysql_query($query); 

Isso sempre mantém o transactionId mais recente gerado como uma linha na tabela, sem explodir desnecessariamente a tabela.

Espero que ajude qualquer pessoa que possa se deparar com isso.

Editar (2018-04-18) :

Como Finesse mencionado abaixo, parece que o comportamento disso foi modificado no MySQL 8.0+.

https://dev.mysql.com/worklog/task/?id=6204

A redação desse log de trabalho está com falhas, na melhor das hipóteses, no entanto, parece que o InnoDB nessas versões mais recentes agora suporta valores persistentes de autoinc durante as reinicializações.

-Grêmio

Gremio
fonte
Nada mal para um primeiro post, cara. +1.
ceejayoz
Doce pedaço de conhecimento lá Gremio. Obrigado!! Eu havia adiado isso completamente e arquivado o problema internamente como algo a ser revisado posteriormente, mas voltando a ele, sua solução funciona muito bem!
precisa
3

Ocorremos esse problema e descobrimos que, quando a tabela de otimização era executada em uma tabela vazia, o valor de incremento automático também era redefinido. Veja este relatório de erros do MySQL .

Como solução alternativa, você pode:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

Em vez de OPTIMIZE TABLE.

Parece que é isso que o MySQL faz internamente (sem definir o valor de incremento automático, obviamente)

Cajado
fonte
2

Apenas um tiro no escuro - se o aplicativo estiver usando a TRUNCATE TABLEpara esvaziar a tabela quando terminar o processamento, isso redefinirá o campo de incremento automático. Aqui está uma breve discussão sobre a questão. Embora esse link mencione que o InnoDB não redefine os auto_increments em um trunc, isso foi relatado como um bug e corrigido alguns anos atrás.

Supondo que meu palpite esteja correto, você pode mudar de truncar para excluir para corrigir o problema.

dsolimano
fonte
Eu não acho que estávamos usando TRUNCATE, mas tivemos que verificar para ter certeza. Até chegou ao ponto de verificar o nível do driver PHP para ter certeza. Mas não estávamos. Aprecie a solução possível.
Hooligancat
1

Somente uma redefinição explícita desse valor, ou uma eliminação / recriação desse campo ou outra operação violenta semelhante deve redefinir um contador de auto_increment. (O TRUNCATE era uma teoria realmente boa.) Parece impossível que você repita um INT de 32 bits quando o último valor que você testemunha é de apenas 600k. Definitivamente, não deve ser redefinido apenas porque a tabela se esvazia. Você tem um bug no mysql ou algo no seu código PHP. Ou o cara no cubículo ao lado está brincando com você.

Você pode depurar ativando o log binário , pois ele conterá instruções como esta em todo:

SET INSERT_ID=3747670/*!*/;

Então, pelo menos, você pode ver todos os detalhes do que está acontecendo com essa tabela, inclusive logo antes da redefinição do contador.

IcarusNM
fonte
obrigado pela dica de depuração. Tem sido um tempo desde que eu precisava para verificar em um Serverfault e eu aprecio sua dica
Hooligancat
0

ALTER TABLE table_name ENGINE = MyISAM

Funciona para mim. Nossa tabela é sempre mantida muito pequena, portanto não há necessidade do InnoDB.

Conserto rápido
fonte
Você precisa de transações?
Anthony Rutledge
0

O InnoDB não armazena o valor de incremento automático no disco, esquecendo-o quando o servidor MySQL é desligado. Quando o MySQL é iniciado novamente, o motor InnoDB restaura o valor de incremento automático desta forma: SELECT (MAX(id) + 1) AS auto_increment FROM table. Este é um erro que é fixo no MySQL versão 8.0.

Mude o mecanismo da tabela para resolver o problema:

ALTER TABLE table ENGINE = MyISAM

Ou atualize o servidor MySQL para a versão 8.0 quando for lançado.

Finesse
fonte