Partilhar via


Trabalhar com controle de alterações (SQL Server)

Aplica-se a:SQL ServerBanco de Dados SQL do AzureInstância Gerenciada SQL do Azure

Os aplicativos que usam o controle de alterações devem ser capazes de obter alterações controladas, aplicar essas alterações a outro armazenamento de dados e atualizar o banco de dados de origem. Este artigo descreve como executar essas tarefas e também a função que o controle de alterações desempenha quando ocorre um failover e um banco de dados deve ser restaurado a partir de um backup.

Obter alterações usando funções de controle de alterações

Descreve como usar as funções de controle de alterações para obter alterações e informações sobre as alterações feitas em um banco de dados.

Sobre as funções de controle de alterações

Os aplicativos podem usar as seguintes funções para obter as alterações feitas em um banco de dados e informações sobre as alterações:

Função CHANGETABLE(CHANGES ...)
A função rowset é usada para consultar informações de alteração. A função consulta os dados armazenados nas tabelas internas de controle de alterações. A função retorna um conjunto de resultados que contém as chaves primárias de linhas que foram alteradas juntamente com outras informações de alteração, como a operação, colunas atualizadas e versão da linha.

CHANGETABLE(CHANGES ...) usa uma última versão de sincronização como argumento. A última versão de sincronização é obtida usando a variável @last_synchronization_version. A semântica da última versão de sincronização é a seguinte:

  • O cliente de chamada obteve alterações e sabe sobre todas as alterações até e incluindo a última versão de sincronização.

  • CHANGETABLE(CHANGES ...) retornará, portanto, todas as alterações que ocorreram após a última versão de sincronização.

    A ilustração a seguir mostra como CHANGETABLE(CHANGES ...) é usado para obter alterações.

    Diagrama que mostra um exemplo de saída de consulta de rastreamento de alterações.

    Neste exemplo, o Cliente A sincronizou pela última vez às 9h30, enquanto o Cliente B sincronizou pela última vez às 10h30. Às 10h00 e novamente às 11h00, foram feitas várias alterações aos dados. Essas alterações controladas são resumidas abaixo.

    CHANGETABLE(ALTERAÇÕES...) Saída - 11h30

    Cliente A foi sincronizado pela última vez às 9h30.

    ID do produto Funcionamento Colunas
    139 Atualizar Nome, Preço
    140 Suprimir -
    141 Inserir -

    Cliente B sincronizado pela última vez às 10h30.

    ID do produto Funcionamento Colunas
    139 Atualizar Preço
    140 Suprimir -
    141 Atualizar Preço

Função CHANGE_TRACKING_CURRENT_VERSION()
Usado para obter a versão atual que será usada na próxima vez ao consultar alterações. Esta versão representa a versão da última transação confirmada.

Função CHANGE_TRACKING_MIN_VALID_VERSION()
Usado para obter a versão mínima válida que um cliente pode ter e ainda obter resultados válidos de CHANGETABLE(). O cliente deve verificar a última versão de sincronização em relação ao valor que é retornado por esta função. Se a última versão de sincronização for menor do que a versão retornada por essa função, o cliente não poderá obter resultados válidos de CHANGETABLE() e terá que reinicializar.

Obter dados iniciais

Antes que um aplicativo possa obter alterações pela primeira vez, o aplicativo deve enviar uma consulta para obter os dados iniciais e a versão de sincronização. O aplicativo deve obter os dados apropriados diretamente da tabela e, em seguida, usar CHANGE_TRACKING_CURRENT_VERSION() para obter a versão inicial. Esta versão será passada para CHANGETABLE(CHANGES ...) na primeira vez que as alterações forem obtidas.

O exemplo a seguir mostra como obter a versão de sincronização inicial e o conjunto de dados inicial.

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;

Use as funções de controle de alterações para obter alterações

Para obter as linhas alteradas para uma tabela e informações sobre as alterações, use CHANGETABLE(CHANGES...). Por exemplo, a consulta a seguir obtém alterações para a tabela SalesLT.Product.

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;

Normalmente, um cliente desejará obter os dados mais recentes para uma linha em vez de apenas as chaves primárias para a linha. Portanto, um aplicativo juntaria os resultados de CHANGETABLE(CHANGES ...) com os dados na tabela do usuário. Por exemplo, a consulta a seguir se une à tabela SalesLT.Product para obter os valores para as colunas Name e ListPrice. Observe o uso de OUTER JOIN. Isso é necessário para garantir que as informações de alteração sejam retornadas para as linhas que foram excluídas da tabela do usuário.

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;

