Détection des modifications dans une table SQL Server


13

Dans mon application, avec une base de données exécutée sur SQL Server 2012, j'ai un travail (tâche planifiée) qui exécute périodiquement une requête coûteuse et écrit les résultats dans une table qui peut ensuite être interrogée par l'application.

Idéalement, je voudrais exécuter cette requête coûteuse uniquement si quelque chose a changé depuis la dernière exécution de la requête. Étant donné que les tables source sont très grandes, je ne peux pas simplement sélectionner une somme de contrôle sur toutes les colonnes candidates ou quelque chose comme ça.

J'ai les idées suivantes:

  • Écrivez explicitement un dernier horodatage modifié, un indicateur "doit être des requêtes", ou quelque chose comme ça dans une table de suivi chaque fois que je change quelque chose dans une table source.
  • Utilisez un déclencheur pour faire de même.

Cependant, j'aimerais vraiment savoir s'il existe un moyen léger de détecter les modifications sur une table sans que je suive explicitement les écritures. Puis-je, par exemple, obtenir le "courant" ROWVERSIONd'une table ou quelque chose comme ça?

Réponses:


14

Non, il n'y en a pas. Toute sorte de suivi «dernière mise à jour à» se heurterait à un grave problème de performances car toutes les mises à jour, de toutes les transactions, tenteraient de mettre à jour le seul enregistrement suivant la «dernière mise à jour à». Cela signifierait effectivement qu'une seule transaction peut mettre à jour la table à tout moment, et toutes les autres transactions doivent attendre que la première soit validée . Sérialisation complète. Le nombre d'administrateurs / développeurs désireux de supporter une telle pénalité de performance juste pour le bénéfice de savoir quand la dernière mise à jour s'est produite est probablement faible.

Vous êtes donc obligé de le gérer via un code personnalisé. Cela signifie des déclencheurs, car l'alternative (détection à partir des enregistrements de journal) est une prérogative réservée uniquement à la réplication transactionnelle (ou son alter ego CDC ). Sachez que si vous essayez de le suivre via une colonne `` dernière mise à jour à '', vous serez alors confronté exactement au problème de sérialisation mentionné ci-dessus. Si la mise à jour de la concurrence est importante, vous devez utiliser un mécanisme de file d'attente (le déclencheur utilise un INSERT, puis un processus agrège les valeurs insérées pour formuler la `` dernière mise à jour à ''). N'essayez pas de tricher avec une solution «intelligente» comme se faufiler sur l'identité actuelle ou rechercher sys.dm_db_index_usage_stats . Et aussi une colonne "updated_at" par enregistrement, comme les horodatages Rails,

Existe-t-il une alternative «légère»? En fait, il y en a un, mais il est difficile de dire s'il fonctionnera pour vous et il est difficile de le faire correctement: Notifications de requête . La notification de requête fait exactement cela, elle établira une notification si des données ont changé et vous devez actualiser votre requête. Bien que la plupart des développeurs ne connaissent que son incarnation .Net en tant que SqlDependency, Query Notification peut être utilisé comme un mécanisme persistant de longue durée pour détecter les modifications de données. Comparé au véritable suivi des modifications, il sera vraiment léger et sa sémantique sera plus proche de vos besoins (quelque chose, n'importe quoi , changé, vous devez donc réexécuter la requête).

Mais à la fin, à votre place, je reconsidérerais vraiment mes hypothèses et retournerais à la planche à dessin. Vous pouvez peut-être utiliser l'envoi de journaux ou la réplication pour configurer une base de données de rapports sur un autre serveur. Ce que j'ai lu entre les lignes, c'est que vous avez besoin d'un pipeline ETL approprié et d'un entrepôt de données analytiques ...


Alors pourquoi Microsoft prendrait-il la peine de créer sys.dm_db_index_usage_stats, si les informations fournies ne peuvent pas être fiables?
Craig Efrein

Ce n'est pas un DMV conçu pour le suivi des modifications . Est très fiable pour l'objectif prévu, qui est le réglage des performances.
Remus Rusanu

8

On dirait que j'ai deux ans de retard dans le jeu, ici, mais il y a en effet une façon assez légère de faire ce que vous demandez.

Il existe deux mécanismes SQL Server qui peuvent vous aider. Votre solution ultime pourrait être un hybride des deux.

Modifier le suivi . SQL Server a la capacité de placer des tables spécifiques sous surveillance, en enregistrant uniquement les lignes qui ont changé (par leur valeur de clé primaire) et le type de changement (Insert, Update ou Delete). Une fois que vous avez configuré la détection des modifications sur un ensemble de tables, une requête légère peut vous indiquer si des modifications ont été apportées à la table depuis la dernière vérification. Le surcoût équivaut à peu près au maintien d'un index simple supplémentaire.

Rowversion / timestamp . Il s'agit d'un type de colonne varbinaire à 8 octets (pouvant être converti en BigInt) qui est incrémenté, à l'échelle de la base de données, chaque fois qu'une ligne qui en contient une est insérée ou mise à jour (cela ne facilite pas les suppressions). Si vous avez indexé ces colonnes, vous pouvez facilement savoir si les données de ligne ont changé en comparant le MAX (horodatage) à sa valeur depuis la dernière évaluation. Étant donné que la valeur augmente de façon monotone, cela vous donnerait une indication fiable que les données ont changé si la nouvelle valeur est supérieure à ce qu'elle était la dernière fois que vous l'avez vérifiée.


