Arbeiten mit Änderungsnachverfolgung (SQL Server)

Gilt für:SQL ServerAzure SQL-DatenbankAzure SQL Managed Instance

Anwendungen, die die Änderungsnachverfolgung verwenden, müssen in der Lage sein, Überarbeitungen abzurufen, diese auf einen anderen Datenspeicher anzuwenden und die Quelldatenbank zu aktualisieren. In diesem Artikel wird beschrieben, wie diese Aufgaben ausgeführt werden, und auch die Rollenänderungsnachverfolgung spielt, wenn ein Failover auftritt und eine Datenbank aus einer Sicherung wiederhergestellt werden muss.

Abrufen von Änderungen mithilfe der Änderungsnachverfolgungsfunktionen

Beschreibt, wie mithilfe der Änderungsnachverfolgungsfunktionen Änderungen und Informationen zu den in einer Datenbank vorgenommenen Änderungen abgerufen werden können.

Informationen zu den Funktionen zur Änderungsnachverfolgung

Anwendungen können mit den folgenden Funktionen die in einer Datenbank vorgenommenen Änderungen sowie die Informationen zu diesen Änderungen abrufen:

CHANGETABLE(CHANGES ...)-Funktion
Diese Rowsetfunktion wird verwendet, um Änderungsinformationen abzufragen. Die Funktion fragt die in den internen Änderungsnachverfolgungstabellen gespeicherten Daten ab. Die Funktion gibt ein Resultset zurück, das die Primärschlüssel der Zeilen enthält, die sich geändert haben. Außerdem werden weitere Informationen zurückgegeben, z. B. der Vorgang, die aktualisierten Spalten und die Version der Zeile.

CHANGETABLE(CHANGES ...) verwendet die letzte Synchronisierungsversion als Argument. Die letzte Synchronisierungsversion wird mit der @last_synchronization_version-Variablen abgerufen. Die Semantik der letzten Synchronisierungsversion lautet wie folgt:

  • Der aufrufende Client hat alle Änderungen bis zur letzten Synchronisierungsversion (einschließlich) abgerufen.

  • CHANGETABLE(CHANGES ...) gibt also alle Änderungen zurück, die nach der letzten Synchronisierungsversion vorgenommen wurden.

    Die folgende Abbildung zeigt, wie CHANGETABLE(CHANGES ...) verwendet wird, um Änderungen abzurufen.

    Diagram that shows an example of change tracking query output.

    In diesem Beispiel wurde Client A zuletzt um 9:30 Uhr synchronisiert, während Client B zuletzt um 10:30 Uhr synchronisiert wurde. Um 10:00 Uhr und erneut um 11:00 Uhr wurden mehrere Änderungen an den Daten vorgenommen. Diese überarbeitungen Änderungen werden unten zusammengefasst.

    CHANGETABLE(CHANGES...) Ausgabe - 11:30 Uhr

    Client A wurde zuletzt um 9:30 Uhr synchronisiert.

    Produkt-ID Vorgang Spalten
    139 Aktualisieren Name, Preis
    140 Löschen -
    141 „Insert“ -

    Client B wurde zuletzt um 10:30 Uhr synchronisiert.

    Produkt-ID Vorgang Spalten
    139 Aktualisieren Preis
    140 Löschen -
    141 Aktualisieren Preis

CHANGE_TRACKING_CURRENT_VERSION()-Funktion
Wird verwendet, um die aktuelle Version abzurufen, die beim nächsten Abfragen von Änderungen verwendet wird. Diese Version stellt die Version der letzten Transaktion dar, für die ein Commit ausgeführt wurde.

CHANGE_TRACKING_MIN_VALID_VERSION()-Funktion
Wird verwendet, um die mindestens gültige Version abzurufen, die ein Client haben kann und dennoch gültige Ergebnisse von CHANGETABLE() abrufen kann. Der Client muss die Version der letzten Synchronisierung mit dem Wert abgleichen, der von dieser Funktion zurückgegeben wird. Wenn die Version der letzten Synchronisierung niedriger ist als die von dieser Funktion zurückgegebene Version, kann der Client keine gültigen Ergebnisse von CHANGETABLE() abrufen und muss neu initialisiert werden.

Abrufen von Anfangsdaten

