Come viene generalmente memorizzata la registrazione di ogni modifica di una riga in un database?


10

In un progetto a cui sto lavorando, ogni modifica alle righe in alcune tabelle del database deve essere tracciata per ulteriore controllo o rollback. Deve essere facile trovare chi ha modificato la riga, da quale indirizzo IP e quando ed essere in grado di ripristinare la versione precedente.

La stessa cosa viene usata ad esempio da Stack Exchange. Quando cambio la domanda di qualcun altro, è possibile scoprire che l'ho modificata e ripristinare le modifiche.

Qual è la tecnica generale utilizzata per archiviare ogni modifica a un oggetto in un database , dato che il mio schema attuale ha principalmente le stesse proprietà (sotto) di un'app aziendale media?

  • Gli oggetti hanno dimensioni relativamente ridotte: potrebbero essercene alcuni, nvarchar(1000)ad esempio, ma non enormi BLOB di dati binari, che vengono archiviati direttamente sul disco e accessibili direttamente e non tramite Microsoft SQL filestream,
  • Il carico del database è piuttosto basso e l'intero database è gestito da una macchina virtuale su un server,
  • L'accesso alle versioni precedenti non deve essere veloce come l'accesso all'ultima versione, ma deve essere aggiornato¹ e non troppo lento².

<Tl-dr>

Ho pensato ai seguenti casi, ma non ho esperienza reale con questo tipo di scenari, quindi ascolterei le opinioni degli altri:

  1. Memorizza tutto nella stessa tabella, distinguendo le righe per ID e versione. IMO, è seriamente stupido e farà male presto o tardi a livello prestazionale. Con questo approccio, è anche impossibile impostare un livello di sicurezza diverso per gli ultimi elementi e per la traccia delle versioni. Infine, ogni query sarebbe più complicata da scrivere. In realtà, per accedere ai dati aggiornati, sarei costretto a raggruppare tutto per ID e recuperare, in ciascun gruppo, l'ultima versione.

  2. Archivia la versione più recente in una tabella e, ad ogni modifica, copia la versione obsoleta in un'altra tabella in un altro schema. Il difetto è che ogni volta archiviamo ogni valore, anche se non è cambiato. L'impostazione di valori invariati su nullnon è una soluzione, poiché devo anche tenere traccia di quando il valore viene modificato da nullo verso null.

  3. Archivia l'ultima versione in una tabella e l'elenco delle proprietà modificate con i valori precedenti in un'altra tabella. Questo sembra avere due difetti: il più importante è che l'unico modo per ordinare tipi eterogenei di valori precedenti nella stessa colonna è avere un binary(max). Il secondo è che, credo, sarebbe più difficile usare tale struttura quando si visualizzano le versioni precedenti all'utente.

  4. Fai la stessa cosa di due punti precedenti, ma archivia le versioni in un database separato. Per quanto riguarda le prestazioni, potrebbe essere interessante al fine di evitare di rallentare l'accesso alle ultime versioni avendo le versioni precedenti nello stesso database; tuttavia, credo che sia un'ottimizzazione prematura e deve essere fatto solo se c'è una prova che avere versioni più vecchie e più recenti nello stesso database è un collo di bottiglia.

</ TL-dr>


¹ Ad esempio, sarebbe inaccettabile archiviare le modifiche in un file di registro, come avviene per i registri HTTP, e scaricare i dati dal registro al database di notte quando il carico del server è più basso. Le informazioni sulle diverse versioni devono essere disponibili immediatamente o quasi immediatamente; è accettabile qualche secondo di ritardo.

² Le informazioni non sono accessibili molto frequentemente e solo da specifici gruppi di utenti, ma sarebbe inaccettabile costringerli ad attendere 30 secondi affinché venga visualizzato l'elenco delle versioni. Ancora una volta, è accettabile qualche secondo di ritardo.


Risposte:


8

Il modo normale di eseguire la registrazione di controllo di questo tipo è disporre di una tabella shadow e registrare le modifiche con i trigger nella tabella di base che si sta controllando. Le altre tabelle possono essere posizionate su un diverso disco fisico se necessario a quello per le prestazioni e è possibile inserire degli indici su di esse se è necessario supportare il recupero rapido dei dati.

Le tabelle avranno all'incirca la stessa struttura delle tabelle originali, ma avranno una colonna datetime per quando è avvenuta la modifica e un indicatore per l'inserimento, la modifica o l'eliminazione della riga. Il sequenziamento delle versioni può essere eseguito mediante il timestamp.

La data di modifica può essere effettuata rendendo la colonna datetime non nulla con un valore predefinito di getdate (); una colonna dell'utente di controllo acquisirà l'utente con una colonna non nulla predefinita su Suser_Sname (). Supponendo che l'utente reale venga impersonato nella sessione, questo acquisirà l'identità dell'utente che sta apportando la modifica.

Il database non ha modo di conoscere l'indirizzo IP che si collega a un server Web. L'applicazione dovrà acquisire e registrare esplicitamente l'indirizzo IP con la transazione.

Se si dispone di un numero elevato di tabelle che si desidera controllare, è possibile utilizzare i metadati dal dizionario dei dati di sistema per generare i trigger a livello di programmazione.

Questa soluzione è di gran lunga la migliore per diversi motivi:

  • Cattura eventuali modifiche alla tabella, non solo quelle apportate dall'applicazione.

  • Le tabelle di controllo possono essere inserite in un diverso set di dischi per ridurre il carico di I / O sulle tabelle primarie.

  • È possibile utilizzare una vista basata su un'unione della tabella e della tabella del registro di controllo per mostrare l'intera cronologia inclusa la versione corrente.

  • È possibile indicizzare le tabelle del registro di controllo come necessario in modo che gli utenti del controllo possano interrogarle in modo reattivo. Come al solito, la selezione dell'indice è un compromesso tra prestazioni della query e sovraccarico di aggiornamento.


provi a dire se ho una tabella 1000 che devo mantenere il registro per qualsiasi modifica, allora devo creare una tabella shadow 1000 eh? e 1000 trigger per catturare il cambiamento? se sì, allora è un'idea falsa ... possiamo creare una singola tabella cronologica e un singolo trigger per acquisire e registrare i dati modificati. possiamo archiviare i dati di riga vecchi e nuovi in ​​quella tabella come xml .... questo è un sacco di persone .... sono chiaro !!
Thomas,

1
Per 1000 tabelle si scrive un'utilità che legge le definizioni dal dizionario dei dati di sistema e genera i trigger e le definizioni delle tabelle. L'ho fatto su un sistema con 560 tavoli e funziona perfettamente.
Preoccupato di

0

Conosco molti sistemi CMS (incluso Wordpress) che utilizzano una singola tabella per memorizzare tutte le versioni dei dati. Ma ancora una volta, devono farlo solo per la tabella che contiene i post del blog. Vedi la struttura del database Wordpress .

Inoltre, il numero di record e il numero di revisioni che ogni riga attraversa svolgerà un ruolo significativo nella tua decisione.


0

Informazioni sul controllo delle versioni CMS; per drupal crea una tabella speciale per ogni campo dell'entità che memorizza il vecchio valore; un tale concetto ti consente una buona manipolazione dei tuoi dati ma penso che sia costoso, la mia soluzione è quella di convertire il mio oggetto in formato XML e archiviarlo come stringa con gli altri campi (changetime, id ...)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.