Mysql
 sql >> Base de données >  >> RDS >> Mysql

Gestion de la latence dans les transactions MySQL

Vous êtes décroché de ne pas vouloir tout encapsuler dans une grande requête, car cela ne résoudra rien non plus, cela le rendra simplement moins probable.

Ce dont vous avez besoin, ce sont des verrous sur les lignes ou des verrous sur l'index où la nouvelle ligne serait insérée.

Alors, comment obtenir des verrous exclusifs ?

Deux connexions, mysql1 et mysql2, chacune demandant un verrou exclusif en utilisant SELECT ... FOR UPDATE . La table 'history' a une colonne 'user_id' qui est indexée. (Il s'agit également d'une clé étrangère.) Aucune ligne n'a été trouvée, elles semblent donc toutes deux se dérouler normalement, comme si rien d'inhabituel n'allait se produire. L'user_id 2808 est valide mais n'a rien dans l'historique.

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);

... et je ne reçois pas mon invite ... pas de réponse ... parce qu'une autre session a aussi un verrou ... mais alors :

mysql2> insert into history(user_id) values (2808);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

Ensuite, mysql1 renvoie immédiatement le succès de l'insertion.

Query OK, 1 row affected (3.96 sec)

Il ne reste plus que mysql1 à COMMIT et comme par magie, nous avons empêché un utilisateur avec 0 entrées d'insérer plus d'une entrée. Le blocage s'est produit parce que les deux sessions avaient besoin que des choses incompatibles se produisent :mysql1 avait besoin que mysql2 libère son verrou avant de pouvoir valider et mysql2 avait besoin que mysql1 libère son verrou avant de pouvoir insérer. Quelqu'un doit perdre ce combat, et généralement le fil qui a fait le moins de travail est le perdant.

Mais que se passe-t-il s'il y avait déjà 1 ou plusieurs lignes existantes lorsque j'ai fait le SELECT ... FOR UPDATE ? Dans ce cas, le verrou aurait été sur les lignes, donc la deuxième session pour essayer de SELECT bloquerait en fait l'attente du SELECT jusqu'à ce que la première session décide soit de COMMIT ou ROLLBACK , à ce moment-là, la deuxième session aurait vu un décompte précis du nombre de lignes (y compris celles insérées ou supprimées par la première session) et aurait pu décider avec précision que l'utilisateur disposait déjà du maximum autorisé.

Vous ne pouvez pas surpasser une condition de concurrence, mais vous pouvez les exclure.