Das Sperrverhalten der Datenbank steuern

Abgeschlossen

Standardmäßig bestimmt die Laufzeit von Business Central automatisch die Isolationsstufen, die beim Abfragen der Datenbank verwendet werden. AL-Entwickler können die Datenbankisolationsstufe allerdings für einzelne Lesevorgänge in einer Datensatzinstanz explizit steuern.

Die Isolationsstufe einer Transaktion bestimmt den Grad ihrer Isolierung von anderen Transaktionen, damit Probleme in gleichzeitig auftretenden Situationen verhindert werden. Die Isolationsstufe verbessert auf Datensatzebene die Integrität und Stabilität von Daten, wenn mehrere Transaktionen denselben Datensatz lesen. Es schützt eine Transaktion vor Auswirkungen durch andere Transaktionen, indem es Sperren setzt, das Lesen nicht festgeschriebener Daten verhindert oder Änderungen verhindert.

Datenbanksperren können für Leistungsprobleme eine Hauptursache sein. Wenn für den AL-Code weniger Sperren erforderlich sind, erhöht er die Leistung des Systems für Endbenutzer. Durch die Verwendung der Isolationsstufe der Datensatzinstanz können Sie die Leistung verbessern, indem Sie Datenbanksperren auf das Notwendige beschränken.

Die Laufzeit von Business Central bestimmt automatisch die Isolationsstufen, die beim Abfragen der Datenbank verwendet werden. Die Isolationsstufe einer Transaktion wird implizit durch Schreibvorgänge in einen Datensatz oder explizit über einen LockTable-Methodenaufruf erhöht, jeweils auf Tabellenbasis. Die erhöhte Isolationsstufe besteht für die gesamte Transaktion, sodass nachfolgend ausgeführter Code von der erhöhten Isolationsstufe betroffen ist, unabhängig davon, ob dies erforderlich oder beabsichtigt ist.

Das folgende Beispiel zeigt AL-Code mit Hinweisen zur SQL-Isolationsstufe, die bei Datenbanklesevorgängen mit Anmerkungen versehen sind und nur auf transaktionsbedingter Sperrung basieren.

```al-language

local procedure CurrentBehavior()
var
    cust: Record Customer;
    otherCust: Record Customer;
    curr: Record Currency;
begin
    cust.FindFirst(); // READUNCOMMITTED

    // Heighten isolation level for Customer table.
    cust.LockTable();
    cust.FindLast(); // UPDLOCK

    // Also impacts other instances of same table.
    otherCust.FindSet(); // UPDLOCK

    // But does not impact other tables.
    curr.Find(); // READUNCOMMITTED
end;
```

Mit der Einführung der Isolationsstufe für Datensatzinstanzen kann die Isolationsstufe für Lesevorgänge auf einer Datensatzinstanz explizit ausgewählt werden. Die Datensatzinstanz-Isolationsstufe überschreibt die Isolationsstufe der Transaktion für eine bestimmte Tabelle. Es ist möglich, die Isolationsstufe sowohl zu erhöhen als auch zu verringern, wobei der Effekt auf die Datensatzinstanz beschränkt bleibt und nicht über die gesamte Dauer der Transaktion anhält.

Im folgenden Beispiel wird AL-Code mit Hinweisen zur SQL-Isolationsstufe gezeigt, die bei Datenbanklesevorgängen mit Anmerkungen versehen sind, wobei die Isolationsstufe der Datensatzinstanz zum Überschreiben der Isolationsstufe der Transaktion verwendet wird.

```al-language

local procedure UsingReadIsolation()
var
    cust: Record Customer;
    otherCust: Record Customer;
    curr: Record Currency;
begin
    cust.FindFirst(); // READUNCOMMITTED

    // Heighten isolation level for Customer table.
    cust.LockTable();

    // Explicitly select another isolation level than the transaction's.
    otherCust.ReadIsolation := IsolationLevel::ReadUncommitted;

    otherCust.FindSet(); // READUNCOMMITED
end;
```