Damit eine Anwendung Änderungen abrufen kann, muss sie zunächst die Anfangsdaten und die Synchronisierungsversion abfragen. Die Anwendung muss die entsprechenden Daten direkt aus der Tabelle abrufen und dann CHANGE_TRACKING_CURRENT_VERSION() zum Abrufen der Anfangsversion verwenden. Diese Version wird beim ersten Abrufen von Änderungen an CHANGETABLE(CHANGES ...) übergeben.

Das folgende Beispiel zeigt, wie die Anfangsversion der Synchronisierung und das Anfangsdataset abgerufen werden.

declare @synchronization_version bigint;

-- Obtain the current synchronization version. This will be used next time that changes are obtained.
SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

-- Obtain initial data set.
SELECT
    P.ProductID, P.Name, P.ListPrice
FROM
   SalesLT.Product AS P;

Verwenden der Änderungsnachverfolgungsfunktionen zum Abrufen von Änderungen

Verwenden Sie CHANGETABLE(CHANGES...), um die geänderten Zeilen für eine Tabelle und Informationen zu den Änderungen abzurufen. Die folgende Abfrage ruft beispielsweise Änderungen für die SalesLT.Product Tabelle ab.

declare @last_synchronization_version bigint;

SELECT
    CT.ProductID, CT.SYS_CHANGE_OPERATION,
    CT.SYS_CHANGE_COLUMNS, CT.SYS_CHANGE_CONTEXT
FROM
    CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT;

In der Regel möchte ein Client nicht nur die Primärschlüssel, sondern die neuesten Daten für eine Zeile abrufen. In diesem Fall führt eine Anwendung die Ergebnisse der CHANGETABLE(CHANGES ...)-Funktion mit den Daten in der Benutzertabelle zusammen. Beispiel: Bei der folgenden Abfrage wird die Funktion mit der SalesLT.Product -Tabelle verknüpft, um die Werte der Name -Spalte und der ListPrice -Spalte abzurufen. Beachten Sie, dass OUTER JOINverwendet wird. Dies ist erforderlich, um sicherzustellen, dass die Änderungsinformationen für die Zeilen zurückgegeben werden, die aus der Benutzertabelle gelöscht wurden.

SELECT
    CT.ProductID, P.Name, P.ListPrice,
    CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS,
    CT.SYS_CHANGE_CONTEXT
FROM
    SalesLT.Product AS P
RIGHT OUTER JOIN
    CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT
ON
    P.ProductID = CT.ProductID;

Verwenden Sie die CHANGE_TRACKING_CURRENT_VERSION()-Funktion wie im folgenden Beispiel gezeigt, um die in der nächsten Änderungsenumeration zu verwendende Version abzurufen.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

Beim Abrufen von Änderungen muss eine Anwendung sowohl CHANGETABLE(CHANGES ...) als auch CHANGE_TRACKING_CURRENT_VERSION() verwenden, wie im folgenden Beispiel gezeigt.

-- Obtain the current synchronization version. This will be used the next time CHANGETABLE(CHANGES...) is called.
SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

-- Obtain incremental changes by using the synchronization version obtained the last time the data was synchronized.
SELECT
    CT.ProductID, P.Name, P.ListPrice,
    CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS,
    CT.SYS_CHANGE_CONTEXT
FROM
    SalesLT.Product AS P
RIGHT OUTER JOIN
    CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT
ON
    P.ProductID = CT.ProductID;

Versionsnummern

Eine Datenbank mit aktivierter Änderungsnachverfolgung verfügt über einen Versionszähler, der hochgezählt wird, wenn Änderungen an den nachverfolgten Tabellen vorgenommen werden. Jede geänderte Zeile verfügt über eine ihr zugeordnete Versionsnummer. Wenn eine Anforderung zur Abfrage von Änderungen an eine Anwendung gesendet wird, wird eine Funktion aufgerufen, die eine Versionsnummer angibt. Die Funktion gibt Informationen über alle Änderungen zurück, die ab dieser Version vorgenommen wurden. In gewisser Hinsicht entspricht die Änderungsnachverfolgungsversion dem Konzept des rowversion -Datentyps.

Überprüfen der letzten synchronisierten Version

Informationen über Änderungen werden für einen beschränkten Zeitraum beibehalten. Dieser Zeitraum wird mit dem CHANGE_RETENTION-Parameter festgelegt, der als Teil von ALTER DATABASE angegeben werden kann.