Para obter a versão que será utilizada na próxima enumeração de alterações, use CHANGE_TRACKING_CURRENT_VERSION(), conforme mostrado no exemplo a seguir.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

Quando um aplicativo obtém alterações, ele deve usar CHANGETABLE(CHANGES...) e CHANGE_TRACKING_CURRENT_VERSION(), conforme mostrado no exemplo a seguir.

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

Números de versão

Um banco de dados que tem o controle de alterações habilitado tem um contador de versão que aumenta à medida que as alterações são feitas nas tabelas controladas. Cada linha alterada tem um número de versão associado a ela. Quando uma solicitação é enviada a um aplicativo para consultar alterações, uma função é chamada que fornece um número de versão. A função retorna informações sobre todas as alterações que foram feitas desde essa versão. De certa forma, a versão de controle de alterações é semelhante em conceito ao rowversion tipo de dados.

Validar a última versão sincronizada

As informações sobre alterações são mantidas por um tempo limitado. O período de tempo é controlado pelo parâmetro CHANGE_RETENTION que pode ser especificado como parte do ALTER DATABASE.

O tempo especificado para CHANGE_RETENTION determina a frequência com que todos os aplicativos devem solicitar alterações do banco de dados. Se um aplicativo tiver um valor para last_synchronization_version que seja mais antigo do que a versão de sincronização mínima válida para uma tabela, esse aplicativo não poderá executar enumeração de alteração válida. Isso ocorre porque algumas informações de alteração podem ter sido removidas. Antes de um aplicativo obter alterações usando CHANGETABLE(CHANGES ...), o aplicativo deve validar o valor para last_synchronization_version que planeja passar para CHANGETABLE(CHANGES ...). Se o valor de last_synchronization_version não for válido, esse aplicativo deverá reinicializar todos os dados.

O exemplo a seguir mostra como verificar a validade do valor de last_synchronization_version para cada tabela.

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

Como mostra o exemplo a seguir, a validade do valor de last_synchronization_version pode ser verificada em relação a todas as tabelas no banco de dados.

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

Usar o rastreamento de colunas

O controle de colunas permite que os aplicativos obtenham os dados apenas para as colunas que foram alteradas, em vez de toda a linha. Por exemplo, considere o cenário em que uma tabela tem uma ou mais colunas que são grandes, mas raramente mudam; e também tem outras colunas que mudam com frequência. Sem o rastreamento de coluna, um aplicativo só pode determinar que uma linha foi alterada e teria que sincronizar todos os dados que incluem os dados de coluna grande. No entanto, usando o controle de coluna, um aplicativo pode determinar se os dados de coluna grande foram alterados e sincronizar os dados somente se eles tiverem sido alterados.

As informações de rastreamento das colunas aparecem na coluna SYS_CHANGE_COLUMNS que é retornada pela função CHANGETABLE(CHANGES ...).

O rastreio de coluna pode ser usado para que NULL seja retornado para uma coluna que não sofreu alteração. Se a coluna puder ser alterada para NULL, uma coluna separada deverá ser retornada para indicar se a coluna foi alterada.

No exemplo a seguir, a coluna CT_ThumbnailPhoto será NULL se essa coluna não for alterada. Esta coluna também pode ser NULL porque foi alterada para NULL - o aplicativo pode usar a coluna CT_ThumbNailPhoto_Changed para determinar se a coluna foi alterada.

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

Obter resultados consistentes e corretos

A obtenção dos dados alterados para uma tabela requer várias etapas. Resultados inconsistentes ou incorretos podem ser retornados se certos problemas não forem considerados e tratados.

Por exemplo, para obter as alterações feitas em uma tabela Sales e SalesOrders tabela, um aplicativo deve executar as seguintes etapas:

  1. Valide a última versão sincronizada utilizando CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Obtenha a versão que pode ser usada para obter a alteração na próxima vez usando CHANGE_TRACKING_CURRENT_VERSION().

  3. Obtenha as alterações para a tabela Sales usando CHANGETABLE(CHANGES ...).

  4. Obtenha as alterações para a tabela SalesOrders usando CHANGETABLE(CHANGES ...).