In der folgenden Liste werden die verschiedenen anwendbaren Isolationsstufen des IsolationLevel-Optionstyps beschrieben:

  • Standard

    • Verfolgt den Status der Transaktion. Es ist das Gleiche, als würde keine Leseisolation verwendet.
  • ReadUncommitted

    • Ermöglicht ungültige Lesevorgänge, was bedeutet, dass Zeilen gelesen werden können, die von anderen Transaktionen geändert, aber noch nicht festgeschrieben wurden. Dies akzeptiert keine Sperren und ignoriert Sperren von anderen Transaktionen.
  • ReadCommitted

    • Ermöglicht nur das Lesen festgeschriebener Daten. Mit anderen Worten: Es können keine Daten gelesen werden, die durch andere Transaktionen geändert, aber noch nicht festgeschrieben wurden. Dies garantiert jedoch nicht, dass gelesene Zeilen während der gesamten Transaktion konsistent bleiben.
  • RepeatableRead

    • Stellt sicher, dass alle Lesevorgänge stabil sind, indem geteilte Sperren für die gesamte Lebensdauer der Transaktion beibehalten werden. Die Transaktion kann keine Daten lesen, die von anderen Transaktionen geändert, aber noch nicht festgeschrieben wurden, und keine andere Transaktion kann Daten ändern, die von der aktuellen Transaktion gelesen wurden, bis die aktuelle Transaktion abgeschlossen wurde.
  • UpdLock

    • Liest zur Aktualisierung und verhindert, dass andere mit gleicher Absicht lesen.

Zuvor stellte AL nur explizite Kontrolle der Isolationsstufe über die Methode LockTable bereit, was sicherstellen würde, dass alle Lesevorgänge für den Rest der Transaktion UpdLock wären. Stattdessen kann der Code mit der Isolationsstufe der Datensatzinstanz explizit auf die Isolationsgarantien hinweisen, die er braucht, und nachfolgenden Code von seiner Ausführung unberührt lassen.

Das folgende Beispiel erhöht die Isolationsstufe für eine Datensatzinstanz vom Typ Sachposten. Es nimmt die Sperre für die letzte Zeile, während nachfolgende Lesevorgänge keine weiteren Sperren auslösen. Eine solche Verwendung ist in Fällen mit Ereignisabonnenten nützlich, bei denen Code in einen vorhandenen Geschäftslogikfluss eingefügt wird. Wo es nicht erwartet wurde, dass ein LockTable-Aufruf eingeführt wird, der dazu führt, dass nachfolgende Lesevorgänge für eine Tabelle gesperrt werden.

```al-language

// Gets the next "Entry No." and locks just last row.
// Without causing the rest of transaction to begin taking locks.
local procedure GetNextEntryNo(): Integer
var
    GLEntry: Record "G/L Entry";
begin
    GLEntry.ReadIsolation := IsolationLevel::UpdLock;
    GLEntry.FindLast();
    exit(GLEntry."Entry No." + 1)
end;
```

Innerhalb einer Transaktion ist es nicht möglich, die aktuelle in der Transaktion verwendete Isolationsstufe zu bestimmen. Wenn zuvor ausgeführter Code eine höhere Isolationsstufe ausgelöst hat, sind für das Zählen auf der gesamten Tabelle Sperren für die gesamte Tabelle erforderlich. Mit der Datensatzinstanz-Isolationsstufe können Sie zum Beispiel eine geschätzte Datensatzanzahl erhalten, ohne alle anderen daran zu hindern, Änderungen an der Tabelle vorzunehmen.

```al-language

local procedure GetEstimatedCount(tableno: Integer) : Integer
var
    rref: RecordRef;
begin
    rref.Open(tableno);
    rref.ReadIsolation := IsolationLevel::ReadUncommitted;
    exit(rref.Count);
end;
```

Bei Verwendung von FlowFields und dem Standardtransaktionsstatus wird der Status der Zieltabelle der FlowField-Formel verwendet, um die Isolationsstufe zu bestimmen, nicht den Zielstatus der Quelltabelle. Bei Verwendung der Datensatzinstanz-Isolationsstufe spielt die Zieltabelle keine Rolle, da die in der ReadIsolation-Methode angegebene Isolationsstufe verwendet wird. Betrachten Sie das folgende Beispiel.

```al-language

local procedure Foo()
var
    purchLine: Record "Purchase Line";
    curr: Record Currency;
begin
    curr.FindFirst();

    curr.CalcFields(curr."Vendor Outstanding Orders"); // READUNCOMMITED

    purchLine.LockTable();

    curr.CalcFields(curr."Vendor Outstanding Orders"); // UPDLOCK

    curr.ReadIsolation := IsolationLevel::ReadUncommitted;

    curr.CalcFields(curr."Vendor Outstanding Orders"); // READUNCOMMITTED
end;
```