Die für CHANGE_RETENTION angegebene Zeit bestimmt, wie häufig alle Anwendungen Änderungen aus der Datenbank anfordern müssen. Wenn eine Anwendung einen Wert für last_synchronization_version hat, der älter als die mindestens gültige Synchronisierungsversion für eine Tabelle ist, kann diese Anwendung keine gültige Änderungsenumeration ausführen. Das liegt daran, dass einige Änderungsinformationen möglicherweise bereinigt wurden. Bevor eine Anwendung Änderungen mithilfe von CHANGETABLE(CHANGES ...) abruft, muss die Anwendung den Wert für last_synchronization_version überprüfen, den sie an CHANGETABLE(CHANGES ...)übergeben soll. Wenn der Wert von last_synchronization_version ungültig ist, muss diese Anwendung alle Daten neu initialisieren.

Im folgenden Beispiel wird gezeigt, wie die Gültigkeit des last_synchronization_version -Werts für die einzelnen Tabellen überprüft wird.

-- Check individual table.
IF (@last_synchronization_version < CHANGE_TRACKING_MIN_VALID_VERSION(
                                   OBJECT_ID('SalesLT.Product')))
BEGIN
  -- Handle invalid version and do not enumerate changes.
  -- Client must be reinitialized.
END;

Wie im folgenden Beispiel gezeigt, kann die Gültigkeit des last_synchronization_version -Werts für alle Tabellen in der Datenbank überprüft werden.

-- Check all tables with change tracking enabled
IF EXISTS (
  SELECT 1 FROM sys.change_tracking_tables
  WHERE min_valid_version > @last_synchronization_version )
BEGIN
  -- Handle invalid version & do not enumerate changes
  -- Client must be reinitialized
END;

Verwenden der Spaltennachverfolgung

Die Spaltennachverfolgung ermöglicht Anwendungen, Daten statt für die gesamte Zeile nur für die Spalten abzurufen, die geändert wurden. Nehmen Sie z. B. an, eine Tabelle hat ein oder mehrere große Spalten, in denen selten Änderungen vorgenommen werden, sowie andere Spalten, in denen häufig Änderungen vorgenommen werden. Ohne die Spaltennachverfolgung kann eine Anwendung nur die Änderung einer Zeile erkennen, sodass alle Daten, einschließlich der Daten in den großen Spalten, synchronisiert werden müssten. Mit der Spaltennachverfolgung kann eine Anwendung ermitteln, in welcher Spalte Daten geändert wurden, und nur die geänderten Daten synchronisieren.

Die Spaltennachverfolgungsinformationen sind in der SYS_CHANGE_COLUMNS-Spalte enthalten, die von der CHANGETABLE(CHANGES ...)-Funktion zurückgegeben wird.

Die Spaltennachverfolgung kann verwendet werden, sodass NULL für eine Spalte zurückgegeben wird, die nicht geändert wurde. Wenn die Spalte in NULL geändert werden kann, muss eine separate Spalte zurückgegeben werden, um anzugeben, ob die Spalte geändert wurde.

Im folgenden Beispiel lautet NULL die CT_ThumbnailPhoto Spalte, wenn sich diese Spalte nicht geändert hat. Der Wert dieser Spalte kann jedoch auch NULL lauten, da sie in NULL geändert werden kann. In diesem Fall kann die Anwendung mit der Spalte CT_ThumbNailPhoto_Changed angeben, ob die Spalte geändert wurde.

DECLARE @PhotoColumnId int = COLUMNPROPERTY(
    OBJECT_ID('SalesLT.Product'),'ThumbNailPhoto', 'ColumnId');

SELECT
    CT.ProductID, P.Name, P.ListPrice, -- Always obtain values.
    CASE
           WHEN CHANGE_TRACKING_IS_COLUMN_IN_MASK(
                     @PhotoColumnId, CT.SYS_CHANGE_COLUMNS) = 1
            THEN ThumbNailPhoto
            ELSE NULL
      END AS CT_ThumbNailPhoto,
      CHANGE_TRACKING_IS_COLUMN_IN_MASK(
                     @PhotoColumnId, CT.SYS_CHANGE_COLUMNS) AS
                                   CT_ThumbNailPhoto_Changed,
     CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS,
     CT.SYS_CHANGE_CONTEXT
FROM
     SalesLT.Product AS P
INNER JOIN
     CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT
ON
     P.ProductID = CT.ProductID AND
     CT.SYS_CHANGE_OPERATION = 'U';

Abrufen konsistenter und korrekter Ergebnisse

