Detectando alterações em uma tabela do SQL Server


13

No meu aplicativo, com um banco de dados em execução no SQL Server 2012, tenho um trabalho (tarefa agendada) que executa periodicamente uma consulta dispendiosa e grava os resultados em uma tabela que pode ser consultada posteriormente pelo aplicativo.

Idealmente, eu gostaria de executar essa consulta cara apenas se algo mudasse desde a última execução da consulta. Como as tabelas de origem são muito grandes, não posso simplesmente selecionar uma soma de verificação em todas as colunas candidatas ou algo assim.

Eu tenho as seguintes idéias:

  • Escreva explicitamente um carimbo de data / hora alterado pela última vez, um sinalizador "deve-se consultar" ou algo assim em uma tabela de rastreamento sempre que alterar algo em uma tabela de origem.
  • Use um gatilho para fazer o mesmo.

No entanto, eu realmente gostaria de saber se existe uma maneira leve de detectar alterações em uma tabela sem que eu rastreie explicitamente as gravações. Posso, por exemplo, obter o "atual" ROWVERSIONde uma tabela ou algo assim?

Respostas:


14

Não, não existe. Qualquer tipo de rastreamento 'last updated at' apresentaria um grave problema de desempenho, pois todas as atualizações, de todas as transações, tentariam atualizar o registro único que rastreia a 'última atualização em'. Isso efetivamente significa que apenas uma transação pode atualizar a tabela a qualquer momento, e todas as outras transações precisam aguardar a confirmação da primeira. . Serialização completa. O número de administradores / desenvolvedores dispostos a tolerar essa penalidade de desempenho apenas para o benefício de saber quando a última atualização ocorreu é provavelmente pequeno.

Portanto, você está preparado para lidar com isso via código personalizado. Isso significa gatilhos, já que a alternativa (detecção de registros de log) é uma prerrogativa reservada apenas para replicação transacional (ou é o alter-ego do CDC ). Esteja ciente de que se você tentar controlá-lo por meio da coluna 'última atualização em', estará enfrentando exatamente o problema de serialização mencionado acima. Se a simultaneidade da atualização for importante, você precisará usar um mecanismo de fila (o gatilho usa um INSERT e, em seguida, um processo agrega os valores inseridos para formular a 'última atualização em'). Não tente trapacear com alguma solução 'inteligente', como esconder a identidade atual ou procurar sys.dm_db_index_usage_stats . E também uma coluna 'updated_at' por registro, como os registros de data e hora do Rails,

Existe alguma alternativa 'leve'? Na verdade, existe um, mas é difícil dizer se funcionará para você e é difícil acertar: Notificações de consulta . A notificação de consulta faz exatamente isso; ele configurará uma notificação se algum dado sofrer alterações e você precisar atualizar sua consulta. Embora a maioria dos desenvolvedores esteja familiarizada apenas com sua encarnação .Net como SqlDependency, o Query Notification pode ser usado como um mecanismo persistente e duradouro para detectar alterações de dados. Comparado com o verdadeiro rastreamento de alterações, ele será realmente leve e sua semântica estará mais próxima das suas necessidades (algo, qualquer coisa , mudou, portanto, você precisa executar novamente a consulta).

Mas, no final, em seu lugar, eu realmente reconsideraria minhas suposições e voltaria à prancheta. Talvez você possa usar o envio ou a replicação de logs para configurar um banco de dados de relatórios, em um servidor diferente. O que eu li nas entrelinhas é que você precisa de uma tubulação ETL adequada e de um data warehouse de análise ...


Então, por que a Microsoft se daria ao trabalho de criar sys.dm_db_index_usage_stats, se não é possível confiar nas informações fornecidas?
Craig Efrein

Não é um DMV projetado para rastreamento de alterações . É muito confiável para a finalidade pretendida, que é o ajuste de desempenho.
Remus Rusanu

8

Parece que estou dois anos atrasado para o jogo, aqui, mas há realmente uma maneira bastante leve de fazer o que você está pedindo.

Existem dois mecanismos do SQL Server que podem ajudá-lo. Sua solução final pode ser um híbrido dos dois.

Alterar rastreamento . O SQL Server tem a capacidade de colocar tabelas específicas sob observação, registrando apenas quais linhas foram alteradas (pelo valor da chave primária) e que tipo de alteração foi (Inserir, Atualizar ou Excluir). Depois de configurar a detecção de alterações em um conjunto de tabelas, uma consulta leve pode informar se foram feitas alterações na tabela desde a última vez que você verificou. A sobrecarga é aproximadamente a mesma que manter um índice simples adicional.

Rowversion / registro de data e hora . Este é um tipo de coluna varbinária de 8 bytes (convertível em BigInt) que é incrementado, em todo o banco de dados, sempre que uma linha que contém uma é inserida ou atualizada (não ajuda com exclusões). Se você indexou essas colunas, poderá saber facilmente se os dados da linha foram alterados comparando o MAX (carimbo de data e hora) com seu valor desde a última vez em que foram avaliados. Como o valor está aumentando monotonicamente, isso forneceria uma indicação confiável de que os dados foram alterados se o novo valor for maior que na última vez que você o verificou.


