Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Ausführen großer Abfragen im Hintergrund MS SQL

Aus meiner Sicht hat Ihr Server ein ernsthaftes Performance-Problem. Auch wenn wir davon ausgehen, dass keiner der Datensätze in der Abfrage

select some_col with (nolock) where id_col between 57000000 and 57001000

im Speicher war, sollte es keine 21 Sekunden dauern, um die wenigen Seiten nacheinander von der Festplatte zu lesen (Ihr gruppierter Index auf der id_col sollte nicht fragmentiert sein, wenn es sich um eine automatische Identität handelt und Sie nicht etwas Dummes getan haben, wie das Hinzufügen eines "desc" zur Indexdefinition).

Aber wenn Sie das nicht beheben können / wollen, wäre mein Rat, das Update in kleinen Paketen wie 100-1000 Datensätzen gleichzeitig durchzuführen (je nachdem, wie viel Zeit die Suchfunktion verbraucht). Eine Aktualisierung/Transaktion sollte nicht länger als 30 Sekunden dauern.

Sie sehen, dass jede Aktualisierung eine exklusive Sperre für alle Datensätze behält, die sie geändert hat, bis die Transaktion abgeschlossen ist. Wenn Sie keine explizite Transaktion verwenden, wird jede Anweisung in einem einzigen, automatischen Transaktionskontext ausgeführt, sodass die Sperren freigegeben werden, wenn die Aktualisierungsanweisung abgeschlossen ist.

Aber Sie können auf diese Weise immer noch in Deadlocks geraten, je nachdem, was die anderen Prozesse tun. Wenn sie mehr als einen Datensatz gleichzeitig modifizieren oder selbst wenn sie Lesesperren für mehrere Zeilen sammeln und halten, können Sie Deadlocks bekommen.

Um Deadlocks zu vermeiden, muss Ihre Update-Anweisung alle Datensätze sperren, die sie auf einmal ändern wird. Der Weg, dies zu tun, besteht darin, die einzelne Update-Anweisung (mit nur den wenigen Zeilen, die durch die id_col begrenzt sind) in eine serialisierbare Transaktion wie

zu platzieren
IF @@TRANCOUNT > 0
  -- Error: You are in a transaction context already

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

-- Insert Loop here to work "x" through the id range
  BEGIN TRANSACTION
    UPDATE SOMETABLE
      SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
      WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
  COMMIT
-- Next loop

-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
  UPDATE SOMETABLE
    SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
    WHERE [some_col] = 243 AND id_col >= x
COMMIT

Für jede Aktualisierung wird dies eine Aktualisierung/exklusive Schlüsselbereichssperre für die angegebenen Datensätze vornehmen (aber nur für sie, da Sie die Aktualisierung durch den gruppierten Indexschlüssel einschränken). Es wird warten, bis alle anderen Aktualisierungen für dieselben Datensätze abgeschlossen sind, dann seine Sperre erhalten (was eine Blockierung für alle anderen Transaktionen verursacht, aber immer noch nur für die angegebenen Datensätze), dann die Datensätze aktualisieren und die Sperre freigeben.

Die letzte zusätzliche Anweisung ist wichtig, da sie eine Schlüsselbereichssperre bis "unendlich" nimmt und somit sogar Einfügungen am Ende des Bereichs verhindert, während die Update-Anweisung ausgeführt wird.