Zum Abrufen der Änderungsdaten für eine Tabelle sind mehrere Schritte erforderlich. Inkonsistente oder falsche Ergebnisse können zurückgegeben werden, wenn bestimmte Probleme nicht berücksichtigt und behandelt werden.

Um beispielsweise die Änderungen abzurufen, die an einer Sales Tabelle und SalesOrders Tabelle vorgenommen wurden, führt eine Anwendung die folgenden Schritte aus:

  1. Überprüfung der letzten Synchronisierungsversion mit CHANGE_TRACKING_MIN_VALID_VERSION()

  2. Abrufen der Version, die beim nächsten Abrufen von Änderungen verwendet werden kann, mit CHANGE_TRACKING_CURRENT_VERSION()

  3. Rufen Sie die Änderungen für die Sales Tabelle mithilfe von CHANGETABLE(CHANGES ...).

  4. Rufen Sie die Änderungen für die SalesOrders Tabelle mithilfe von CHANGETABLE(CHANGES ...).

In der Datenbank werden zwei Prozesse ausgeführt, die sich auf die von den oben genannten Schritten zurückgegebenen Ergebnisse auswirken können:

  • Der im Hintergrund ausgeführte Cleanupprozess entfernt Änderungsnachverfolgungsinformationen, die älter sind als die angegebene Beibehaltungsdauer.

    Beim Cleanupprozess handelt es sich um einen eigenen, im Hintergrund ausgeführten Prozess, der die Beibehaltungsdauer verwendet, die bei der Konfiguration der Änderungsnachverfolgung für die Datenbank angegeben wurde. Das Problem liegt darin, dass der Cleanupprozess genau in dem Zeitraum nach der Überprüfung der letzten Synchronisierungsversion und vor dem Aufruf von CHANGETABLE(CHANGES ...) ausgeführt werden kann. Eine zuletzt gültige Synchronisierungsversion ist möglicherweise nicht mehr gültig, wenn die Änderungen abgerufen werden. Aus diesem Grund kann es hier zu falschen Ergebnissen kommen.

  • Laufende DML-Vorgänge erfolgen in den Tabellen "Vertrieb" und SalesOrders "Tabelle", z. B. den folgenden Vorgängen:

    • Mit der CHANGE_TRACKING_CURRENT_VERSION()-Funktion können Änderungen an den Tabellen vorgenommen werden, nachdem die Version für die nächste Aufzählung von Änderungen abgerufen wurde. In diesem Fall werden möglicherweise mehr Änderungen zurückgegeben als erwartet.

    • Eine Transaktion kann sich in der Zeit zwischen dem Aufruf zum Abrufen von Änderungen aus der Sales Tabelle und dem Aufruf zum Abrufen von Änderungen aus der SalesOrders Tabelle commiten. Daher könnten die Ergebnisse für die SalesOrder Tabelle fremdschlüsselwert haben, der in der Sales Tabelle nicht vorhanden ist.

Zur Handhabung dieser aufgelisteten Fälle wird die Verwendung der Momentaufnahmeisolation empfohlen. Hierdurch können Sie die Konsistenz der Änderungsinformationen sicherstellen und Racebedingungen im Zusammenhang mit dem im Hintergrund ausgeführten Cleanupprozess vermeiden. Wenn Sie keine Snapshot-Transaktionen verwenden, kann die Entwicklung einer Anwendung, die die Änderungsnachverfolgung verwendet, erheblich mehr Aufwand erfordern.

Verwenden der Momentaufnahmeisolation

Die Änderungsnachverfolgung wurde für die Verwendung mit der Momentaufnahmeisolation optimiert. Die Momentaufnahmeisolation muss für die Datenbank aktiviert werden. Alle zum Abrufen von Änderungen erforderlichen Schritte müssen in eine Momentaufnahmetransaktion eingeschlossen werden. Dadurch wird sichergestellt, dass alle Änderungen, die an Daten vorgenommen werden, beim Abrufen von Änderungen für die Abfragen innerhalb der Momentaufnahmetransaktion nicht sichtbar sind.

Führen Sie die folgenden Schritte aus, um Daten in einer Momentaufnahmetransaktion abzurufen:

  1. Legen Sie die Transaktionsisolationsstufe auf MOMENTAUFNAHME fest, und starten Sie die Transaktion.

  2. Überprüfen Sie die letzte Synchronisierungsversion mit CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Rufen Sie die Version, die bei der nächsten Aufzählung von Änderungen verwendet wird, mit CHANGE_TRACKING_CURRENT_VERSION() ab.

  4. Abrufen der Änderungen für die Sales Tabelle mithilfe von CHANGETABLE(CHANGES ...)

  5. Abrufen der Änderungen für die SalesOrders Tabelle mithilfe von CHANGETABLE(CHANGES ...)

  6. Führen Sie einen Commit für die Transaktion aus.

