Pergunta

Como você parar as condições de corrida no MySQL? o problema em questão é causado por um algoritmo simples:

  1. selecione uma linha da tabela
  2. Se ele não existir, insira-

e então você começa uma linha duplicada, ou se você evitá-lo através de chaves únicas / primário, um erro.

Agora normalmente eu pensaria transações ajudar aqui, mas porque a linha não existir, a transação não fazer realmente ajuda (ou estou faltando alguma coisa?).

sons LOCK TABLE como um exagero, especialmente se a tabela é atualizada várias vezes por segundo.

A única solução que eu posso pensar é GET_LOCK () para cada id diferente, mas não há uma maneira melhor? Será que não existem problemas de escalabilidade aqui também? E também, fazê-lo para cada mesa soa um pouco artificial, já que soa como um problema muito comum em bancos de dados de alta concorrência para mim.

Foi útil?

Solução

o que você quer é BLOQUEIO

ou se isso parece como excessiva sobre INSERIR IGNORE com um cheque que a linha foi realmente inserido.

Se você usar a palavra-chave IGNORE, erros que ocorrem durante a execução de INSERT declaração são tratadas como avisos em seu lugar.

Outras dicas

Parece-me que você deve ter um índice exclusivo em sua coluna id, então uma inserção repetida provocaria um erro em vez de ser cegamente aceito novamente.

Isso pode ser feito definindo o id como uma chave primária ou usando um índice exclusivo por si só.

Eu acho que a primeira pergunta que você precisa perguntar é por que você tem muitos tópicos fazendo o trabalho exato mesmo? Por que eles têm de inserir o exato a mesma linha?

Depois que ser respondida, acho que simplesmente ignorar os erros será a solução mais perfeita, mas medir ambas as abordagens (GET_LOCK v / s ignorar erros) e veja por si mesmo.

Não há nenhuma outra maneira que eu conheço. Por que você quer evitar erros? Você ainda tem que código para o caso quando um outro tipo de erro ocorre.

Como staticsan diz transações ajudam, mas, como eles geralmente estão implícitas, se duas inserções são correu por diferentes threads, eles serão tanto dentro um transações implícitas e ver vistas consistentes do banco de dados.

O bloqueio da tabela inteira é realmente um exagero. Para obter o efeito que você quer, você precisa de algo que a literatura chama de "fechaduras predicado". Ninguém jamais viu aqueles exceto impresso no papel que os estudos acadêmicos são publicados. A próxima melhor coisa são fechaduras nas "caminhos de acesso" para os dados (em algumas das DBMS: "bloqueios de página").

Alguns sistemas não-SQL permitem que você faça ambos (1) e (2) em uma única declaração, mais ou menos o que significa que as condições de corrida potenciais decorrentes de seu sistema operacional suspender a sua direita segmento de execução entre (1) e (2) , são totalmente eliminados.

No entanto, na ausência de bloqueios de predicados tais sistemas ainda vai precisar de recorrer a algum tipo de esquema de bloqueio e mais fina a "granularidade" (/ "scope") dos bloqueios que leva, o melhor para a concorrência.

(E para concluir:. De algumas DBMS - especialmente os que você não tem que pagar para - de fato, não oferecem granularidade do bloqueio mais fina do que "toda a tabela")

Em um nível técnico, uma transação vai ajudar aqui porque outros segmentos não verá a nova linha até que você confirmar a transação.

Mas, na prática, que não resolver o problema - ele só move -lo. Sua aplicação agora precisa verificar se a submissão falha e decidir o que fazer. I normalmente teria que reverter o que fez, e reiniciar a transação porque agora a linha será visível. Isto é como programador baseado em transação é suposto trabalho.

Eu corri para o mesmo problema e procurou a Net por um momento:)

Finalmente eu vim com uma solução semelhante ao método para criação de sistema de arquivos objetos em diretórios compartilhados (temporárias) para arquivos temporários de forma segura abertos:

$exists = $success = false;
do{
 $exists = check();// select a row in the table 
 if (!$exists)
  $success = create_record();
  if ($success){
   $exists = true;
  }else if ($success != ERROR_DUP_ROW){
    log_error("failed to create row not 'coz DUP_ROW!");
    break;
  }else{
    //probably other process has already created the record,
    //so try check again if exists
  }
}while(!$exists)

Do not ter medo de ocupado loop -. Normalmente ele irá executar uma ou duas vezes

Você evitar linhas duplicadas muito simplesmente colocando índices únicos em suas tabelas. Isso não tem nada a ver com fechaduras ou transações.

Você se importa se uma inserção falha porque é uma duplicata? Você precisa ser notificado se ele falhar? Ou é tudo que importa que a linha foi inserida, e não importa por quem ou quantas duplicatas inserções falhou?

Se você não se importa, então tudo que você precisa é INSERT IGNORE. Não há necessidade de pensar sobre transações ou bloqueios de tabela em tudo.

InnoDB tem nível de linha de bloquear automaticamente, mas isso só se aplica a atualizações e exclusões. Está certo que isso não se aplica a inserções. Você não pode bloquear o que ainda não existe!

Você pode LOCK explicitamente a tabela inteira. Mas se o seu objetivo é evitar duplicatas, então você está fazendo errado. Novamente, use um índice exclusivo.

Se houver um conjunto de alterações a serem feitas e você quer um resultado de tudo-ou-nada (ou mesmo um conjunto de tudo-ou-nada os resultados no resultado de tudo-ou-nada maior), então usar transações e pontos de salvamento. Em seguida, use ROLLBACK ou ROLLBACK TO SAVEPOINT *savepoint_name* a mudanças de desfazer, incluindo exclusões, atualizações e inserções.

tabelas LOCK não é um substituto para transações, mas é a sua única opção com tabelas MyISAM, que não suportam transações. Você também pode usá-lo com tabelas InnoDB se o bloqueio de nível de nível de linha não é suficiente. Consulte este página para mais informações sobre usando transações com as declarações da tabela de bloqueio.

Eu tenho um problema semelhante. Eu tenho uma tabela que na maioria das circunstâncias deve ter um valor TICKET_ID único, mas existem alguns casos em que vou ter duplicatas; não o melhor design, mas é o que é.

  1. cheques usuário A para ver se o bilhete é reservado, não é
  2. verifica o usuário B para ver se o bilhete é reservado, não é
  3. O usuário B insere um registro 'reservados' na tabela para esse bilhete
  4. Um usuário insere um registro 'reservados' na tabela para esse bilhete
  5. verificação B usuário para duplicado? Sim, é o meu recorde mais recente? Sim, deixá-lo
  6. Usuário Um cheque de duplicata? Sim, é o meu recorde mais recente? Não, excluí-lo

O usuário B tem reservado o bilhete, os relatórios do usuário um back que o bilhete tenha sido tomada por outra pessoa.

A chave no meu exemplo é que você precisa de um tie-breaker, no meu caso é o ID auto-incremento na linha.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top