Verwenden der Änderungsnachverfolgung (SQL Server)

Gilt für:SQL ServerAzure SQL-DatenbankVerwaltete Azure SQL-InstanzSQL-Datenbank in Microsoft Fabric

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 Tasks ausgeführt werden. Zudem wird beschrieben, welche Rolle die Änderungsnachverfolgung spielt, wenn ein Failover auftritt und eine Datenbank von einer Sicherung wiederhergestellt werden muss.

Änderungen mithilfe von Funktionen zur Änderungsverfolgung abrufen

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

Informationen zu Änderungsnachverfolgungs-Funktionen

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 eine Ergebnismenge zurück, die die Primärschlüssel der geänderten Zeilen zusammen mit weiteren Änderungsinformationen enthält, etwa der Operation, den aktualisierten Spalten und der Version der Zeile.

    CHANGETABLE(CHANGES ...) verwendet eine 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 Änderungen abgerufen und kennt alle Änderungen bis einschließlich der letzten Synchronisierungsversion.

  • CHANGETABLE(CHANGES ...) gibt daher alle Änderungen zurück, die nach der letzten Synchronisierungsversion aufgetreten sind.

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

Diagramm, das ein Beispiel für die Ausgabe der Änderungsnachverfolgungsabfrage zeigt.

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 nachverfolgten Änderungen werden im folgenden Beispiel zusammengefasst.

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

Client A wurde zuletzt um 9:30 Uhr synchronisiert.

Product ID Vorgang Spalten
139 Aktualisieren Name, Preis
140 Löschen -
141 Einfügen -

Client B wurde zuletzt um 10:30 Uhr synchronisiert.

Product ID Vorgang Spalten
139 Aktualisieren Preis
140 Löschen -
141 Aktualisieren Preis
  • CHANGE_TRACKING_CURRENT_VERSION() Funktion

    Wird verwendet, um die aktuelle Version zu erhalten, die bei der nächsten Abfrage von Änderungen verwendet werden soll. 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 Mindestversion zu ermitteln, mit der ein Client noch gültige Ergebnisse von CHANGETABLE() erhalten kann. Der Client muss die Version der letzten Synchronisierung mit dem Wert abgleichen, der von dieser Funktion zurückgegeben wird. Wenn die letzte Synchronisierungsversion kleiner 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 der 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 zum Abrufen der ursprünglichen Version verwenden CHANGE_TRACKING_CURRENT_VERSION() . Diese Version wird an CHANGETABLE(CHANGES ...) das erste Mal übergeben, wenn Änderungen abgerufen werden.

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 Änderungsnachverfolgungs-Funktionen zum Abrufen von Änderungen

Um die geänderten Zeilen für eine Tabelle und Informationen zu den Änderungen abzurufen, verwenden Sie CHANGETABLE(CHANGES...). 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. Daher würde eine Anwendung die Ergebnisse aus CHANGETABLE(CHANGES ...) mit den Daten in der Benutzertabelle zusammenführen. 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 Synchronisierungsversion

Informationen über Änderungen werden für einen beschränkten Zeitraum beibehalten. Die Dauer wird durch den Parameter CHANGE_RETENTION gesteuert, der als Teil von ALTER DATABASE angegeben werden kann.

Die angegebene CHANGE_RETENTION 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 minimale gültige Synchronisierungsversion für eine Tabelle ist, kann diese Anwendung keine gültige Änderungsenumeration durchführen. Das liegt daran, dass einige Änderungsinformationen möglicherweise bereinigt wurden. Bevor eine Anwendung mithilfe von CHANGETABLE(CHANGES ...) Änderungen abruft, muss sie den Wert für last_synchronization_version, den sie an CHANGETABLE(CHANGES ...) zu übergeben beabsichtigt, validieren. Wenn der Wert last_synchronization_version ungültig ist, muss diese Anwendung alle Daten erneut 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;

Spaltennachverfolgung verwenden

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. Allerdings kann eine Anwendung mithilfe der Spaltenverfolgung feststellen, ob sich die Daten der großen Spalte geändert haben, und die Daten nur dann synchronisieren, wenn dies der Fall ist.

Spaltenverfolgungsinformationen werden in der SYS_CHANGE_COLUMNS Spalte angezeigt, die von der CHANGETABLE(CHANGES ...) Funktion zurückgegeben wird.

Die Spaltenverfolgung kann verwendet werden, damit für eine Spalte, die nicht geändert wurde, NULL zurückgegeben wird. Wenn die Spalte zu NULL geändert werden kann, ist eine separate Spalte zurückzugeben, die angibt, ob die Spalte geändert wurde.

Im folgenden Beispiel wird für die Spalte CT_ThumbnailPhoto der Wert NULL zurückgegeben, wenn sie nicht geändert wurde. Diese Spalte könnte auch NULL sein, weil sie in NULL geändert wurde. Die Anwendung kann die CT_ThumbNailPhoto_Changed Spalte verwenden, um zu bestimmen, ob sich die Spalte geändert hat.

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';