Folgendes ist zu beachten, wenn alle Schritte zum Abrufen von Änderungen innerhalb einer Momentaufnahmetransaktion erfolgen:

  • Wenn die letzte Synchronisierungsversion nach der Überprüfung der letzten Synchronisierungsversion bereinigungsfähig ist, sind die Ergebnisse aus CHANGETABLE(CHANGES ...) weiterhin gültig, da die von der Bereinigung ausgeführten Löschvorgänge nicht innerhalb der Transaktion sichtbar sind.

  • Alle Änderungen, die an der Sales Tabelle oder der SalesOrders Tabelle vorgenommen werden, nachdem die nächste Synchronisierungsversion abgerufen wurde, sind nicht sichtbar, und die Aufrufe von CHANGETABLE(CHANGES ...) geben niemals Änderungen mit einer späteren Version zurück, als die von CHANGE_TRACKING_CURRENT_VERSION() zurückgegeben wurde. Die Konsistenz zwischen der Sales Tabelle und der SalesOrders Tabelle wird ebenfalls beibehalten, da die Transaktionen, die im Zeitraum zwischen Aufrufen von CHANGETABLE(CHANGES ...) zugesichert wurden, nicht sichtbar sind.

Das folgende Beispiel zeigt, wie die Momentaufnahmeisolation für eine Datenbank aktiviert wird.

-- The database must be configured to enable snapshot isolation.
ALTER DATABASE AdventureWorksLT
    SET ALLOW_SNAPSHOT_ISOLATION ON;

Eine Momentaufnahmetransaktion wird wie folgt verwendet:

SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRAN
  -- Verify that version of the previous synchronization is valid.
  -- Obtain the version to use next time.
  -- Obtain changes.
COMMIT TRAN

Weitere Informationen zu Snapshot-Transaktionen finden Sie unter SET TRANSACTION ISOLATION LEVEL (Transact-SQL).

Bereinigen und Momentaufnahmeisolation

Das Aktivieren der Momentaufnahmeisolation und der Änderungsnachverfolgung in derselben Datenbank oder in zwei verschiedenen Datenbanken innerhalb derselben Instanz kann dazu führen, dass der Bereinigungsprozess abgelaufene Zeilen verlässt sys.syscommittab , wenn eine geöffnete Transaktion in der Datenbank mit Snapshotisolation vorhanden ist. Dies kann passieren, da der Bereinigungsprozess der Änderungsnachverfolgung bei der Durchführung der Bereinigung eine instanzweite Untergrenze (die sichere Bereinigungsversion) berücksichtigt. Dadurch wird sichergestellt, dass der automatische Bereinigungsprozess für die Änderungsnachverfolgung keine Zeilen entfernt, die möglicherweise von der geöffneten Transaktion in der Datenbank mit aktivierter Snapshotisolation erforderlich sind. Behalten Sie die Isolation von zugesicherten Momentaufnahmen bei, und Speichern von Momentaufnahmen so kurz wie möglich, um sicherzustellen, dass abgelaufene Zeilen sys.syscommittab zeitnah bereinigt werden.

Alternativen zur Snapshotisolation

Es gibt Alternativen zur Verwendung der Momentaufnahmeisolation, die jedoch einen größeren Aufwand erfordern, um sicherzustellen, dass alle Anwendungsanforderungen erfüllt werden. Führen Sie die folgenden Schritte aus, um sicherzustellen, dass die last_synchronization_version gültig ist und die Daten nicht vom Bereinigungsprozess entfernt werden, bevor Änderungen abgerufen werden:

  1. Überprüfen Sie den Wert last_synchronization_version nach dem Aufrufen der CHANGETABLE()-Funktion.

  2. Überprüfen Sie den Wert last_synchronization_version bei jeder Abfrage zum Abrufen von Änderungen mit CHANGETABLE().

