Projekt bazy danych do rejestrowania audytu


151

Za każdym razem, gdy potrzebuję zaprojektować nową bazę danych, spędzam trochę czasu zastanawiając się, jak skonfigurować schemat bazy danych, aby prowadzić dziennik audytu zmian.

Zadano już tutaj kilka pytań na ten temat, ale nie zgadzam się, że istnieje jedno najlepsze podejście do wszystkich scenariuszy:

Natknąłem się również na ten interesujący artykuł o utrzymywaniu dziennika zmian w bazie danych, w którym próbuję wymienić zalety i wady każdego podejścia. Jest bardzo dobrze napisany i zawiera ciekawe informacje, ale jeszcze bardziej utrudnia mi to podjęcie decyzji.

Moje pytanie brzmi: czy istnieje odniesienie, którego mogę użyć, może książka lub coś w rodzaju drzewa decyzyjnego, do którego mogę się odwołać, aby zdecydować, w którą stronę powinienem pójść, na podstawie pewnych zmiennych wejściowych, takich jak:

  • Dojrzałość schematu bazy danych
  • Sposób odpytywania dzienników
  • Prawdopodobieństwo, że trzeba będzie odtworzyć rekordy
  • Co ważniejsze: wydajność zapisu lub odczytu
  • Charakter rejestrowanych wartości (ciąg, liczby, obiekty blob)
  • Dostępna przestrzeń do przechowywania

Znane mi podejścia to:

1. Dodaj kolumny dla daty utworzenia i modyfikacji oraz użytkownika

Przykład tabeli:

  • ID
  • wartość_1
  • wartość_2
  • wartość_3
  • stworz Date
  • zmieniona data
  • stworzone przez
  • modyfikowane przez

Główne wady: tracimy historię modyfikacji. Nie można wycofać zmian po zatwierdzeniu.

2. Wstaw tylko tabele

Przykład tabeli :

  • ID
  • wartość_1
  • wartość_2
  • wartość_3
  • z
  • do
  • usunięte (Boolean)
  • użytkownik

Główne wady: Jak aktualizować klucze obce? Potrzeba ogromnej przestrzeni

3. Utwórz oddzielną tabelę historii dla każdej tabeli

Przykład tabeli historii:

  • ID
  • wartość_1
  • wartość_2
  • wartość_3
  • wartość_4
  • użytkownik
  • usunięte (Boolean)
  • znak czasu

Główne wady: Musi powielić wszystkie kontrolowane tabele. Jeśli schemat ulegnie zmianie, konieczna będzie również migracja wszystkich dzienników.

4. Utwórz skonsolidowaną tabelę historii dla wszystkich tabel

Przykład tabeli historii:

  • Nazwa tabeli
  • pole
  • użytkownik
  • Nowa wartość
  • usunięte (Boolean)
  • znak czasu

Główne wady: Czy będę w stanie łatwo odtworzyć rekordy (wycofać) w razie potrzeby? Kolumna nowa_wartość musi być ogromnym ciągiem, aby mogła obsługiwać wszystkie różne typy kolumn.



1
a co z używaniem bazy danych historii zamiast tabel?
Jowen

Może mógłbyś sprawdzić projekt github.com/airblade/paper_trail
zx1986

Czy to zły pomysł, aby rejestrować wszystkie (wymagane) zapytania wykonane tak, jak jest?
Dinushan

Odpowiedzi:


87

Jedną z metod używanych na kilku platformach wiki jest oddzielenie danych identyfikacyjnych od kontrolowanej zawartości. Dodaje to złożoności, ale w końcu otrzymujesz ścieżkę audytu pełnych rekordów, a nie tylko list edytowanych pól, które następnie musisz zmiksować, aby dać użytkownikowi wyobrażenie o tym, jak wyglądał stary rekord.

Na przykład, gdybyś miał tabelę o nazwie Szanse sprzedaży do śledzenia umów sprzedaży, w rzeczywistości utworzyłbyś dwie oddzielne tabele:

Opportunities
Opportunities_Content (lub coś w tym rodzaju)

Szanse stół miałby informacje chcesz użyć, aby jednoznacznie identyfikować rekord i byłoby domu klucz podstawowy chcesz odniesienia dla swoich zagranicznych kluczowych relacji. Opportunities_Content tabela będzie posiadać wszystkie pola użytkownicy mogą zmieniać i dla którego chcesz zachować ślad rewizyjny. Każdy rekord w tabeli zawartości zawierałby własną PK oraz dane zmodyfikowane przez i data modyfikacji. Szanse tabela będzie zawierać odniesienie do obecnej wersji, jak również informacje o tym, kiedy główny rekord został pierwotnie stworzony i przez kogo.

Oto prosty przykład:

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

A zawartość:

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

Prawdopodobnie uczyniłbym PK tabeli zawartości kluczem wielokolumnowym z identyfikatora strony i wersji, pod warunkiem, że wersja byłaby typem tożsamości. Użyjesz kolumny Revision jako FK. Następnie wyciągasz skonsolidowany rekord, łącząc się w ten sposób:

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

Tam mogą być jakieś błędy ... to jest z głowy. Powinien jednak dać wyobrażenie o alternatywnym wzorze.


