Wie kann ein Wert (Zähler) threadsicher abgefragt und erhöht werden? (Rennbedingungen vermeiden)

10

In einer Tabelle , wobei jede Zeile einen Zähler hat (nur ein ganzzahliger Wert ist ), muß ich den Stromwert zu erhalten und zu erhöhen es zugleich .

Eigentlich möchte ich das tun:

SELECT counter FROM table WHERE id=123
UPDATE table SET counter=counter+1 WHERE id=123

Dies als zwei Abfragen zu tun ist jedoch offensichtlich nicht threadsicher: Mehrere Prozesse, die dasselbe (in derselben Zeile) ausführen, erhalten möglicherweise denselben Zählerwert. Ich brauche sie alle einzigartig zu sein, so dass jeder Prozess die bekommen würde tatsächlichen aktuellen Wert und erhöhen sie um eins.

Ich kann mir eine Konstruktion vorstellen, bei der ich eine manuelle Sperre pro Zeile implementiere, aber ich frage mich, ob es einen einfacheren Weg gibt, dies zu tun.

RocketNuts
quelle
Transaktionen vielleicht nutzen?
Ypercubeᵀᴹ

Antworten:

15

Die Update-Anweisungen funktionieren einwandfrei ohne die Auswahl zuvor! Da einzelne Anweisungen per Definition sicher sind, führt selbst zwei gleichzeitig ausgeführte UPDATE-Abfragen dazu, dass die Zeile zweimal erhöht wird.

Wenn Sie tatsächlich den Wert für Ihr PHP-Skript auswählen, etwas damit tun und später diesen genauen Zählerwert aktualisieren möchten, können Sie Folgendes tun:

BEGIN;
SELECT `counter` FROM `table` WHERE `id` = 123 FOR UPDATE;
UPDATE `table` SET `counter` = `counter`+1 WHERE `id` = 123;
COMMIT;

Dadurch wird eine neue Transaktion gestartet, dann werden die zu aktualisierenden Zeilen ausgewählt und ausschließlich gesperrt. Sie können diese dann sicher aktualisieren, ohne sich Sorgen machen zu müssen, dass andere Clients ihren Inhalt ändern oder sogar auf die gesperrten Zeilen zugreifen. Schließlich müssen Sie Ihre Änderungen festschreiben.

Sie sollten auch etwas über Isolationsstufen lesen . Sie möchten wahrscheinlich keinen Wert wie READ UNCOMMITTEDIsolationsstufe. Alles andere sollte für diesen Anwendungsfall in Ordnung sein.

GhostGambler
quelle
Ich habe an anderen Stellen gelesen, dass das UPDATE table SET counter = counter + 1ausreichend atomar ist? Benötigen Sie noch die dazugehörigen Transaktionsanweisungen?
CMCDragonkai
@CMCDragonkai Ihre Abfrage allein ist atomar. Wenn Sie jedoch den vorherigen Wert auswählen und keine FOR UPDATETransaktionen verwenden, unterscheidet sich der ausgewählte Wert möglicherweise von dem Wert, der in der Aktualisierungsabfrage verwendet wurde. Meine Kombination von Abfragen sperrt die Zeile, sobald der Wert ausgewählt ist, und stellt daher sicher, dass dieser genaue Zählerwert in der Aktualisierungsabfrage verwendet wird.
GhostGambler
Ok, aber das ist nur notwendig, wenn ich andere Arbeiten als das Inkrementieren mache, oder? So wie es aussieht, ist eine einsame atomare Update-Abfrage gut genug, wenn das alles ist, was ich tun möchte?
CMCDragonkai
1
@CMCDragonkai Wenn Sie keine weitere Abfrage ausführen, die die Spalte berührt, sind Sie gut.
GhostGambler