Änderungen können nach dem Abrufen der Synchronisierungsversion für die nächste Aufzählung auftreten. Dieses Problem lässt sich auf zwei Arten lösen: Welche Option Sie verwenden, hängt von der Anwendung ab und wie diese die Nebeneffekte des jeweiligen Ansatzes handhabt:

  • Ignorieren Sie Änderungen, deren Version neuer ist als die neue Synchronisierungsversion.

    Dieser Ansatz hat den Nebeneffekt, dass eine neue oder aktualisierte Zeile übersprungen wird, wenn diese vor der neuen Synchronisierungsversion erstellt oder aktualisiert wurde und danach ebenfalls aktualisiert wird. Wenn eine neue Zeile vorhanden ist, kann ein Problem mit der referenziellen Integrität auftreten, wenn eine Zeile in einer anderen Tabelle vorhanden war, die erstellt wurde, die auf die übersprungene Zeile verweist. Wenn eine aktualisierte Zeile vorhanden ist, wird die Zeile übersprungen und erst nach dem nächsten Mal synchronisiert.

  • Berücksichtigen Sie alle Änderungen, auch die, deren Version neuer ist als die neue Synchronisierungsversion.

    Die Zeilen, deren Version neuer ist als die neue Synchronisierungsversion, werden bei der nächsten Synchronisation erneut abgerufen. Dies muss von der Anwendung entsprechend berücksichtigt werden.

Zusätzlich zu den beiden oben genannten Optionen können Sie abhängig vom Vorgang einen Ansatz entwerfen, der beide Optionen kombiniert. Sie möchten beispielsweise, dass eine Anwendung, für die Änderungen am besten ignoriert werden sollten, die neuer sind als die nächste Synchronisierungsversion, in der die Zeile erstellt oder gelöscht wurde, jedoch nicht ignoriert werden.

Hinweis

Zur Auswahl des richtigen Ansatzes für die Anwendung, wenn Sie die Änderungsnachverfolgung (oder benutzerdefinierte Nachverfolgungsmechanismen) verwenden, sind umfangreiche Analysen erforderlich. Aus diesem Grund ist es viel einfacher, die Momentaufnahmeisolation zu verwenden.

So behandelt die Änderungsnachverfolgung Änderungen an einer Datenbank

Einige Anwendungen, die die Änderungsnachverfolgung verwenden, führen die bidirektionale Synchronisierung mit einem anderen Datenspeicher aus. Das heißt, Änderungen, die in der SQL Server-Datenbank vorgenommen werden, werden im anderen Datenspeicher aktualisiert, und Änderungen, die im anderen Speicher vorgenommen werden, werden in der SQL Server-Datenbank aktualisiert.

Zur Aktualisierung der lokalen Datenbank mit Änderungen von einem anderen Datenspeicher muss die Anwendung die folgenden Vorgänge ausführen:

  • Überprüfung auf Konflikte.

    Zu einem Konflikt kommt es, wenn die gleichen Daten in beiden Datenspeichern gleichzeitig geändert werden. Die Anwendung muss überprüfen können, ob solche Konflikte vorliegen, und genügend Informationen erfassen, um den Konflikt zu beheben.

  • Speichern von Anwendungskontextinformationen.

    Die Anwendung speichert Daten mit Änderungsnachverfolgungsinformationen. Diese Informationen stehen beim Abrufen von Änderungen aus der lokalen Datenbank neben anderen Änderungsnachverfolgungsinformationen zur Verfügung. Ein gängiges Beispiel dieser Kontextinformationen ist die ID des Datenspeichers, in dem die Änderung vorgenommen wurde.

Zur Ausführung der oben genannten Vorgänge kann eine Synchronisierungsanwendung die folgenden Funktionen verwenden:

  • CHANGETABLE(VERSION...)

    Beim Vornehmen von Änderungen kann eine Anwendung diese Funktion zur Überprüfung auf Konflikte verwenden. Die Funktion ruft die letzten Änderungsnachverfolgungsinformationen für eine angegebene Zeile in einer änderungsnachverfolgten Tabelle ab. Die Änderungsnachverfolgungsinformationen schließen die Version der letzten Änderung der Zeile ein. Die Anwendung kann dann mithilfe dieser Informationen ermitteln, ob die Zeile seit der letzten Synchronisierung der Anwendung geändert wurde.

  • WITH CHANGE_TRACKING_CONTEXT

    Eine Anwendung kann diese Klausel verwenden, um Kontextdaten zu speichern.

Nach Konflikten suchen

In einem bidirektionales Synchronisierungsszenario muss die Clientanwendung bestimmen, ob eine Zeile seit dem letzten Erhalt der Änderungen nicht aktualisiert wurde.

