SQL-Server-Queues: Übernehmen und sicher fortsetzen
Erstellen Sie eine atomare Übernahme, verstehen Sie READPAST und verhindern Sie, dass veraltete Worker einen neuen Besitzer überschreiben.
Ein SELECT mit anschließendem separatem UPDATE ist keine sichere Arbeitsübernahme. Zwei Worker können dieselbe bereite Zeile auswählen, bevor einer den Status ändert. Die Queue benötigt einen atomaren Zustandswechsel und danach einen kurzen Commit. Die möglicherweise langsame fachliche Arbeit folgt erst nach diesem Abschluss.
Arbeit atomar übernehmen
Erstellen Sie die Übungstabelle einmal in einer Wegwerfdatenbank. Ihr Index unterstützt die Suche nach bereiten Aufgaben, ohne ständig die gesamte abgeschlossene Historie zu durchsuchen. Der Inhalt ist absichtlich klein. Größere Dokumente werden in echten Queues häufig über eine Referenz angebunden.
CREATE TABLE dbo.QueueDemo
( JobId bigint IDENTITY PRIMARY KEY, State char(1) NOT NULL,
ClaimToken uniqueidentifier NULL, LeaseUntil datetime2(3) NULL,
Payload nvarchar(100) NOT NULL );
CREATE INDEX IX_QueueDemo_Ready ON dbo.QueueDemo(State, JobId);
INSERT dbo.QueueDemo(State, Payload) VALUES ('R', N'first'), ('R', N'second');
Führen Sie die Übernahme unter READ COMMITTED ohne umgebende Transaktion aus. Die Prüfung verdeutlicht diese Grenze. UPDLOCK koordiniert konkurrierende Übernahmen; READPAST erlaubt das Überspringen gesperrter Zeilen. READCOMMITTEDLOCK verlangt Sperrsemantik auch bei aktiviertem Read-Committed-Versioning. Diese Kombination gehört zu diesem Isolationsvertrag und sollte nicht unverändert in eine SNAPSHOT-Transaktion übernommen werden.
IF @@TRANCOUNT <> 0 OR (@@OPTIONS & 2) = 2
THROW 50000, 'Use autocommit for this example.', 1;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
DECLARE @claimed table
(JobId bigint, ClaimToken uniqueidentifier, Payload nvarchar(100));
DECLARE @token uniqueidentifier = NEWID();
;WITH candidate AS
( SELECT TOP (1) * FROM dbo.QueueDemo
WITH (UPDLOCK, READPAST, READCOMMITTEDLOCK)
WHERE State = 'R' ORDER BY JobId )
UPDATE candidate
SET State = 'W', ClaimToken = @token,
LeaseUntil = DATEADD(minute, 5, SYSUTCDATETIME())
OUTPUT inserted.JobId, inserted.ClaimToken, inserted.Payload
INTO @claimed;
SELECT * FROM @claimed;
Die CTE ordnet die Kandidaten nach JobId. Das garantiert keine globale FIFO-Reihenfolge: Eine gesperrte frühere Aufgabe kann übersprungen werden, und die Abschlussreihenfolge hängt von der Bearbeitungsdauer ab. READPAST überspringt Zeilensperren, nicht jede Seiten- oder Schemasperre. Ein leeres Ergebnis bedeutet lediglich, dass dieser Versuch keine verfügbare Zeile übernommen hat.
OUTPUT INTO erfasst Kennung und Token derselben Aktualisierung. Das abschließende SELECT läuft nach dem erfolgreichen Autocommit-UPDATE. Anwendungen müssen Ausführungsfehler trotzdem als Fehler behandeln und dürfen nicht mit teilweise beobachteten Ausgaben weiterarbeiten. Testen Sie mehrere gleichzeitige Worker und protokollieren Sie jede übernommene JobId samt Token.
Verwaiste Arbeit sicher erkennen
Die Lease ist eine Ablaufregel. Sie hält einen alten Worker nicht physisch an. Ein blockierter Prozess kann nach Ablauf wieder aktiv werden. Deshalb braucht jeder Besitzerwechsel ein neues ClaimToken. Der Abschluss muss sowohl JobId als auch den aktuellen Token prüfen, damit ein alter Worker keinen neu übernommenen Versuch abschließt.
-- Parameters supplied from the successful claim:
-- @JobId bigint, @ClaimToken uniqueidentifier
UPDATE dbo.QueueDemo
SET State = 'D', LeaseUntil = NULL
WHERE JobId = @JobId AND ClaimToken = @ClaimToken AND State = 'W';
SELECT @@ROWCOUNT AS CompletedRows;
Null geänderte Zeilen bedeuten, dass Besitz oder Zustand nicht mehr passen. Behandeln Sie dies nicht stillschweigend als erfolgreichen Abschluss. Definieren Sie tokengeprüfte Heartbeats, maximale Laufzeiten und die Rückgabe abgelaufener Aufgaben in den bereiten Zustand. Auch der Wiederhersteller muss bedingt aktualisieren und den alten Token ungültig machen. Ein blindes UPDATE nach einem früheren SELECT erzeugt erneut das ursprüngliche Rennen.
Externe Effekte und Wiederholung
Eine Lease garantiert keine genau einmalige E-Mail, Zahlung oder externe API-Ausführung. Ein Worker kann den externen Effekt abschließen und vor der Statusspeicherung ausfallen. Nach Ablauf versucht ein anderer Worker dieselbe Arbeit erneut. Verwenden Sie eine stabile fachliche Vorgangskennung und eine idempotente Zieloperation oder ein dauerhaftes Abstimmungsverfahren.
Speichern Sie Versuchshistorie und begrenzen Sie wiederholte Fehler. Ein dauerhaft ungültiger Inhalt sollte mit nachvollziehbarer Diagnose in einen Fehlerzustand wechseln. Überwachen Sie Alter der ältesten bereiten Aufgabe, abgelaufene Leases, wiederholte Fehler und Abschlussdauer. Die reine Anzahl wartender Aufgaben übersieht eine einzelne dauerhaft festhängende wichtige Aufgabe.
Beenden Sie vor Einführung einen Worker nach der Übernahme, verzögern Sie einen über den Ablauf hinaus und verlieren Sie absichtlich eine Abschlussantwort. Prüfen Sie Wiederherstellbarkeit, die Wirkung veralteter Tokens und die Vermeidung doppelter fachlicher Effekte. Kontrollieren Sie zusätzlich, dass ein leerer Übernahmeversuch eine begrenzte Wartezeit im Worker auslöst statt einer endlosen schnellen Schleife. Solche Fehlerexperimente beschreiben die Zuverlässigkeit genauer als ein Durchsatztest mit ausschließlich gesunden Prozessen.
Technische Referenzen: Microsoft Learn: Table hints · Microsoft Learn: OUTPUT.