Rilevamento delle modifiche in una tabella di SQL Server


13

Nella mia applicazione, con un DB in esecuzione su SQL Server 2012, ho un lavoro (attività pianificata) che esegue periodicamente una query costosa e scrive i risultati in una tabella che può essere successivamente interrogata dall'applicazione.

Idealmente, vorrei eseguire quella query costosa solo se qualcosa è cambiato dall'ultima esecuzione della query. Poiché le tabelle di origine sono molto grandi, non posso semplicemente selezionare un checksum su tutte le colonne candidate o qualcosa del genere.

Ho le seguenti idee:

  • Scrivi esplicitamente un ultimo timestamp modificato, un flag "must be query" o qualcosa del genere su una tabella di tracciamento ogni volta che cambio qualcosa in una tabella di origine.
  • Usa un grilletto per fare lo stesso.

Tuttavia, mi piacerebbe davvero sapere se esiste un modo leggero per rilevare le modifiche su una tabella senza che io segua esplicitamente le scritture. Posso, ad esempio, ottenere la "corrente" ROWVERSIONdi un tavolo o qualcosa del genere?

Risposte:


14

No, non ce n'è. Qualsiasi tipo di tracciamento "ultimo aggiornamento alle" si verificherebbe in un grave problema di prestazioni in quanto tutti gli aggiornamenti, da tutte le transazioni, tenterebbero di aggiornare il record unico che tracciava "l'ultimo aggiornamento alle". Ciò significherebbe effettivamente che una sola transazione può aggiornare la tabella in qualsiasi momento e tutte le altre transazioni devono attendere il primo commit . Serializzazione completa. Il numero di amministratori / sviluppatori disposti a sopportare una tale penalità delle prestazioni solo per il beneficio di sapere quando si è verificato l'ultimo aggiornamento è probabilmente piccolo.

Quindi sei bloccato per gestirlo tramite codice personalizzato. Ciò significa trigger poiché l'alternativa (rilevamento dai record di registro) è una prerogativa riservata solo alla replica transazionale (o al suo alter-ego CDC ). Tieni presente che se provi a rintracciarlo tramite una colonna "ultimo aggiornamento alle", dovrai affrontare esattamente il problema di serializzazione sopra menzionato. Se la concorrenza degli aggiornamenti è importante, allora dovresti utilizzare un meccanismo di coda (il trigger utilizza un INSERT e quindi un processo aggrega i valori inseriti per formulare "l'ultimo aggiornamento alle"). Non cercare di imbrogliare con una soluzione "intelligente" come sgattaiolare l'identità attuale o cercare sys.dm_db_index_usage_stats . E anche una colonna "aggiornata_at" per record, come hanno i timestamp di Rails,

Esiste un'alternativa "leggera"? In realtà ce n'è uno, ma è difficile dire se funzionerà per te ed è difficile farlo bene: Notifiche di query . Query Notification fa esattamente questo, imposterà una notifica se alcuni dati sono cambiati ed è necessario aggiornare la query. Sebbene la maggior parte degli sviluppatori abbia familiarità solo con la sua incarnazione .Net come SqlDependency, la notifica delle query può essere utilizzata come meccanismo persistente e di lunga durata per rilevare la modifica dei dati. Rispetto al vero rilevamento delle modifiche sarà davvero leggero e la sua semantica è più vicina alle tue esigenze (qualcosa, qualsiasi cosa , è cambiata, quindi devi rieseguire la query).

Ma alla fine, al posto tuo, riconsidererei davvero i miei presupposti e tornerei al tavolo da disegno. Forse è possibile utilizzare la distribuzione dei log o la replica per impostare un database di report su un altro server. Quello che ho letto tra le righe è che hai bisogno di una corretta pipeline ETL e di un data warehouse di analisi ...


Quindi perché Microsoft dovrebbe preoccuparsi di creare sys.dm_db_index_usage_stats, se non è possibile fare affidamento sulle informazioni fornite?
Craig Efrein,

Non è un DMV progettato per il rilevamento delle modifiche . È molto affidabile per lo scopo previsto, ovvero l'ottimizzazione delle prestazioni.
Remus Rusanu,

8

Sembra che io sia in ritardo di due anni, qui, ma c'è davvero un modo piuttosto leggero di fare ciò che stai chiedendo.

Esistono due meccanismi di SQL Server che possono aiutarti. La tua soluzione definitiva potrebbe essere un ibrido dei due.

Monitoraggio delle modifiche . SQL Server ha la capacità di mettere sotto controllo tabelle specifiche, registrando solo quali righe sono state modificate (in base al loro valore di chiave primaria) e che tipo di modifica è stata (Inserisci, Aggiorna o Elimina). Una volta impostato il rilevamento delle modifiche su un set di tabelle, una query leggera può dire se sono state apportate modifiche alla tabella dall'ultima volta che hai controllato. Il sovraccarico è approssimativamente uguale al mantenimento di un indice semplice aggiuntivo.