Das folgende Beispiel zeigt, wie die Überprüfung auf Konflikte mit der CHANGETABLE(VERSION ...)-Funktion effizient und ohne separate Abfrage ausgeführt wird. Im Beispiel wird von CHANGETABLE(VERSION ...) die SYS_CHANGE_VERSION für die von @product idbestimmte Zeile angegeben. CHANGETABLE(CHANGES ...) kann die gleichen Informationen abrufen, aber das wäre weniger effizient. Wenn der Wert SYS_CHANGE_VERSION der Zeile größer als der Wert der @last_sync_versionZeile ist, liegt ein Konflikt vor. Wenn ein Konflikt vorliegt, wird die Zeile nicht aktualisiert. Die ISNULL() -Prüfung ist erforderlich, da möglicherweise keine Änderungsinformationen für die Zeile verfügbar sind. Es wären keine Änderungsinformationen vorhanden, wenn die Zeile seit der Aktivierung der Änderungsnachverfolgung nicht aktualisiert wurde oder die Änderungsinformationen bereinigt wurden.

-- Assumption: @last_sync_version has been validated.
UPDATE SalesLT.Product
SET ListPrice = @new_listprice
FROM SalesLT.Product AS P
WHERE ProductID = @product_id
    AND @last_sync_version >= ISNULL((
            SELECT CT.SYS_CHANGE_VERSION
            FROM CHANGETABLE(VERSION SalesLT.Product, (ProductID), (P.ProductID)) AS CT
            ), 0);

Mit dem folgenden Code kann die Anzahl der aktualisierten Zeilen überprüft werden, und es können weitere Informationen über den Konflikt ermittelt werden.

-- If the change cannot be made, find out more information.
IF (@@ROWCOUNT = 0)
BEGIN
    -- Obtain the complete change information for the row.
    SELECT
        CT.SYS_CHANGE_VERSION, CT.SYS_CHANGE_CREATION_VERSION,
        CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS
    FROM
        CHANGETABLE(CHANGES SalesLT.Product, @last_sync_version) AS CT
    WHERE
        CT.ProductID = @product_id;

    -- Check CT.SYS_CHANGE_VERSION to verify that it really was a conflict.
    -- Check CT.SYS_CHANGE_OPERATION to determine the type of conflict:
    -- update-update or update-delete.
    -- The row that is specified by @product_id might no longer exist 
    -- if it has been deleted.
END

Festlegen von Kontextinformationen

Die WITH CHANGE_TRACKING_CONTEXT-Klausel ermöglicht die Speicherung von Kontextinformationen zusammen mit den Änderungsinformationen. Diese Informationen können dann aus der Spalte SYS_CHANGE_CONTEXT abgerufen werden, die von CHANGETABLE(CHANGES ...) zurückgegeben wird.

Kontextinformationen werden in der Regel verwendet, um die Quelle der Änderungen zu identifizieren. Wenn die Quelle einer Änderung identifiziert werden kann, können diese Informationen von einem Datenspeicher verwendet werden, um das Abrufen von Änderungen bei der nächsten Synchronisierung zu vermeiden.

-- Try to update the row and check for a conflict.
WITH CHANGE_TRACKING_CONTEXT (@source_id)
UPDATE
  SalesLT.Product
SET
  ListPrice = @new_listprice
FROM
  SalesLT.Product AS P
WHERE
  ProductID = @product_id AND
    @last_sync_version >= ISNULL (
    (SELECT CT.SYS_CHANGE_VERSION FROM CHANGETABLE(VERSION SalesLT.Product,
    (ProductID), (P.ProductID)) AS CT),
       0);

Sicherstellen konsistenter und korrekter Ergebnisse

Eine Anwendung muss bei der Überprüfung des Werts von @last_sync_version den Cleanupprozess berücksichtigen. Grund hierfür ist, dass Daten möglicherweise nach dem Aufruf von CHANGE_TRACKING_MIN_VALID_VERSION(), jedoch vor Durchführung des Updates entfernt werden.

Sie sollten die Momentaufnahmeisolation verwenden und die Änderungen innerhalb einer Momentaufnahmetransaktion vornehmen.

-- Prerequisite is to ensure ALLOW_SNAPSHOT_ISOLATION is ON for the database.

SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRAN
    -- Verify that last_sync_version is valid.
    IF (@last_sync_version <
CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID('SalesLT.Product')))
    BEGIN
       RAISERROR (N'Last_sync_version too old', 16, -1);
    END
    ELSE
    BEGIN
        -- Try to update the row.
        -- Check @@ROWCOUNT and check for a conflict.
    END;
COMMIT TRAN;

Hinweis

Es besteht die Möglichkeit, dass die bei der Momentaufnahmetransaktion aktualisierte Zeile bereits in einer anderen Transaktion aktualisiert wurde, nachdem die Momentaufnahmetransaktion gestartet wurde. In diesem Fall tritt ein Momentaufnahmeisolations-Updatekonflikt auf und bewirkt, dass die Transaktion beendet wird. Wiederholen Sie in diesem Fall das Update. Dies führt dann dazu, dass ein Änderungsnachverfolgungskonflikt erkannt wird und keine Zeilen geändert werden.

Änderungsnachverfolgung und Datenwiederherstellung

Bei Anwendungen, für die eine Synchronisierung erforderlich ist, muss der Fall berücksichtigt werden, dass eine für die Änderungsnachverfolgung aktivierte Datenbank eine frühere Version der Daten wiederherstellt. Dies kann auftreten, nachdem eine Datenbank aus einer Sicherung wiederhergestellt wurde, wenn ein Failover zu einem asynchronen Datenbankspiegel vorhanden ist oder ein Fehler beim Verwenden des Protokollversands auftritt. Das folgende Beispiel veranschaulicht dieses Szenario:

  1. Tabelle T1 wird geändert, und die mindest gültige Version für die Tabelle ist 50.

  2. Eine Clientanwendung synchronisiert die Daten bei Version 100 und ruft Informationen über alle Änderungen zwischen Version 50 und 100 ab.

  3. Nach Version 100 werden zusätzliche Änderungen an der Tabelle T1 vorgenommen.

  4. In Version 120 ist ein Fehler aufgetreten, und der Datenbankadministrator stellt die Datenbank mit Datenverlust wieder her. Nach der Wiederherstellung enthält die Tabelle Daten bis einschließlich Version 70, und die minimale synchronisierte Version ist weiterhin 50.

    Dies bedeutet, dass der synchronisierte Datenspeicher über Daten verfügt, die nicht mehr im primären Datenspeicher vorhanden sind.

  5. T1 wird häufig aktualisiert. Die aktuelle Version ist daher 130.

  6. Die Clientanwendung synchronisiert erneut und gibt als zuletzt synchronisierte Version 100 aus. Die Überprüfung dieser Version durch den Client ist erfolgreich, da 100 größer als 50 ist.

    Der Client ruft die Änderungen zwischen Version 100 und 130 ab. An diesem Punkt ist dem Client nicht bewusst, dass die Änderungen zwischen 70 und 100 nicht mit dem zuvor übereinstimmen. Die Daten auf dem Client und server werden nicht synchronisiert.

Wenn die Datenbank nach Version 100 zu einem Punkt wiederhergestellt wurde, gibt es keine Probleme mit der Synchronisierung. Der Client und der Server würden während des nächsten Synchronisierungsintervalls Daten ordnungsgemäß synchronisieren.

Die Änderungsnachverfolgung bietet keine Unterstützung für die Wiederherstellung aus dem Verlust von Daten. Es gibt jedoch zwei Optionen zum Erkennen dieser Art von Synchronisierungsproblemen:

  • Speichern Sie eine Datenbankversions-ID auf dem Server, und aktualisieren Sie diesen Wert immer dann, wenn eine Datenbank wiederhergestellt wird oder auf sonstige Weise ein Datenverlust auftritt. Die ID würde in jeder Clientanwendung gespeichert, und jeder Client müsste diese ID beim Synchronisieren der Daten überprüfen. Wenn Datenverlust auftritt, stimmen die IDs nicht überein, und die Clients werden erneut initialisiert. Ein Nachteil ist, wenn der Datenverlust die letzte synchronisierte Grenze nicht überschritten hatte, kann der Client unnötige Erneuteitialisierung durchführen.

  • Zeichnen Sie die Versionsnummer der letzten Synchronisierung auf dem Server auf, wenn ein Client Änderungen abfragt. Wenn ein Problem mit den Daten vorliegt, stimmen die zuletzt synchronisierten Versionsnummern nicht überein. Dies weist darauf hin, dass eine Neuinitialisierung erforderlich ist.

Siehe auch