10
Pod względem dobrego podejścia do audytu, ale w przypadku produkcji zajmie dużo czasu opracowanie oddzielnej tabeli audytu dla każdej tabeli w bazie danych, napisanie wyzwalaczy dla każdej tabeli w celu wychwycenia zmian i zapisanie ich w tabeli audytu. Co więcej, opracowanie jednego raportu z audytu dla wszystkich tabel jest ogromnym wyzwaniem, ponieważ każda tabela audytu ma inną strukturę.
asim-ishaq

11
Jeśli pisanie i utrzymywanie skryptów dla każdej tabeli jest problemem dla organizacji, która zamierza zarządzać bazą danych poddaną audytowi, naturalnie zaleciłbym zatrudnienie albo doświadczonego administratora danych, albo wysoce elastycznego i bardzo doświadczonego inżyniera oprogramowania z odpowiednim doświadczeniem w tworzeniu kontrolowanych baz danych .
Hardryv

1
Czy to prawda, że PageContent.PageIDto FK to Page.IDi Page.CurrentRevisionFK to PageContent.Revision? Czy ta zależność jest naprawdę okrężna?

2
Głosowałem negatywnie, ponieważ nie dotyczy on wspomnianych alternatyw. Daje inną opcję, która jest bardzo specyficznym rozwiązaniem dla bardzo konkretnego przypadku użycia. Ale widzę zalety sugerowanego projektu
acteon

1
Przychodzi mi do głowy bardzo niewiele pól, o których mógłbym śmiało powiedzieć, że się nie zmienią, więc wszystkie „główne” tabele dla każdej jednostki po prostu będą id, revision_id; bardziej przypominał stół łączeniowy. To dla mnie trochę śmierdzące. Jaką ma to przewagę nad podejściem 3 w PO (tabela historii na tabelę poddaną audytowi)?
Kenmore

14

Jeśli używasz SQL Server 2008, prawdopodobnie powinieneś rozważyć zmianę przechwytywania danych. Jest to nowość na rok 2008 i może zaoszczędzić znaczną ilość pracy.


tutaj jest łącze do informacji o śledzeniu zmian w programie SQL 2012. msdn.microsoft.com/en-us/library/bb933994.aspx +1 za korzystanie z wbudowanych funkcji, nie ma sensu ponownie wymyślać koła.
Chris,

4
@Chris, czy sam go kiedyś używałeś? Rzeczywiście, śledzi wszystko ... ale możliwość wyciągnięcia z niego przydatnych informacji to zupełnie inna historia. Nie mogę użyć koła traktora do mojego roweru.
Jowen,

To naprawdę byłoby niesamowite. Ale jeśli masz tylko wersję Standard SQL Server, tak jak ja, nie masz szczęścia: „Przechwytywanie danych zmian jest dostępne tylko w wersjach Enterprise , Developer i Enterprise Evaluation ”.
Brad Turek

6

Nie znam żadnego odniesienia, ale jestem pewien, że ktoś coś napisał.

Jeśli jednak celem jest po prostu rejestracja tego, co się wydarzyło - najbardziej typowe użycie dziennika audytu - dlaczego nie po prostu zachować wszystkiego:

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

Przypuszczalnie jest to utrzymywane przez spust.


Nie znam żadnego sposobu na uzyskanie tego w serwerze bazy danych, ale oczywiście można to zrobić z zewnątrz.
wallyk

5
Wydaje mi się, że jest to ten sam wzorzec projektowy, co czwarta opcja pokazana w pierwotnym pytaniu.
givanse

3

Stworzymy małą przykładową bazę danych dla aplikacji do blogowania. Wymagane są dwie tabele:

blog: przechowuje unikalny identyfikator posta, tytuł, treść i usuniętą flagę. audit: przechowuje podstawowy zestaw zmian historycznych z identyfikatorem rekordu, identyfikatorem wpisu na blogu, typem zmiany (NOWA, EDYCJA lub USUŃ) oraz datą / godziną tej zmiany. Poniższy kod SQL tworzy blogi indeksuje usuniętą kolumnę:

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';

Poniższy kod SQL tworzy audittabelę. Wszystkie kolumny są indeksowane, a klucz obcy jest definiowany dla audit.blog_id, który odwołuje się do blog.id. Dlatego też, gdy fizycznie USUWAMY wpis na blogu, usuwana jest również jego pełna historia kontroli.

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;

2

Myślę, że nie ma to jak drzewo decyzyjne. Ponieważ niektóre zalety i wady (lub wymagania) nie są tak naprawdę policzalne. Jak na przykład mierzy się dojrzałość?

Po prostu wyrównaj wymagania biznesowe dotyczące rejestrowania audytu. Spróbuj przewidzieć, jak te wymagania mogą się zmienić w przyszłości i wygeneruj swoje wymagania techniczne. Teraz możesz porównać to z zaletami i wadami i wybrać odpowiednią / najlepszą opcję.

Zapewniamy, że nie ma znaczenia, jak zdecydujesz, zawsze znajdzie się ktoś, kto myśli, że podjąłeś złą decyzję. Jednak odrobiłeś pracę domową i uzasadniłeś swoją decyzję.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.