Dois processos estão ocorrendo no banco de dados que podem afetar os resultados retornados pelas etapas anteriores:

  • O processo de limpeza é executado em segundo plano e remove informações de controle de alterações mais antigas do que o período de retenção especificado.

    O processo de limpeza é um processo em segundo plano separado que usa o período de retenção especificado quando você configura o controle de alterações para o banco de dados. O problema é que o processo de limpeza pode ocorrer no tempo entre quando a última versão de sincronização foi validada e quando a chamada para CHANGETABLE(CHANGES...) é feita. Uma última versão de sincronização que era válida pode não ser mais válida no momento em que as alterações são recuperadas. Portanto, resultados incorretos podem ser retornados.

  • As operações DML em curso estão ocorrendo nas tabelas Vendas e SalesOrders, como as seguintes operações:

    • Alterações podem ser feitas nas tabelas após a versão para a próxima vez ter sido obtida usando CHANGE_TRACKING_CURRENT_VERSION(). Portanto, mais alterações podem ser retornadas do que o esperado.

    • Uma transação pode ser completada no tempo entre a chamada para buscar alterações na tabela Sales e a chamada para buscar alterações na tabela SalesOrders. Portanto, os resultados da tabela SalesOrder podem ter um valor de chave estrangeira que não existe na tabela Sales.

Para superar os desafios listados anteriormente, recomendamos que utilize o isolamento por snapshot. Isso ajudará a garantir a consistência das informações de alteração e evitar condições de concorrência relacionadas à tarefa de limpeza em segundo plano. Se não usar transações de instantâneo, o desenvolvimento de uma aplicação que utilize o rastreio de alterações pode exigir muito mais esforço.

Usar isolamento de instantâneo

O controle de alterações foi projetado para funcionar bem com o isolamento de instantâneos. O isolamento de instantâneo deve ser ativado para o banco de dados. Todas as etapas necessárias para obter alterações devem ser incluídas em uma transação de instantâneo. Isso garantirá que todas as alterações feitas nos dados não fiquem visíveis para as consultas dentro da transação de snapshot enquanto se obtêm alterações.

Para obter dados numa transação de "snapshot", execute as seguintes etapas:

  1. Defina o nível de isolamento da transação como instantâneo e inicie uma transação.

  2. Valide a última versão de sincronização usando a função CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Obtenha a versão para uso na próxima vez ao usar CHANGE_TRACKING_CURRENT_VERSION().

  4. Obtenha as alterações para a tabela Sales usando CHANGETABLE(CHANGES ...)

  5. Obtenha as alterações para a tabela SalesOrders usando CHANGETABLE(CHANGES ...)

  6. Confirme a transação.

Alguns pontos a serem lembrados, pois todas as etapas para obter alterações estão dentro de uma transação de instantâneo:

  • Se a limpeza ocorrer após a última versão de sincronização ser validada, os resultados de CHANGETABLE(CHANGES ...) ainda serão válidos, pois as operações de exclusão executadas pela limpeza não serão visíveis dentro da transação.

  • Quaisquer alterações feitas na tabela Sales ou na tabela SalesOrders após a próxima versão de sincronização ser obtida não serão visíveis e as chamadas para CHANGETABLE(CHANGES ...) nunca retornarão alterações com uma versão posterior à retornada por CHANGE_TRACKING_CURRENT_VERSION(). A consistência entre a tabela Sales e a tabela SalesOrders também será mantida, porque as transações que foram confirmadas no tempo entre as chamadas para CHANGETABLE(CHANGES ...) não serão visíveis.

O exemplo a seguir mostra como o isolamento de instantâneo é ativado para uma base de dados.

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

Uma transação de instantâneo é usada da seguinte maneira:

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

Para obter mais informações sobre transações de instantâneo, consulte SET TRANSACTION ISOLATION LEVEL (Transact-SQL).

Limpeza e isolamento de instantâneos

Habilitar o isolamento de instantâneo e a monitorização de alterações no mesmo banco de dados, ou em dois bancos de dados diferentes dentro da mesma instância, pode fazer com que o processo de limpeza deixe linhas expiradas em sys.syscommittab quando há uma transação aberta no banco de dados com isolamento de instantâneo. Isso pode acontecer, pois o processo de limpeza de controle de alterações leva em conta uma marca d'água baixa em toda a instância (que é a versão de limpeza segura) ao executar a limpeza. Isso é feito para garantir que o processo de limpeza automática de controle de alterações não remova nenhuma linha que possa ser exigida pela transação aberta no banco de dados que tem o isolamento de instantâneo habilitado. Mantenha o isolamento de snapshot confirmado de leitura e as transações de isolamento de snapshot breves para garantir que as linhas expiradas do sys.syscommittab sejam limpas em tempo hábil.

Alternativas ao isolamento de snapshot

