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

Lidando com a latência em transações MySQL


Você está fora do gancho por não querer encapsular tudo em uma consulta grande, porque isso também não resolverá nada, apenas tornará menos provável.

O que você precisa são bloqueios nas linhas ou bloqueios no índice onde a nova linha seria inserida.

Então, como podemos obter bloqueios exclusivos?

Duas conexões, mysql1 e mysql2, cada uma delas solicitando um bloqueio exclusivo usando SELECT ... FOR UPDATE . A tabela 'history' tem uma coluna 'user_id' que é indexada. (Também é uma chave estrangeira.) Não há linhas encontradas, então ambas parecem prosseguir normalmente como se nada incomum fosse acontecer. O user_id 2808 é válido, mas não tem nada no histórico.
mysql1> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql2> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql1> select * from history where user_id = 2808 for update;
Empty set (0.00 sec)

mysql2> select * from history where user_id = 2808 for update;
Empty set (0.00 sec)

mysql1> insert into history(user_id) values (2808);

... e não recebo meu prompt de volta ... sem resposta ... porque outra sessão também tem um bloqueio ... mas então:
mysql2> insert into history(user_id) values (2808);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

Então mysql1 imediatamente retorna sucesso na inserção.
Query OK, 1 row affected (3.96 sec)

Tudo o que resta é o mysql1 para COMMIT e magicamente, impedimos que um usuário com 0 entradas inserisse mais de 1 entrada. O deadlock ocorreu porque ambas as sessões precisavam que coisas incompatíveis acontecessem:mysql1 precisava que mysql2 liberasse seu bloqueio antes de poder confirmar e mysql2 precisava que mysql1 liberasse seu bloqueio antes de poder inserir. Alguém tem que perder essa luta, e geralmente o segmento que deu menos trabalho é o perdedor.

Mas e se já existissem 1 ou mais linhas quando eu fiz o SELECT ... FOR UPDATE ? Nesse caso, o bloqueio estaria nas linhas, então a segunda sessão para tentar SELECT na verdade bloquearia esperando pelo SELECT até que a primeira sessão decidiu ou COMMIT ou ROLLBACK , momento em que a segunda sessão teria visto uma contagem precisa do número de linhas (incluindo as inseridas ou excluídas pela primeira sessão) e poderia ter decidido com precisão que o usuário já tinha o máximo permitido.

Você não pode superar uma condição de corrida, mas pode bloqueá-los.