7

Si la source est en insertion uniquement, donnez-lui une IDENTITYcolonne. Lorsque vous effectuez votre transfert de données, vous enregistrez la valeur la plus élevée écrite. Lors du prochain transfert, il vous suffit de rechercher des valeurs supérieures à celles enregistrées lors du transfert précédent. Nous le faisons pour transférer des enregistrements de journal vers un entrepôt de données.

Pour les lignes pouvant être mises à jour, ajoutez un indicateur "sale". Il aura trois valeurs - propre, sale et supprimé. Les requêtes quotidiennes devront omettre des lignes avec l'indicateur défini sur "supprimé". Cela coûtera cher en maintenance, en test et en temps d'exécution. Après la grande requête, vous mentionnez que toutes les lignes marquées pour suppression doivent être supprimées et le drapeau réinitialisé pour toutes les autres. Cela n'évolue pas bien.

Une alternative plus légère à Change Data Capture est Change Tracking . Il ne vous dira pas quelles valeurs ont changé, juste que la ligne a changé depuis sa dernière requête. Les fonctions intégrées facilitent la récupération des valeurs modifiées et la gestion du suivi. Nous avons réussi à utiliser CT pour traiter environ 100 000 changements par jour dans une table de 100 000 000 lignes.

Les notifications de requête agissent toujours à un levier plus élevé - au niveau d'un ensemble de résultats. Conceptuellement, c'est comme définir une vue. Si SQL Server détecte que toute ligne renvoyée via cette vue a changé, il envoie un message à l'application. Il n'y a aucune indication sur le nombre de lignes modifiées ni sur les colonnes. Il n'y a qu'un simple message disant "quelque chose s'est produit". Il appartient à la demande de se renseigner et de réagir. Pratiquement, c'est beaucoup plus complexe que cela, comme vous pouvez l'imaginer. Il existe des restrictions sur la façon dont la requête peut être définie et la notification peut se déclencher pour des conditions autres que les données modifiées. Lorsque la notification se déclenche, elle est supprimée. Si d'autres activités intéressantes se produisent par la suite, aucun autre message ne sera envoyé.

Dans le cadre de la question du PO, QN aura l'avantage d'être peu coûteux à mettre en place et peu coûteux en temps d'exécution. Il peut être important de mettre en place et de maintenir un régime rigoureux d'abonnement-réaction-message. Étant donné que le tableau de données est volumineux, il est probable qu'il y aura des modifications fréquentes, ce qui signifie que la notification est susceptible de se déclencher dans la plupart des cycles de traitement. Comme il n'y a aucune indication de ce qui a changé, le traitement incrémentiel des deltas ne sera pas possible, comme ce serait le cas avec CT ou CDC. Les frais généraux dus à un faux déclenchement sont fastidieux, mais même dans le pire des cas, la requête coûteuse n'a pas besoin d'être exécutée plus fréquemment qu'elle ne l'est actuellement.


3

SqlTableDependency

SqlTableDependency est un composant d'implémentation de haut niveau pour accéder aux notifications contenant des valeurs d'enregistrement de table sur la base de données SQL Server.

SqlTableDependency est un composant C # générique utilisé pour recevoir des notifications lorsque le contenu d'une table de base de données spécifiée change.

Quelle est la différence avec .NET SqlDepenency?

Fondamentalement, la principale différence est que SqlTableDependency envoie des événements contenant des valeurs pour l'enregistrement inséré, modifié ou supprimé, ainsi que l'opération DML (insertion / suppression / mise à jour) exécutée sur la table: SqlDepenency ne dit pas quelles données ont été modifiées sur le table de base de données, ils disent seulement que quelque chose a changé.

Jetez un œil au projet GITHUB .


1

Si les mises à jour que vous attendez affectent un index (et seulement si), vous pouvez utiliser la table système sys.dm_db_index_usage_statspour détecter la dernière mise à jour d'un index sur la table en question. Vous utiliseriez le last_user_updatechamp.

Par exemple, pour obtenir les tableaux les plus récemment mis à jour:

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, pour vérifier si une table spécifique a été modifiée depuis une date spécifique:

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'

Quelle est votre opinion sur le commentaire de Remus ci-dessus? "N'essayez pas de tricher avec une solution" intelligente "comme se faufiler sur l'identité actuelle ou rechercher sys.dm_db_index_usage_stats." (Voir aussi son commentaire ci-dessous sa réponse.)
Fabian Schmied

1
@FabianSchmied Intéressant - Je n'avais pas vu que lorsque j'ai ajouté ma réponse, je ne pouvais rien trouver d'autoritaire à part une autre des réponses de Remus pour indiquer qu'il n'était pas fiable pour ce cas d'utilisation; la page MS pour dm_db_index_operational_statsaffiche les problèmes (effacés lorsque le cache de métadonnées s'efface), mais pas pour dm_db_index_usage_stats. Le seul problème que j'ai trouvé était avec les reconstructions d'index, les redémarrages du serveur et le détachement de la base de données effaçant les statistiques d'utilisation, et cela ne semblait pas s'appliquer ici. Serait intéressé de voir des informations justifiées à ce sujet.
Geoff
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.