7

Se a fonte for somente de inserção, dê uma IDENTITY coluna. Ao fazer a transferência de dados, você registra o valor mais alto escrito. Durante a próxima transferência, você só precisa consultar valores maiores que os registrados na transferência anterior. Fazemos isso para transferir registros de log para um data warehouse.

Para linhas atualizáveis, adicione um sinalizador "sujo". Ele terá três valores - limpo, sujo e excluído. As consultas diárias terão de omitir linhas com o sinalizador definido como "excluído". Isso custará caro em manutenção, teste e tempo de execução. Após a grande consulta, você menciona que todas as linhas marcadas para exclusão devem ser removidas e o sinalizador redefinido para todas as outras. Isso não vai escalar bem.

Uma alternativa mais leve ao Change Data Capture é o Change Tracking . Não vai te dizer o que valores foram alterados, apenas que a linha foi alterada desde a última consulta. As funções integradas facilitam a recuperação de valores alterados e o gerenciamento de rastreamento. Tivemos sucesso usando o CT para processar cerca de 100.000 alterações por dia em uma tabela de 100.000.000 de linhas.

As Notificações de consulta atuam em uma alavanca mais alta ainda - no nível de um conjunto de resultados. Conceitualmente, é como definir uma visão. Se o SQL Server detectar que qualquer linha retornada por essa exibição foi alterada, ela dispara uma mensagem para o aplicativo. Não há indicação de quantas linhas foram alteradas ou de quais colunas. Há apenas uma mensagem simples dizendo "algo aconteceu". Cabe ao aplicativo perguntar e reagir. Praticamente é muito mais complexo que isso, como você pode imaginar. Existem restrições sobre como a consulta pode ser definida e a notificação pode disparar para condições diferentes de dados alterados. Quando a notificação é acionada, ela é removida. Se outra atividade de interesse ocorrer posteriormente, nenhuma outra mensagem será enviada.

No contexto da pergunta do OP, a QN terá a vantagem de ter uma baixa sobrecarga para configurar e pouco custo de tempo de execução. Pode ser um esforço significativo para estabelecer e manter um regime rigoroso de assinatura de mensagem e reação. Como a tabela de dados é grande, é provável que ocorram alterações frequentes, o que significa que a notificação provavelmente disparará na maioria dos ciclos de processamento. Como não há indicação do que o processamento incremental alterado dos deltas não será possível, como seria com o CT ou CDC. A sobrecarga devido ao acionamento falso é cansativa, mas mesmo no pior dos casos, a consulta cara não precisa ser executada com mais frequência do que atualmente.


3

SqlTableDependency

SqlTableDependency é um componente de implementação de alto nível para acessar notificações contendo valores de registro de tabela no banco de dados do SQL Server.

SqlTableDependency é um componente C # genérico usado para receber notificações quando o conteúdo de uma tabela de banco de dados especificada é alterado.

Qual é a diferença com o .NET SqlDepenency?

Basicamente, a principal diferença é que o SqlTableDependency envia eventos contendo valores para o registro inserido, alterado ou excluído, bem como a operação DML (inserir / excluir / atualizar) executada na tabela: SqlDepenency não informa quais dados foram alterados no tabela de banco de dados, eles apenas dizem que algo mudou.

Dê uma olhada no projeto GITHUB .


1

Se as atualizações esperadas afetarem um índice (e somente se), você poderá usar a tabela do sistema sys.dm_db_index_usage_statspara detectar a última atualização de um índice na tabela em questão. Você usaria o last_user_updatecampo.

Por exemplo, para obter as tabelas atualizadas mais recentemente:

select
    object_name(object_id) as OBJ_NAME, *
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
order by
    dm_db_index_usage_stats.last_user_update desc

Ou, para verificar se uma tabela específica foi alterada desde uma data específica:

select
    case when count(distinct object_id) > 0 then 1 else 0 end as IS_CHANGED
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
    and object_id = object_id('MY_TABLE_NAME')
    and last_user_update > '2016-02-18'

Qual sua opinião sobre o comentário de Remus acima? "Não tente trapacear com alguma solução 'inteligente', como esconder a identidade atual ou procurar sys.dm_db_index_usage_stats." (Veja também o seu comentário abaixo sua resposta.)
Fabian Schmied

1
@FabianSchmied Interessante - eu não tinha visto que, quando adicionei minha resposta, não encontrei nada autorativo além de outras respostas de Remus para indicar que não é confiável para este caso de uso; a página da Microsoft dm_db_index_operational_statsmostra problemas (limpos quando o cache de metadados limpa), mas não para dm_db_index_usage_stats. O único problema que encontrei foi com reconstruções de índice, reinicializações de servidores e desanexação de banco de dados, limpando as estatísticas de uso, e não parecia que isso se aplicava aqui. Estaria interessado em ver informações fundamentadas sobre isso.
Geoff
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.