Konsistente und korrekte Ergebnisse erzielen

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

Um zum Beispiel die Änderungen zu erhalten, die an einer Tabelle Sales und einer Tabelle SalesOrders vorgenommen wurden, würde eine Anwendung die folgenden Schritte ausführen:

  1. Überprüfen Sie die letzte synchronisierte Version mithilfe von CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Rufen Sie die Version ab, die beim nächsten Mal verwendet werden kann, um Änderungen mithilfe von CHANGE_TRACKING_CURRENT_VERSION() abzurufen.

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

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

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. In diesem Fall kann es vorkommen, dass die gerade für gültig befundene letzte Synchronisierungs-Version beim Abruf der Änderungen nicht mehr gültig ist. Aus diesem Grund kann es hier zu falschen Ergebnissen kommen.

  • In den Tabellen Sales und SalesOrders finden laufend DML-Operationen statt, wie z. B. die folgenden Operationen:

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

    • Eine Transaktion könnte in der Zeit zwischen dem Aufruf, Änderungen aus der Tabelle Sales zu holen, und dem Aufruf, Änderungen aus der Tabelle SalesOrders zu holen, übertragen werden. Daher könnten die Ergebnisse für die Tabelle SalesOrder einen Fremdschlüsselwert haben, der in der Tabelle Sales nicht existiert.

Um die zuvor aufgeführten Herausforderungen zu bewältigen, empfehlen wir Ihnen, die Snapshotisolation zu verwenden. Hierdurch können Sie die Konsistenz der Änderungsinformationen sicherstellen und Racebedingungen im Zusammenhang mit dem im Hintergrund ausgeführten Cleanupprozess vermeiden. Ohne die Verwendung von Momentaufnahme-Transaktionen ist die Entwicklung einer Anwendung, die die Änderungsnachverfolgung verwendet, mit erheblich mehr Aufwand verbunden.

Verwendung der Momentaufnahmeisolation

Die Änderungsnachverfolgung wurde so konzipiert, dass sie gut mit der Snapshotisolation funktioniert. Die Momentaufnahmeisolation muss für die Datenbank aktiviert werden. Alle Schritte, die erforderlich sind, um Änderungen zu erhalten, müssen innerhalb einer Snapshottransaktion enthalten sein. Dadurch wird sichergestellt, dass alle Änderungen, die während des Abrufens von Änderungen an den Daten vorgenommen werden, für die Abfragen innerhalb der Snapshot-Transaktion nicht sichtbar sind.

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

  1. Legen Sie die Transaktionsisolationsstufe auf Snapshot fest und starten Sie eine Transaktion.

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

  3. Rufen Sie mit CHANGE_TRACKING_CURRENT_VERSION() die Version ab, die beim nächsten Mal verwendet werden soll.

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

  5. Änderungen für die SalesOrdersTabelle mithilfe von CHANGETABLE(CHANGES ...) abrufen

  6. Bestätigen Sie die Transaktion.

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

  • Wenn die Bereinigung erfolgt, nachdem die letzte Synchronisierungsversion überprüft wurde, sind die Ergebnisse CHANGETABLE(CHANGES ...) weiterhin gültig, da die von der Bereinigung ausgeführten Löschvorgänge nicht innerhalb der Transaktion sichtbar sind.

  • Änderungen, die an der Tabelle Sales oder der Tabelle SalesOrders vorgenommen werden, nachdem die nächste Synchronisierungsversion abgerufen wurde, werden nicht sichtbar sein, und Aufrufe von CHANGETABLE(CHANGES ...) geben niemals Änderungen mit einer späteren Version zurück als der von CHANGE_TRACKING_CURRENT_VERSION() zurückgegebenen. Die Konsistenz zwischen der Tabelle Sales und der Tabelle SalesOrders bleibt ebenfalls gewahrt, da die Transaktionen, die in der Zeit zwischen Aufrufen von CHANGETABLE(CHANGES ...) festgeschrieben wurden, nicht sichtbar werden.

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 Snapshot-Transaktion 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

Die Aktivierung von Momentaufnahme-Isolierung und Änderungsnachverfolgung in derselben Datenbank oder in zwei verschiedenen Datenbanken innerhalb derselben Instanz kann dazu führen, dass der Bereinigungsprozess abgelaufene Zeilen in sys.syscommittab zurücklässt, wenn in der Datenbank mit Momentaufnahme-Isolierung eine Transaktion geöffnet 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 bei der automatischen Bereinigung der Änderungsnachverfolgung keine Zeilen entfernt werden, die möglicherweise von der geöffneten Transaktion in der Datenbank benötigt werden, für die die Momentaufnahme-Isolation aktiviert ist. Halten Sie Read Committed Snapshot Isolation und Transaktionen mit Snapshot Isolation so kurz wie möglich, um sicherzustellen, dass abgelaufene Zeilen aus sys.syscommittab rechtzeitig gelöscht werden.

