Sobre os problemas de simultaneidade, você tem um 'fácil' forma de evitar problemas de simultaneidade no 2º método, dentro de sua transação faça um select na linha de artigos (o
For update
agora está implícito). Qualquer inserção simultânea no mesmo artigo não poderá obter esse mesmo bloqueio e aguardará por você. Com os novos níveis de isolamento padrão, mesmo sem usar o nível de serialização na transação, você não veria nenhuma inserção simultânea na tabela de votos até o final de sua transação. Portanto, sua SUM deve permanecer coerente ou parecer coerente . Mas se uma transação concorrente inserir um voto no mesmo artigo e confirmar antes de você (e este segundo não ver sua inserção), a última transação a ser confirmada substituirá o contador e você perderá 1 voto. Então execute um bloqueio de linha no artigo usando um select antes (e faça seu trabalho em uma transação, é claro). É fácil testar, abrir 2 sessões interativas no MySQL e iniciar transações com BEGIN.
Se você usar o gatilho, estará em uma transação por padrão. Mas acho que você deve executar também o select na tabela de artigos para fazer um bloqueio de linha implícito para gatilhos simultâneos em execução (mais difícil de testar).
- Não se esqueça dos acionadores de exclusão.
- Não se esqueça dos acionadores de atualização.
- Se você não usar gatilhos e permanecer no código, tome cuidado para que todas as consultas de inserção/exclusão/atualização em votos executem um bloqueio de linha no artigo correspondente antes da transação. Não é muito difícil esquecer um.
Último ponto:faça transações mais difíceis, antes de iniciar a transação use:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Dessa forma, você não precisa de bloqueios de linha em artigos, o MySQL detectará que ocorre uma gravação em potencial na mesma linha e bloqueará as outras transações até que você termine. Mas não use algo que você calculou a partir de uma solicitação anterior . A consulta de atualização estará aguardando uma liberação de bloqueio nos artigos, quando o bloqueio for liberado pela 1ª transação
COMMIT
a computação de SUM
deve ser feito novamente para contar. Portanto, a consulta de atualização deve conter o SUM
ou faça um acréscimo. update articles set nb_votes=(SELECT count(*) from vote) where id=2;
E aqui você verá que o MySQL é inteligente, um deadlock é detectado se 2 transações estiverem tentando fazer isso enquanto a inserção foi feita simultaneamente. Nos níveis de serialização não encontrei uma maneira de obter um valor errado com:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
insert into vote (...
update articles set nb_votes=(
SELECT count(*) from vote where article_id=xx
) where id=XX;
COMMIT;
Mas esteja pronto para lidar com a quebra de transação que você deve refazer.