Existem alternativas ao isolamento instantâneo, mas elas exigem mais trabalho para garantir que todos os requisitos da aplicação sejam atendidos. Para se certificar de que o last_synchronization_version é válido e que os dados não são removidos pelo processo de limpeza antes de as alterações serem obtidas, siga estes passos:

  1. Verifique last_synchronization_version após as chamadas para CHANGETABLE().

  2. Verifique last_synchronization_version como parte de cada consulta para obter alterações usando CHANGETABLE().

As alterações podem ocorrer após a versão de sincronização para a próxima enumeração ter sido obtida. Há duas maneiras de lidar com essa situação. A opção usada depende do aplicativo e de como ele pode lidar com os efeitos colaterais de cada abordagem:

  • Ignore as alterações que têm uma versão maior do que a nova versão de sincronização.

    Essa abordagem tem o efeito colateral de que uma linha nova ou atualizada seria ignorada se fosse criada ou atualizada antes da nova versão de sincronização, mas atualizada depois. Se houver uma nova linha, pode ocorrer um problema de integridade referencial se já existisse uma linha noutra tabela que referenciava a linha que foi ignorada. Se houver uma linha existente atualizada, a linha será ignorada e não sincronizada até a próxima vez.

  • Inclua todas as alterações, mesmo aquelas que têm uma versão maior do que a nova versão de sincronização.

    As linhas que têm uma versão maior do que a nova versão de sincronização serão obtidas novamente na próxima sincronização. Isso deve ser esperado e tratado pelo aplicativo.

Além das duas opções anteriores, você pode criar uma abordagem que combine ambas as opções, dependendo da operação. Por exemplo, você pode querer um aplicativo para o qual é melhor ignorar as alterações mais recentes do que a próxima versão de sincronização na qual a linha foi criada ou excluída, mas as atualizações não são ignoradas.

Observação

Escolher a abordagem que funcionará para o aplicativo quando você estiver usando o controle de alterações (ou qualquer mecanismo de rastreamento personalizado) requer uma análise significativa. Portanto, é muito mais simples usar o isolamento por instantâneos.

Como o controle de alterações lida com alterações em um banco de dados

Alguns aplicativos que usam o controle de alterações executam sincronização bidirecional com outro armazenamento de dados. Ou seja, as alterações feitas no banco de dados do SQL Server são atualizadas no outro armazenamento de dados e as alterações feitas no outro repositório são atualizadas no banco de dados do SQL Server.

Quando um aplicativo atualiza o banco de dados local com alterações de outro armazenamento de dados, o aplicativo deve executar as seguintes operações:

  • Verifique se há conflitos.

    Um conflito ocorre quando os mesmos dados são alterados ao mesmo tempo em ambos os armazenamentos de dados. O pedido deve ser capaz de verificar a existência de um conflito e obter informações suficientes para permitir a sua resolução.

  • Armazene informações de contexto do aplicativo.

    A aplicação armazena dados que têm as informações de controle de alterações. Essas informações estariam disponíveis juntamente com outras informações de controle de alterações quando as alterações fossem obtidas do banco de dados local. Um exemplo comum dessas informações contextuais é um identificador para o armazenamento de dados que foi a fonte da alteração.

Para executar as operações anteriores, um aplicativo de sincronização pode usar as seguintes funções:

  • CHANGETABLE(VERSÃO...)

    Quando um aplicativo está fazendo alterações, ele pode usar essa função para verificar conflitos. A função obtém as informações de controle de alterações mais recentes para uma linha especificada em uma tabela de controle de alterações. As informações de controle de alterações incluem a versão da linha que foi alterada pela última vez. Essas informações permitem que um aplicativo determine se a linha foi alterada após a última vez que o aplicativo foi sincronizado.

  • COM CONTEXTO_DE_ACOMPANHAMENTO_DE_MUDANÇAS

    Um aplicativo pode usar essa cláusula para armazenar dados de contexto.

Verificar se há conflitos

Em um cenário de sincronização bidirecional, o aplicativo cliente deve determinar se uma linha não foi atualizada desde a última vez que o aplicativo obteve as alterações.

O exemplo a seguir mostra como usar a função CHANGETABLE(VERSION ...) para verificar conflitos da maneira mais eficiente, sem uma consulta separada. No exemplo, CHANGETABLE(VERSION ...) determina o SYS_CHANGE_VERSION para a linha especificada por @product id. CHANGETABLE(CHANGES ...) podem obter as mesmas informações, mas isso seria menos eficiente. Se o valor de SYS_CHANGE_VERSION para a linha for maior do que o valor de @last_sync_version, há um conflito. Se houver um conflito, a linha não será atualizada. A verificação ISNULL() é necessária porque pode não haver informação disponível sobre alterações na linha. Nenhuma informação de alteração existiria se a linha não tivesse sido atualizada desde que o controle de alterações foi habilitado ou desde que as informações de alteração foram limpas.

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