Alternativen zur Snapshot-Isolation

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 Daten gültig sind und die Daten nicht vom Bereinigungsprozess entfernt werden, bevor Änderungen abgerufen werden:

  1. Überprüfen Sie last_synchronization_version nach den Aufrufen von CHANGETABLE().

  2. Prüfen Sie last_synchronization_version als Teil jeder Abfrage, um mithilfe von CHANGETABLE() Änderungen abzurufen.

Ä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. Bei einer neuen Zeile kann ein Problem mit der referenziellen Integrität auftreten, wenn eine erstellte Zeile in einer anderen Tabelle auf die übersprungene Zeile verweist. Wenn eine vorhandene Zeile aktualisiert wurde, wird diese Zeile übersprungen und erst beim 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 erwartet und behandelt werden.

Zusätzlich zu den beiden oben genannten Optionen können Sie abhängig vom Vorgang einen Ansatz entwerfen, der beide Optionen kombiniert. Beispielsweise benötigen Sie möglicherweise eine Anwendung, bei der Änderungen, die neuer sind als die nächste Synchronisierungsversion, in der die Zeile erstellt oder gelöscht wurde, am besten ignoriert werden, Aktualisierungen jedoch nicht.

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.

Wie die Änderungsnachverfolgung mit Änderungen an einer Datenbank umgeht

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:

  • Auf Konflikte überprüfen.

    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, die Informationen zur Änderungsverfolgung enthalten. 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 Szenario mit bidirektionaler Synchronisierung muss die Clientanwendung ermitteln, ob eine Zeile seitdem die Anwendung die Änderungen zuletzt abgerufen hat, nicht aktualisiert wurde.

Das folgende Beispiel zeigt, wie Sie mit der Funktion CHANGETABLE(VERSION ...) Konflikte ohne separate Abfrage am effizientesten prüfen können. 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 von SYS_CHANGE_VERSION für die Zeile größer ist als der Wert von @last_sync_version, 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 sind keine Änderungsinformationen vorhanden, wenn die Zeile seit der Aktivierung der Änderungsnachverfolgung oder seit der Bereinigung der Änderungsinformationen nicht aktualisiert wurde.

-- 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

Kontextinformationen einstellen

Mithilfe der WITH CHANGE_TRACKING_CONTEXT Klausel kann eine Anwendung Kontextinformationen zusammen mit den Änderungsinformationen speichern. Diese Information kann dann aus der Spalte SYS_CHANGE_CONTEXT entnommen 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 von konsistenten und richtigen Ergebnissen

Eine Anwendung muss den Bereinigungsprozess berücksichtigen, wenn sie den Wert von @last_sync_version überprüft. Dies liegt daran, dass Daten nach CHANGE_TRACKING_MIN_VALID_VERSION() dem Aufruf entfernt worden sein könnten, aber bevor die Aktualisierung vorgenommen wurde.

Sie sollten die Snapshotisolierung verwenden und die Änderungen innerhalb einer Snapshottransaktion 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 Updatekonflikt bei der Momentaufnahmeisolation auf, der dazu führt, 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, wenn eine Datenbank aus einer Sicherung wiederhergestellt wird, bei einem Failover auf eine asynchrone Datenbankspiegelung oder wenn beim Protokollversand ein Fehler auftritt. Das folgende Beispiel veranschaulicht dieses Szenario:

  1. Für Tabelle T1 werden Änderungen nachverfolgt, und die minimal 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. Bei Version 120 tritt ein Fehler auf, und der Datenbank-Administrator 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. Dadurch erhöht sich die aktuelle Version auf 130.

  6. Die Clientanwendung synchronisiert erneut und gibt als zuletzt synchronisierte Version 100 aus. Der Client validiert diese Zahl erfolgreich, weil 100 größer als 50 ist.

    Der Client ruft die Änderungen zwischen Version 100 und 130 ab. Zu diesem Zeitpunkt weiß der Client nicht, dass die Änderungen zwischen Version 70 und 100 nicht die gleichen wie zuvor sind. Die Daten auf dem Client und dem Server sind nicht synchronisiert.

Es gäbe keine Probleme bei der Synchronisierung, wenn die Datenbank auf einen Punkt nach Version 100 wiederhergestellt würde. Der Client und der Server würden während des nächsten Synchronisierungsintervalls Daten ordnungsgemäß synchronisieren.

Die Änderungsnachverfolgung bietet keine Unterstützung bei der Wiederherstellung nach einem Datenverlust. 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 würden eine Neuinitialisierung durchführen. Ein Nachteil besteht darin, dass der Client eine unnötige Neuinitialisierung durchführt, wenn der Datenverlust nicht über die letzte Synchronisierungsgrenze hinaus erfolgt ist.

  • Wenn ein Client Änderungen anfordert, speichern Sie für jeden Client die Versionsnummer der letzten Synchronisierung auf dem Server. Wenn ein Problem mit den Daten vorliegt, stimmen die Versionsnummern der letzten Synchronisierung nicht überein. Dies weist darauf hin, dass eine Neuinitialisierung erforderlich ist.