sql >> Database teknologi >  >> RDS >> Mysql

Håndtering af latency i MySQL-transaktioner

Du har ikke lyst til at indkapsle alt i én stor forespørgsel, for det løser faktisk heller ikke noget, det gør det bare mindre sandsynligt.

Det, du har brug for, er låse på rækkerne eller låse på indekset, hvor den nye række vil blive indsat.

Så hvordan får vi eksklusive låse?

To forbindelser, mysql1 og mysql2, hver af dem anmoder om en eksklusiv lås ved hjælp af SELECT ... FOR UPDATE . Tabellen 'historik' har en kolonne 'user_id', som er indekseret. (Det er også en fremmednøgle.) Der er ingen rækker fundet, så de ser begge ud til at fortsætte normalt, som om der ikke vil ske noget usædvanligt. User_id 2808 er gyldig, men har intet i historikken.

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

... og jeg får ikke min prompt tilbage ... intet svar ... fordi en anden session også har en lås ... men så:

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

Så returnerer mysql1 straks succes på indsættelsen.

Query OK, 1 row affected (3.96 sec)

Det eneste, der er tilbage, er, at mysql1 skal COMMIT og på magisk vis forhindrede vi en bruger med 0 poster i at indsætte mere end 1 post. Deadlock opstod, fordi begge sessioner havde brug for inkompatible ting for at ske:mysql1 havde brug for mysql2 for at frigive sin lås, før det ville være i stand til at commit, og mysql2 havde brug for mysql1 for at frigive sin lås, før det ville være i stand til at indsætte. Nogen er nødt til at tabe den kamp, ​​og generelt er den tråd, der har gjort det mindste arbejde, taberen.

Men hvad nu hvis der allerede havde været 1 eller flere rækker, da jeg lavede SELECT ... FOR UPDATE ? I så fald ville låsen have været på rækkerne, så den anden session for at prøve at SELECT ville faktisk blokere for at vente på SELECT indtil den første session besluttede at enten COMMIT eller ROLLBACK , på hvilket tidspunkt den anden session ville have set en nøjagtig optælling af antallet af rækker (inklusive eventuelle indsatte eller slettede af den første session) og nøjagtigt kunne have besluttet, at brugeren allerede havde det maksimalt tilladte.

Du kan ikke overgå en løbstilstand, men du kan låse dem ude.




  1. Brug af en UUID som primær nøgle med Laravel 5

  2. Hvorfor genererer Entity Framework indlejrede SQL-forespørgsler?

  3. Sådan gendannes database ved hjælp af RMAN

  4. Leder du efter en enkel fuldtekstsøgning? Prøv MySQL InnoDB + CakePHP med Word Stemming