Der beste Weg, um eine gleichzeitige tabellenbasierte Warteschlange zu implementieren

10

Ich habe eine Tabelle in MySQL, die eine Warteschlange von zu verarbeitenden Links darstellt. Die Links werden einzeln von einer externen App verarbeitet und am Ende gelöscht. Dies ist eine Warteschlange mit hohem Volumen, und ich habe mehrere Instanzen der Verarbeitungs-App, die auf mehrere Server verteilt sind.

Wie kann ich sicherstellen, dass jeder Datensatz nur von einer App ausgewählt wird? Gibt es eine Möglichkeit, den Datensatz zu kennzeichnen / zu sperren?

Im Moment erlaube ich jeder Instanz, nur einen bestimmten Satz von Datensätzen (basierend auf dem MOD ihrer ID) aufzunehmen, um zu vermeiden, dass zwei oder mehr denselben Link abrufen. Dies ist jedoch keine transparente Möglichkeit, die Warteschlangenverarbeitung zu erhöhen Geschwindigkeit nur durch Hinzufügen neuer Instanzen.

Miguel E.
quelle
Mein Mantra: "Stell dich nicht an, mach es einfach". Das heißt, anstatt eine Aufgabe in eine Warteschlange zu werfen, starten Sie einen Prozess, um die Aufgabe auszuführen.
Rick James

Antworten:

7

Erstens: MySQL ist eine der schlechtesten Software, um dies zu implementieren, insbesondere wenn es sehr dynamisch ist. Der Grund dafür ist, dass Engines wie MEMORY und MyISAM nur vollständige Tabellensperren haben, während geeignetere Engines wie InnoDB eine höhere Schreibstrafe haben (um ACID-Eigenschaften bereitzustellen) und für den Zugriff auf Datensätze optimiert sind, die räumlich und zeitlich geschlossen sind (diese sind im Speicher festgelegt) ). Es gibt auch kein gutes Änderungsbenachrichtigungssystem für MySQL - es muss als Abfrage implementiert werden. Es gibt Dutzende von Softwareteilen, die für diese Aufgabe optimiert sind .

Trotzdem habe ich gesehen, dass diese Art des Zugriffs erfolgreich implementiert wurde, wenn die Leistungs- / Effizienzanforderungen nicht sehr hoch sind. Viele Menschen können es sich nicht leisten, nur für einen kleinen Teil der Geschäftslogik eine vollständig separate Technologie einzuführen und zu warten.

SELECT FOR UPDATEist das, wonach Sie suchen - lesen Sie die Serialisierung. Während ein UPDATE / DELETE die Zeile während einer laufenden MYSQL-Transaktion immer sperrt, möchten Sie möglicherweise eine große Transaktion vermeiden, während der Prozess ausgeführt wird.

START TRANSACTION;
SELECT * FROM your_table WHERE state != 'PROCESSING' 
  ORDER BY date_added ASC LIMIT 1 FOR UPDATE;
if (rows_selected = 0) { //finished processing the queue, abort}
else {
UPDATE your_table WHERE id = $row.id SET state = 'PROCESSING'
COMMIT;

// row is processed here, outside of the transaction, and it can take as much time as we want

// once we finish:
DELETE FROM your_table WHERE id = $row.id and state = 'PROCESSING' LIMIT 1;
}

MySQL sorgt dafür, dass alle gleichzeitigen Auswahlen bis auf eine gesperrt werden, wenn Zeilen ausgewählt werden. Da dies zu vielen gesperrten Verbindungen gleichzeitig führen kann, halten Sie die anfängliche Transaktion so klein wie möglich und versuchen Sie, mehr als eine Zeile gleichzeitig zu verarbeiten.

Jynus
quelle
Vielen Dank. Denken Sie, dass die Leistung von einer größeren Sperre profitieren kann (indem Sie das LIMIT auf 10 ändern)?
Miguel E
@MiguelE Im Allgemeinen ist es umso besser, je mehr Zeit Sie mit der Verarbeitung verbringen und je weniger wahrscheinlich es ist, dass Sie mit anderen Transaktionen kollidieren. In einigen Fällen kann dies jedoch davon abhängen - es kann auch den gegenteiligen Effekt verursachen (mehr Transaktionen werden gesperrt). Testen Sie es immer zuerst. Es ist auch wichtig, die Tabelle angemessen zu indizieren. Andernfalls kann es in einigen Isolationsmodi zu einer vollständigen Tabellensperre kommen.
Jynus
1
Und es wäre wahrscheinlich eine gute Idee, das Datum zu verfolgen, an dem Sie mit der Verarbeitung der Zeile begonnen haben, nur für den Fall, dass der Prozess hängt und Sie einen Timeout-Mechanismus implementieren möchten.
Julian
2

Wie ich in diesem Artikel erklärt habe , hat MySQL 8 die Unterstützung für SKIP LOCKED und NO WAIT eingeführt.

SKIP LOCKED ist nützlich zum Implementieren von Jobwarteschlangen (auch als Stapelwarteschlangen bezeichnet), damit Sie Sperren überspringen können, die bereits durch andere gleichzeitige Transaktionen gesperrt sind.

NO WAIT ist nützlich, um zu vermeiden, dass gewartet wird, bis eine gleichzeitige Transaktion die Sperren aufhebt, an denen wir auch interessiert sind. Ohne KEINE WARTUNG müssen wir entweder warten, bis die Sperren freigegeben werden (zum Festschreibungs- oder Freigabezeitpunkt durch die Transaktion, die derzeit die Sperren enthält), oder die Sperrenerfassung läuft ab. Daher wirkt NO WAIT wie ein Sperrzeitlimit mit einem Wert von 0.

Weitere Informationen zu SKIP LOCK und NO WAIT finden Sie in diesem Artikel .

Vlad Mihalcea
quelle
0

Ich habe etwas Ähnliches mit Offline-DBCC-Prüfungen gemacht (zwei Server, die Sicherungswiederherstellungen durchführen, und dann eine DBCC-Prüfung). Ein Server sammelt gestern alle 31 Server-Backups und stellt sie in eine Warteschlange. Dann ziehen dieser Server und ein anderer Server aus dieser Warteschlange. Obwohl es nicht viele Server gibt, sollte die Methode dieselbe bleiben: Lassen Sie den Anwendungsserver eine Aktualisierungsabfrage für die Warteschlange ausführen, um ein Datums- / Zeitfeld und ein Feld "Anwendungsserver" mit dem Namen des Anwendungsservers oder besser noch der numerischen ID zu aktualisieren. Dies führt zu einer Sperre. Wenn bereits eine Sperre von einem anderen Server vorhanden ist, der die nächste Zeile erhält, wird diese blockiert und es wird gewartet, bis die andere App die nächste Zeile erhalten hat. Sie möchten dann, dass die App den neuesten Datensatz aus der Warteschlange für das App-Feld zurückzieht und die gewünschten Informationen abruft. Verwenden von MySQL '

Chris Woods
quelle