O código a seguir pode verificar a contagem de linhas atualizada e identificar mais informações sobre o conflito.

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

Definir informações de contexto

Usando a cláusula WITH CHANGE_TRACKING_CONTEXT, um aplicativo pode armazenar informações de contexto junto com as informações de alteração. Esta informação pode então ser obtida a partir da coluna SYS_CHANGE_CONTEXT que é retornada por CHANGETABLE(CHANGES ...).

As informações de contexto são normalmente usadas para identificar a origem das alterações. Se a origem da alteração puder ser identificada, essas informações poderão ser usadas por um armazenamento de dados para evitar a obtenção de alterações quando ele for sincronizado novamente.

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

Garantir resultados consistentes e corretos

Um aplicativo deve considerar o processo de limpeza quando valida o valor de @last_sync_version. Isso ocorre porque os dados poderiam ter sido removidos depois que CHANGE_TRACKING_MIN_VALID_VERSION() foi chamado, mas antes que a atualização fosse feita.

Você deve usar o isolamento por instantâneo e fazer as alterações dentro de uma transação por instantâneo.

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

Observação

Existe a possibilidade de que a linha a ser atualizada na transação de instantâneo tenha sido atualizada em outra transação depois que a transação de instantâneo foi iniciada. Nesse caso, ocorrerá um conflito de atualização com isolamento por instantâneo, resultando no encerramento da transação. Se isso acontecer, tente atualizar novamente. Isso levará à deteção de um conflito de rastreio de alterações e sem que nenhuma linha seja alterada.

Controlo de alterações e restauro de dados

Os aplicativos que exigem sincronização devem considerar o caso em que um banco de dados com controle de alterações habilitado reverte para uma versão anterior dos dados. Isso pode ocorrer depois que um banco de dados é restaurado a partir de um backup, quando ocorre um failover para um espelho de banco de dados assíncrono ou quando ocorre uma falha ao usar o envio de logs. O cenário a seguir ilustra o problema:

  1. A tabela T1 está com controlo de alterações, e a versão 50 é a mínima válida para a tabela.

  2. Um aplicativo cliente sincroniza dados na versão 100 e obtém informações sobre todas as alterações entre as versões 50 e 100.

  3. Alterações adicionais são feitas na tabela T1 após a versão 100.

  4. Na versão 120, há uma falha e o administrador do banco de dados restaura o banco de dados com perda de dados. Após a operação de restauração, a tabela contém dados até a versão 70 e a versão sincronizada mínima ainda é 50.

    Isso significa que o armazenamento de dados sincronizado tem dados que não existem mais no armazenamento de dados primário.

  5. T1 é atualizado muitas vezes. Isso eleva a versão atual para 130.

  6. O aplicativo cliente sincroniza novamente e fornece uma última versão sincronizada de 100. O cliente valida esse número com êxito porque 100 é maior que 50.

    O cliente obtém alterações entre as versões 100 e 130. Neste ponto, o cliente não está ciente de que as mudanças entre 70 e 100 não são as mesmas de antes. Os dados no cliente e no servidor não estão sincronizados.

Se o banco de dados foi recuperado para um ponto após a versão 100, não haveria problemas com a sincronização. O cliente e o servidor sincronizariam os dados corretamente durante o próximo intervalo de sincronização.

O controle de alterações não fornece suporte para a recuperação da perda de dados. No entanto, há duas opções para detetar esses tipos de problemas de sincronização:

  • Armazene um ID de versão do banco de dados no servidor e atualize esse valor sempre que um banco de dados for recuperado ou perder dados. Cada aplicativo cliente armazenaria a ID e cada cliente teria que validar essa ID quando sincronizasse dados. Se ocorrer perda de dados, os IDs não corresponderão e os clientes serão reinicializados. Uma desvantagem é se a perda de dados não tiver cruzado o último limite sincronizado, o cliente pode fazer reinicialização desnecessária.

  • Quando um cliente consulta alterações, registre o número da última versão de sincronização para cada cliente no servidor. Se houver um problema com os dados, os últimos números de versão sincronizados não corresponderão. Isso indica que uma reinicialização é necessária.

Ver também