sql >> Databasteknik >  >> RDS >> Mysql

Hantering av latens i MySQL-transaktioner

Du är ute efter att inte vilja kapsla in allt i en stor fråga, för det kommer faktiskt inte att lösa någonting heller, det gör det bara mindre troligt.

Det du behöver är lås på raderna, eller lås på indexet där den nya raden skulle infogas.

Så hur får vi exklusiva lås?

Två anslutningar, mysql1 och mysql2, var och en av dem begär ett exklusivt lås med SELECT ... FOR UPDATE . Tabellen 'historik' har en kolumn 'user_id' som är indexerad. (Det är också en främmande nyckel.) Det finns inga rader hittade, så de verkar båda fortsätta normalt som om inget ovanligt kommer att hända. User_id 2808 är giltigt men har ingenting i historiken.

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

... och jag får inte tillbaka min uppmaning ... inget svar ... eftersom en annan session har ett lås också ... men sedan:

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

Då returnerar mysql1 omedelbart framgång på infogningen.

Query OK, 1 row affected (3.96 sec)

Allt som återstår är att mysql1 ska COMMIT och magiskt sett förhindrade vi en användare med 0 poster från att infoga mer än 1 post. Deadlocket inträffade eftersom båda sessionerna behövde inkompatibla saker för att hända:mysql1 behövde mysql2 för att släppa sitt lås innan det skulle kunna commit och mysql2 behövde mysql1 för att släppa sitt lås innan det skulle kunna infogas. Någon måste förlora den kampen, och i allmänhet är den tråd som har gjort minst jobb förloraren.

Men tänk om det hade funnits 1 eller flera rader redan när jag gjorde SELECT ... FOR UPDATE ? I så fall skulle låset ha varit på raderna, så den andra sessionen för att försöka SELECT skulle faktiskt blockera väntan på SELECT tills den första sessionen bestämde sig för att antingen COMMIT eller ROLLBACK , vid vilken tidpunkt den andra sessionen skulle ha sett en korrekt räkning av antalet rader (inklusive eventuella infogade eller raderade av den första sessionen) och korrekt kunde ha beslutat att användaren redan hade det högsta tillåtna antalet.

Du kan inte överträffa ett lopp, men du kan låsa dem ute.




  1. Det går inte att starta mysql-servern i ubuntu

  2. Hur räknar man antalet förekomster av en karaktär i ett Oracle-varchar-värde?

  3. Ersätt nulls-värden i sql med select-satsen i mysql?

  4. Subtrahera månader från ett datum i PostgreSQL