Comment l'enregistrement de chaque changement d'une ligne dans une base de données est-il généralement stocké?


10

Dans un projet sur lequel je travaille, chaque modification des lignes de certaines tables de la base de données doit être suivie pour un audit ou une restauration supplémentaire. Il doit être facile de trouver qui a modifié la ligne, à partir de quelle adresse IP et quand, et de pouvoir restaurer la version précédente.

La même chose est utilisée par exemple par Stack Exchange. Lorsque je modifie la question de quelqu'un d'autre, il est possible de constater que je l'ai modifiée et d'annuler les modifications.

Quelle est la technique générale utilisée pour stocker chaque modification d'un objet dans une base de données , étant donné que mon schéma actuel a essentiellement les mêmes propriétés (ci-dessous) qu'une application métier moyenne?

  • Les objets ont une taille relativement petite: il peut y en avoir nvarchar(1000)par exemple, mais pas d'énormes blobs de données binaires, celui-ci étant stocké directement sur le disque et accessible directement, et non via Microsoft SQL filestream,
  • La charge de la base de données est assez faible et toute la base de données est gérée par une machine virtuelle sur un serveur,
  • L'accès aux versions précédentes ne doit pas être aussi rapide que l'accès à la dernière version, mais doit toujours être à jour¹ et pas trop lent².

<tl-dr>

J'ai pensé aux cas suivants, mais je n'ai pas vraiment d'expérience avec ce genre de scénarios, donc j'entendrais les opinions des autres:

  1. Stockez tout dans la même table, en distinguant les lignes par ID et version. OMI, c'est vraiment stupide, et ça fera mal tôt ou tard au niveau des performances. Avec cette approche, il est également impossible de définir un niveau de sécurité différent pour les derniers éléments et pour le suivi des versions. Enfin, chaque requête serait plus compliquée à écrire. En fait, pour accéder aux données à jour, je serais obligé de tout regrouper par ID et de récupérer, dans chaque groupe, la dernière version.

  2. Stockez la dernière version dans une table et, à chaque modification, copiez la version obsolète dans une autre table dans un autre schéma. L'inconvénient est qu'à chaque fois, nous stockons chaque valeur, même si elle n'a pas changé. Définir des valeurs inchangées sur nulln'est pas une solution, car je dois également suivre le moment où la valeur est modifiée vers nullou depuis null.

  3. Stockez la dernière version dans une table et la liste des propriétés modifiées avec leurs valeurs précédentes dans une autre table. Cela semble avoir deux défauts: le plus important est que la seule façon de trier les types hétérogènes de valeurs précédentes dans la même colonne est d'avoir un binary(max). La seconde est qu'il serait, je crois, plus difficile d'utiliser une telle structure lors de l'affichage des versions précédentes à l'utilisateur.

  4. Faites la même chose que dans deux points précédents, mais stockez les versions dans une base de données distincte. Côté performances, cela peut être intéressant pour éviter de ralentir l'accès aux dernières versions en ayant les versions précédentes dans la même base de données; Pourtant, je pense que c'est une optimisation prématurée et ne doit être effectuée que s'il existe une preuve que le fait d'avoir des versions plus anciennes et plus récentes dans la même base de données est un goulot d'étranglement.

</tl-dr>


¹ Par exemple, il serait inacceptable de stocker les modifications dans un fichier journal, comme c'est le cas pour les journaux HTTP, et de vider les données du journal dans la base de données la nuit lorsque la charge du serveur est la plus faible. Les informations sur les différentes versions doivent être disponibles immédiatement ou presque immédiatement; un délai de quelques secondes est acceptable.

² Les informations ne sont pas consultées très fréquemment et uniquement par un groupe spécifique d'utilisateurs, mais il serait tout de même inacceptable de les obliger à attendre 30 secondes que la liste des versions s'affiche. Encore une fois, un délai de quelques secondes est acceptable.


3
Pertinent: SQL Server Change Data Capture .
Nick Chammas

Réponses:


8

La méthode normale pour effectuer la journalisation d'audit de ce type est d'avoir une table fantôme et de journaliser les modifications avec des déclencheurs sur la table de base que vous auditez. Les autres tables peuvent être placées sur un disque physique différent si vous en avez besoin pour les performances, et vous pouvez y placer des index si vous avez besoin de prendre en charge la récupération rapide des données.

Les tables auront à peu près la même structure que vos tables d'origine, mais auront une colonne datetime pour savoir quand la modification a eu lieu et un marqueur pour savoir si la ligne a été insérée, modifiée ou supprimée. Le séquençage des versions peut être effectué par l'horodatage.

La date de modification peut être effectuée en rendant la colonne datetime non nulle avec une valeur par défaut getdate (); une colonne d'utilisateur d'audit capturera l'utilisateur avec une colonne non nulle par défaut à Suser_Sname (). En supposant que l'utilisateur réel se fait emprunter l'identité dans la session, cela capturera l'identité de l'utilisateur effectuant la modification.

La base de données n'a aucun moyen de connaître l'adresse IP se connectant à un serveur Web. L'application devra capturer et enregistrer explicitement l'adresse IP avec la transaction.

Si vous souhaitez auditer un grand nombre de tables, vous pouvez utiliser les métadonnées du dictionnaire de données système pour générer les déclencheurs par programmation.

Cette solution est de loin la meilleure pour plusieurs raisons:

  • Il capture toutes les modifications apportées à la table, pas seulement celles apportées par l'application.

  • Les tables d'audit peuvent être placées sur un ensemble différent de disques pour réduire la charge d'E / S sur vos tables principales.

  • Vous pouvez utiliser une vue basée sur une union de la table et de la table du journal d'audit pour afficher l'ensemble de l'historique, y compris la version actuelle.

  • Vous pouvez indexer les tables du journal d'audit selon vos besoins afin que les utilisateurs d'audit puissent les interroger en réponse. Comme d'habitude, la sélection d'index est un compromis entre les performances des requêtes et la surcharge de mise à jour.


vous essayez de dire si j'ai 1000 tables dont j'ai besoin pour garder un journal pour tout changement, alors je dois créer 1000 shadow table hein? et 1000 déclencheurs pour capturer le changement? si oui, alors c'est une fausse idée ... nous pouvons créer une seule table d'historique et un seul déclencheur pour capturer et enregistrer les données modifiées. nous pouvons stocker les anciennes et nouvelles données de ligne dans ce tableau sous forme de fichier XML .... c'est ce que beaucoup de gens font .... suis-je clair !!
Thomas

1
Pour 1000 tables, vous écrivez un utilitaire qui lit les définitions du dictionnaire de données système et génère les déclencheurs et les définitions de table. Je l'ai fait sur un système avec 560 tables et cela fonctionne très bien.
ConcernedOfTunbridgeWells

0

Je connais de nombreux systèmes CMS (y compris Wordpress) qui utilisent une seule table pour stocker toutes les versions des données. Mais là encore, ils n'ont qu'à le faire pour le tableau qui contient les articles de blog. Voir la structure de la base de données Wordpress .

De plus, le nombre d'enregistrements et le nombre de révisions effectuées par chaque ligne joueront un rôle important dans votre décision.


0

À propos du versioning CMS; pour drupal, il crée une table spéciale pour chaque champ de l'entité qui stocke l'ancienne valeur; un tel concept vous permet une manipulation fine de vos données mais je pense que c'est cher, ma propre solution est de convertir mon objet au format xml et de le stocker en chaîne avec les autres champs (changetime, id ...)

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.