web-dev-qa-db-fra.com

Conception de base de données pour la journalisation d'audit

Chaque fois que je dois concevoir une nouvelle base de données, je réfléchis assez longuement à la manière dont je devrais configurer le schéma de base de données pour conserver un journal d'audit des modifications.

Certaines questions ont déjà été posées à ce sujet, mais je ne suis pas d'accord sur le fait qu'il existe une meilleure approche pour tous les scénarios:

Je suis également tombé sur cette article intéressant sur la gestion d'un journal des modifications de la base de données qui tente de répertorier les avantages et les inconvénients de chaque approche. C'est très bien écrit et contient des informations intéressantes, mais cela a rendu mes décisions encore plus difficiles.

Ma question est la suivante: Existe-t-il une référence que je peux utiliser, peut-être un livre ou quelque chose comme un arbre de décision auquel je peux me référer pour décider de quelle manière dois-je choisir? aller en fonction de certaines variables d'entrée, comme:

  • La maturité du schéma de base de données
  • Comment les journaux seront interrogés
  • La probabilité qu'il sera nécessaire de recréer des enregistrements
  • Quoi de plus important: performance d'écriture ou de lecture
  • Nature des valeurs en cours de journalisation (chaîne, nombres, blobs)
  • Espace de stockage disponible

Les approches que je connais sont:

1. Ajouter des colonnes pour la date et l'utilisateur créés et modifiés

Exemple de tableau:

  • identifiant
  • valeur_1
  • valeur_2
  • valeur_3
  • created_date
  • date modifiée
  • créé par
  • modifié par

Principaux inconvénients: Nous perdons l'historique des modifications. Impossible de revenir en arrière après la validation.

2. Insérer uniquement des tableaux

exemple de table :

  • identifiant
  • valeur_1
  • valeur_2
  • valeur_3
  • de
  • à
  • supprimé (booléen)
  • utilisateur

Principaux inconvénients: Comment garder les clés étrangères à jour? Immense espace nécessaire

3. Créer une table d'historique distincte pour chaque table

Exemple de table d'historique:

  • identifiant
  • valeur_1
  • valeur_2
  • valeur_3
  • valeur_4
  • utilisateur
  • supprimé (booléen)
  • horodatage

Inconvénients majeurs: il est nécessaire de dupliquer toutes les tables auditées. Si le schéma change, il sera également nécessaire de migrer tous les journaux.

4. Créer une table d'historique consolidé pour toutes les tables

Exemple de table d'historique:

  • nom de la table
  • champ
  • utilisateur
  • nouvelle valeur
  • supprimé (booléen)
  • horodatage

Principaux inconvénients: Est-ce que je pourrai recréer les enregistrements (restauration) si nécessaire facilement? La colonne new_value doit être une chaîne énorme pour pouvoir prendre en charge tous les types de colonnes.

140
jbochi

Une méthode utilisée par quelques plateformes wiki consiste à séparer les données d'identification du contenu que vous auditez. Cela ajoute de la complexité, mais vous vous retrouvez avec une piste de vérification des enregistrements complets, et pas seulement des listes de champs modifiés que vous devez ensuite assembler pour donner à l'utilisateur une idée de l'apparence de l'ancien enregistrement.

Ainsi, par exemple, si vous disposiez d'une table appelée Opportunités pour suivre les ventes, créez deux tables distinctes:

Opportunités
Opportunities_Content (ou quelque chose comme ça)

La table Opportunities contient les informations que vous utilisez pour identifier de manière unique l’enregistrement et héberge la clé primaire que vous référencez pour vos relations de clé étrangère. La table Opportunities_Content contient tous les champs que vos utilisateurs peuvent modifier et pour lesquels vous souhaitez conserver une trace d'audit. Chaque enregistrement de la table Content inclurait son propre PK et les données modifiées et à date modifiée. La table Opportunities inclurait une référence à la version actuelle ainsi que des informations sur la date de création de l'enregistrement principal et par qui.

Voici un exemple simple:

