Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Umgang mit Latenz in MySQL-Transaktionen

Sie sind aus dem Schneider, weil Sie nicht alles in einer großen Abfrage kapseln wollen, denn das wird auch nichts wirklich lösen, es macht es nur weniger wahrscheinlich.

Was Sie brauchen, sind Sperren für die Zeilen oder Sperren für den Index, wo die neue Zeile eingefügt werden würde.

Wie erhalten wir also exklusive Sperren?

Zwei Verbindungen, mysql1 und mysql2, die jeweils eine exklusive Sperre mit SELECT ... FOR UPDATE anfordern . Die Tabelle „history“ hat eine Spalte „user_id“, die indiziert ist. (Es ist auch ein Fremdschlüssel.) Es wurden keine Zeilen gefunden, daher scheinen beide normal fortzufahren, als ob nichts Ungewöhnliches passieren würde. Die user_id 2808 ist gültig, hat aber nichts im Verlauf.

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

... und ich bekomme meinen Prompt nicht zurück ... keine Antwort ... weil eine andere Sitzung auch gesperrt ist ... aber dann:

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

Dann gibt mysql1 sofort Erfolg bei der Einfügung zurück.

Query OK, 1 row affected (3.96 sec)

Alles, was übrig bleibt, ist für mysql1 COMMIT und auf magische Weise haben wir verhindert, dass ein Benutzer mit 0 Einträgen mehr als 1 Eintrag einfügt. Der Deadlock trat auf, weil in beiden Sitzungen inkompatible Dinge passieren mussten:mysql1 benötigte mysql2, um seine Sperre freizugeben, bevor es in der Lage sein würde, festzuschreiben, und mysql2 benötigte mysql1, um seine Sperre freizugeben, bevor es einfügen konnte. Irgendjemand muss diesen Kampf verlieren, und im Allgemeinen ist der Thread, der am wenigsten Arbeit geleistet hat, der Verlierer.

Aber was wäre, wenn bereits 1 oder mehr Zeilen vorhanden gewesen wären, als ich SELECT ... FOR UPDATE ausgeführt habe ? In diesem Fall wäre die Sperre auf den Zeilen gewesen, also versuchte die zweite Sitzung SELECT würde eigentlich das Warten auf SELECT blockieren bis die erste Sitzung sich für entweder COMMIT entschied oder ROLLBACK , zu diesem Zeitpunkt hätte die zweite Sitzung eine genaue Zählung der Anzahl der Zeilen (einschließlich aller in der ersten Sitzung eingefügten oder gelöschten) gesehen und hätte genau entscheiden können, dass der Benutzer bereits über das zulässige Maximum verfügt.

Sie können eine Rennbedingung nicht überbieten, aber Sie können sie aussperren.