Rowversion / timestamp . Questo è un tipo di colonna varbinary a 8 byte (convertibile in un BigInt) che viene incrementato, a livello di database, ogni volta che una riga che ne contiene una viene inserita o aggiornata (non aiuta con le eliminazioni). Se hai indicizzato queste colonne, puoi facilmente capire se i dati delle righe sono cambiati confrontando il MAX (timestamp) con il suo valore dall'ultima volta che sono stati valutati. Poiché il valore sta aumentando monotonicamente, ciò darebbe un'indicazione affidabile che i dati sono cambiati se il nuovo valore è maggiore di quello dell'ultima volta che lo hai verificato.


7

Se l'origine è solo inserimento, assegnagli una IDENTITYcolonna. Quando si esegue il trasferimento dei dati, si registra il valore più alto scritto. Durante il trasferimento successivo è necessario solo eseguire una query per valori superiori a quelli registrati durante il trasferimento precedente. Lo facciamo per il trasferimento dei record di registro in un data warehouse.

Per le file aggiornabili aggiungere un flag "sporco". Avrà tre valori: pulito, sporco ed eliminato. Le query quotidiane dovranno omettere le righe con il flag impostato su "eliminato". Questo sarà costoso in termini di manutenzione, test e tempo di esecuzione. Dopo la query di grandi dimensioni si menziona tutte le righe contrassegnate per l'eliminazione devono essere rimosse e il flag ripristinato per tutte le altre. Questo non si ridimensionerà bene.

Un'alternativa più leggera a Change Data Capture è il rilevamento delle modifiche . Non ti dirà quali valori sono cambiati, solo che la riga è cambiata dall'ultima query. Le funzioni integrate facilitano il recupero di valori modificati e la gestione del monitoraggio. Abbiamo avuto successo utilizzando CT per elaborare circa 100.000 modifiche al giorno in una tabella di 100.000.000 di righe.

Le notifiche delle query agiscono ancora a una leva superiore, a livello di un set di risultati. Concettualmente, è come definire una vista. Se SQL Server rileva che qualsiasi riga restituita tramite tale vista è stata modificata, genera un messaggio per l'applicazione. Non vi sono indicazioni sul numero di righe modificate o su quali colonne. C'è solo un semplice messaggio che dice "qualcosa è successo". Spetta all'applicazione informarsi e reagire. Praticamente è molto più complesso di così, come puoi immaginare. Esistono restrizioni su come definire la query e la notifica può essere attivata per condizioni diverse dalla modifica dei dati. Quando la notifica viene attivata, viene rimossa. Se successivamente si verificano ulteriori attività di interesse, non verranno inviati ulteriori messaggi.

Nel contesto della domanda del PO, QN avrà il vantaggio di essere un overhead basso da impostare e costi di gestione ridotti. Può essere uno sforzo significativo per stabilire e mantenere un rigoroso regime di abbonamento-messaggio-reazione. Poiché la tabella dei dati è grande, è probabile che vi siano frequenti modifiche, il che significa che la notifica si attiverà nella maggior parte dei cicli di elaborazione. Poiché non vi è alcuna indicazione di ciò che non sarà possibile modificare l'elaborazione incrementale dei delta, come farebbe con CT o CDC. Il sovraccarico dovuto a falsi trigger è noioso, ma anche nel peggiore dei casi non è necessario eseguire la query costosa più frequentemente di quanto non sia attualmente.


3

SqlTableDependency

SqlTableDependency è un componente di implementazione di alto livello per accedere alle notifiche contenenti i valori dei record di tabella nel database di SQL Server.

SqlTableDependency è un componente C # generico utilizzato per ricevere notifiche quando cambia il contenuto di una tabella di database specificata.

Qual è la differenza con .NET SqlDepenency?

Fondamentalmente, la differenza principale è che SqlTableDependency invia eventi contenenti valori per il record inserito, modificato o eliminato, così come l'operazione DML (inserisci / elimina / aggiorna) eseguita sulla tabella: SqlDepenency non dice quali dati sono stati cambiati nel tabella del database, dicono solo che qualcosa è cambiato.

Dai un'occhiata al progetto GITHUB .


1

Se gli aggiornamenti che ti aspetti riguardano un indice (e solo se), puoi utilizzare la tabella di sistema sys.dm_db_index_usage_statsper rilevare l'ultimo aggiornamento di un indice nella tabella in questione. Useresti il last_user_updatecampo.

Ad esempio, per ottenere le tabelle aggiornate più di recente:

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

Oppure, per verificare se una tabella specifica è stata modificata da una data specifica:

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 è la tua opinione sul commento di Remus sopra? "Non cercare di imbrogliare con una soluzione 'intelligente' come sgattaiolare l'identità attuale o cercare sys.dm_db_index_usage_stats." (Vedi anche il suo commento sotto la sua risposta.)
Fabian Schmied,

1
@FabianSchmied Interessante: non avevo visto che quando ho aggiunto la mia risposta non sono riuscito a trovare nulla di autorevole a parte un'altra delle risposte di Remus per indicare che non è affidabile per questo caso d'uso; la pagina MS per dm_db_index_operational_statsmostra i problemi (cancellata come cancella la cache dei metadati), ma non per dm_db_index_usage_stats. L'unico problema che ho riscontrato è stato con ricostruzioni dell'indice, riavvii del server e distacco del database che hanno cancellato le statistiche di utilizzo e non sembrava essere stato applicato qui. Sarei interessato a vedere informazioni comprovate su questo.
Geoff,
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.