CREATE TABLE dbo.Page(  
    ID int PRIMARY KEY,  
    Name nvarchar(200) NOT NULL,  
    CreatedByName nvarchar(100) NOT NULL, 
    CurrentRevision int NOT NULL, 
    CreatedDateTime datetime NOT NULL

Et le contenu:

CREATE TABLE dbo.PageContent(
    PageID int NOT NULL,
    Revision int NOT NULL,
    Title nvarchar(200) NOT NULL,
    User nvarchar(100) NOT NULL,
    LastModified datetime NOT NULL,
    Comment nvarchar(300) NULL,
    Content nvarchar(max) NOT NULL,
    Description nvarchar(200) NULL

Je ferais probablement de la table des matières une clé à plusieurs colonnes à partir de PageID et de la révision, à condition que Revision soit un type d'identité. Vous utiliseriez la colonne Révision comme FK. Vous extrayez ensuite l’enregistrement consolidé en JOINING comme ceci:

SELECT * FROM Page
JOIN PageContent ON CurrentRevision = Revision AND ID = PageID

Il y a peut-être des erreurs là-haut… c'est une idée qui m'est venue à l'esprit. Cela devrait vous donner une idée d'un modèle alternatif, cependant.

81
Josh Anderson

Si vous utilisez SQL Server 2008, vous devriez probablement envisager de modifier la capture de données. Ceci est nouveau pour 2008 et pourrait vous faire économiser beaucoup de travail.

13
Randy Minder

Je ne connais aucune référence, mais je suis sûr que quelqu'un a écrit quelque chose.

Toutefois, si le but est simplement d’avoir un compte-rendu de ce qui s’est passé - l’utilisation la plus typique d’un journal d’audit -, pourquoi ne pas simplement tout garder:

timestamp
username
ip_address
procedureName (if called from a stored procedure)
database
table
field
accesstype (insert, delete, modify)
oldvalue
newvalue

On présume que cela est maintenu par un déclencheur.

6
wallyk

Nous allons créer un petit exemple de base de données pour une application de blogging. Deux tables sont requises:

blog: stocke un ID d'article unique, le titre, le contenu et un indicateur supprimé. audit: stocke un ensemble de base de modifications historiques avec un ID d'enregistrement, l'ID de publication de blog, le type de modification (NEW, EDIT ou DELETE) et la date/heure de cette modification. Le code SQL suivant crée le blog et indexe la colonne supprimée:

CREATE TABLE `blog` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `title` text,
    `content` text,
    `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    KEY `ix_deleted` (`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Blog posts';

Le code SQL suivant crée la table audit. Toutes les colonnes sont indexées et une clé étrangère est définie pour audit.blog_id qui fait référence à blog.id. Par conséquent, lorsque nous SUPPRIMONS physiquement une entrée de blog, son historique d’audit complet est également supprimé.

CREATE TABLE `audit` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `blog_id` mediumint(8) unsigned NOT NULL,
    `changetype` enum('NEW','EDIT','DELETE') NOT NULL,
    `changetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `ix_blog_id` (`blog_id`),
    KEY `ix_changetype` (`changetype`),
    KEY `ix_changetime` (`changetime`),
    CONSTRAINT `FK_audit_blog_id` FOREIGN KEY (`blog_id`) REFERENCES `blog` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
3
ajit

Je pense qu'il n'y a rien comme un arbre de décision. Certains des avantages et des inconvénients (ou des exigences) ne sont pas vraiment dénombrables. Comment mesurez-vous la maturité par exemple?

Il vous suffit donc d’aligner les exigences de votre entreprise pour la journalisation de vos audits. Essayez de prédire comment ces exigences pourraient changer à l'avenir et créez vos exigences techniques. Maintenant, vous pouvez comparer le pour et le contre et choisir la bonne/meilleure option.

Et soyez assuré, peu importe la façon dont vous décidez, il y aura toujours quelqu'un qui pense que vous avez pris la mauvaise décision. Cependant, vous avez fait vos devoirs et vous justifiez votre décision.

2
Peter Schuetze