Como a gravação de todas as alterações de uma linha em um banco de dados é geralmente armazenada?

10

Em um projeto em que estou trabalhando, todas as alterações nas linhas em algumas tabelas do banco de dados devem ser rastreadas para posterior auditoria ou reversão. Deve ser fácil encontrar quem modificou a linha, a partir de qual endereço IP e quando, além de poder restaurar a versão anterior.

A mesma coisa é usada, por exemplo, pelo Stack Exchange. Quando altero a pergunta de outra pessoa, é possível descobrir que a alterei e reverter as alterações.

Qual é a técnica geral usada para armazenar todas as alterações em um objeto em um banco de dados , considerando que meu esquema atual tem basicamente as mesmas propriedades (abaixo) que um aplicativo comercial médio?

  • Os objetos têm um tamanho relativamente pequeno: pode haver alguns, nvarchar(1000)por exemplo, mas não enormes blobs de dados binários, sendo armazenados diretamente no disco e acessados ​​diretamente, e não através do Microsoft SQL filestream,
  • A carga do banco de dados é bastante baixa e o banco de dados inteiro é tratado por uma máquina virtual em um servidor,
  • O acesso às versões anteriores não precisa ser tão rápido quanto o acesso à versão mais recente, mas ainda precisa estar atualizado¹ e não muito lento².

<tl-dr>

Pensei nos seguintes casos, mas não tenho experiência real com esse tipo de cenário, para ouvir as opiniões de outros:

  1. Armazene tudo na mesma tabela, distinguindo as linhas por ID e versão. OMI, é seriamente estúpido e prejudicará mais cedo ou mais tarde o nível de desempenho. Com essa abordagem, também é impossível definir um nível de segurança diferente para os itens mais recentes e para o rastreamento de versões. Finalmente, toda consulta seria mais complicada de escrever. Na verdade, para acessar os dados atualizados, eu seria forçado a agrupar tudo por ID e recuperar, em cada grupo, a última versão.

  2. Armazene a versão mais recente em uma tabela e, a cada alteração, copie a versão obsoleta para outra tabela em outro esquema. A falha é que sempre armazenamos todos os valores, mesmo que não tenham mudado. Definir valores inalterados como nullnão é uma solução, pois também devo rastrear quando o valor é alterado para nullou de null.

  3. Armazene a versão mais recente em uma tabela e a lista de propriedades alteradas com seus valores anteriores em outra tabela. Isso parece ter duas falhas: a mais importante é que a única maneira de classificar tipos heterogêneos de valores anteriores na mesma coluna é ter a binary(max). A segunda é que, acredito, seria mais difícil usar essa estrutura ao exibir as versões anteriores para o usuário.

  4. Faça o mesmo que nos dois pontos anteriores, mas armazene as versões em um banco de dados separado. Em termos de desempenho, pode ser interessante para evitar a lentidão do acesso às versões mais recentes, tendo as versões anteriores no mesmo banco de dados; ainda assim, acredito que seja uma otimização prematura e deve ser feita apenas se houver uma prova de que ter versões mais antigas e mais recentes no mesmo banco de dados é um gargalo.

</tl-dr>


¹ Por exemplo, seria inaceitável armazenar as alterações em um arquivo de log, como é feito nos logs HTTP, e liberar os dados do log para o banco de dados à noite, quando a carga do servidor for menor. As informações sobre diferentes versões devem estar disponíveis imediatamente ou quase imediatamente; um atraso de alguns segundos é aceitável.

² As informações não são acessadas com muita frequência e apenas por um grupo específico de usuários, mas, ainda assim, seria inaceitável forçá-los a aguardar 30 segundos pela exibição da lista de versões. Mais uma vez, é aceitável um atraso de alguns segundos.

Arseni Mourzenko
fonte
3
Relevante: SQL Server Change Data Capture .
quer

Respostas:

8

A maneira normal de fazer auditoria nesse tipo de log é ter uma tabela de sombra e registrar alterações com acionadores na tabela base que você está auditando. As outras tabelas podem ser colocadas em um disco físico diferente, se necessário, para desempenho, e você pode colocar índices nelas, se precisar oferecer suporte à recuperação rápida dos dados.

As tabelas terão aproximadamente a mesma estrutura que as tabelas originais, mas terão uma coluna de data e hora para quando a mudança ocorreu e um marcador para saber se a linha foi inserida, alterada ou excluída. O seqüenciamento das versões pode ser feito pelo carimbo de data / hora.

A data da alteração pode ser feita tornando a coluna datetime não nula com o padrão getdate (); uma coluna de usuário de auditoria capturará o usuário com uma coluna não nula padronizada para Suser_Sname (). Supondo que o usuário real esteja sendo representado na sessão, isso capturará a identidade do usuário que está fazendo a alteração.

O banco de dados não tem como estar ciente do endereço IP conectado a um servidor web. O aplicativo precisará capturar e registrar explicitamente o endereço IP com a transação.

Se você tiver um grande número de tabelas que deseja auditar, poderá usar os metadados do dicionário de dados do sistema para gerar os acionadores programaticamente.

Esta solução é de longe a melhor por vários motivos:

  • Ele captura quaisquer alterações na tabela, não apenas as feitas pelo aplicativo.

  • As tabelas de auditoria podem ser colocadas em um conjunto diferente de discos para reduzir a carga de E / S em suas tabelas principais.

  • Você pode usar uma exibição baseada em uma união da tabela e da tabela de log de auditoria para mostrar o histórico inteiro, incluindo a versão atual.

  • Você pode indexar as tabelas de log de auditoria conforme necessário, para que os usuários da auditoria possam consultá-las responsivamente. Como sempre, a seleção de índice é uma troca entre o desempenho da consulta e a sobrecarga da atualização.

ConcernedOfTunbridgeWells
fonte
você tenta dizer se eu tenho 1000 tabela que eu preciso manter o log para qualquer alteração, então eu tenho que criar 1000 tabela de sombra hein? e 1000 gatilho para capturar a mudança? se sim, é uma idéia falsa ... podemos criar uma única tabela de histórico e um único gatilho para capturar e registrar os dados alterados. podemos armazenar dados de linha antigos e novos nessa tabela como um xml .... isso é o que muitas pessoas fazem ... estou claro !!
Thomas
11
Para 1000 tabelas, você escreve uma utlity que lê as definições do dicionário de dados do sistema e gera os gatilhos e as definições de tabela. Eu fiz isso em um sistema com 560 tabelas e funciona muito bem.
ConcernedOfTunbridgeWells
0

Conheço muitos sistemas CMS (incluindo Wordpress) que usam uma única tabela para armazenar todas as versões dos dados. Mas, novamente, eles só precisam fazer isso para a tabela que contém as postagens do blog. Veja a estrutura do banco de dados do Wordpress .

Além disso, o número de registros e o número de revisões pelas quais cada linha passa desempenharão um papel significativo em sua decisão.

Dharmendar Kumar 'DK'
fonte
0

Sobre o versionamento do CMS; para drupal, ele cria uma tabela especial para todos os campos da entidade que armazenam o valor antigo; esse conceito permite uma ótima manipulação de seus dados, mas acho que é caro, minha própria solução é converter meu objeto em formato xml e armazená-lo como string nos outros campos (changetime, id ...)

Bourkadi
fonte