Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Przechowuj wszystkie zmiany danych ze wszystkimi szczegółami (np. Stackoverflow)

Myślałem o tym od jakiegoś czasu i mogę wymyślić tylko dwa sposoby, aby to zrobić. Oba mogą działać w pełni transparentnie po utworzeniu w abstrakcyjnej warstwie/modelu danych.

Nawiasem mówiąc, w doktrynie mapowania ORM istnieje implementacja danych tabelowych „wersjonowalnych”. Zobacz ten przykład w ich dokumentacji . Może to pasuje do twoich potrzeb, ale nie pasuje do moich. Wydaje się, że usuwa wszystkie dane historii po usunięciu oryginalnego rekordu, przez co nie jest on tak naprawdę bezpieczny dla wersji.

Opcja A:posiadanie kopii każdej tabeli do przechowywania danych wersji

Załóżmy, że masz prostą tabelę kontaktów:

CREATE TABLE contact (
    id INT NOT NULL auto_increment,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    PRIMARY KEY (id)
)

Utworzyłbyś kopię tej tabeli i dodałbyś dane rewizji:

CREATE TABLE contact_revisions (
    id INT NOT NULL,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    revision_id INT auto_increment,
    type ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
    change_time DEFAULT current_timestamp,
    PRIMARY KEY(revision_id)
)

Śledź INSERT i UPDATE używając AFTER wyzwalacze. Przy każdej nowej rewizji danych w oryginale wstaw kopię nowych danych do tabeli rewizji i ustaw modyfikację type prawidłowo.

Aby zarejestrować DELETE bezpieczny pod względem wersji, musisz również wstawić nowy wiersz w tabeli historii! W tym celu należy użyć BEFORE DELETE wyzwalać i przechowywać najnowsze wartości, zanim zostaną usunięte. W przeciwnym razie będziesz musiał usunąć każdy NOT NULL ograniczenie w tabeli historii.

Kilka ważnych uwag dotyczących tej implementacji

  • W przypadku tabeli historii musisz usunąć każdy UNIQUE KEY (tutaj:PRIMARY KEY ) z tabeli wersji, ponieważ będziesz mieć ten sam klucz wielokrotnie dla każdej wersji danych.
  • Gdy ALTER schemat i dane w oryginalnej tabeli poprzez aktualizację (np. aktualizację oprogramowania) należy upewnić się, że te same dane lub poprawki schematu zostały zastosowane również do tabeli historii i jej danych. W przeciwnym razie napotkasz kłopoty podczas powrotu do starszej wersji zestawu rekordów.
  • W rzeczywistej implementacji chciałbyś wiedzieć, który użytkownik zmodyfikował dane. Aby zapewnić bezpieczeństwo rewizji, rekord użytkownika nigdy nie powinien być usuwany z tabeli użytkowników. Powinieneś po prostu ustawić wyłączone konto za pomocą flagi.
  • Zazwyczaj jedno działanie użytkownika obejmuje więcej niż jedną tabelę. W realnej implementacji musiałbyś również śledzić, które zmiany w wielu tabelach należą do jednej transakcji użytkownika, a także w jakiej kolejności. W prawdziwym przypadku użycia chciałbyś cofnąć wszystkie zmiany jednej transakcji razem, w odwrotnej kolejności. To wymagałoby dodatkowej tabeli wersji, która śledzi użytkowników i transakcje oraz utrzymuje luźny związek ze wszystkimi tymi indywidualnymi wersjami w tabelach historii.

Korzyści:

  • całkowicie w bazie danych, niezależnie od kodu aplikacji. (cóż, nie wtedy, gdy śledzenie transakcji użytkownika jest ważne. wymagałoby to pewnej logiki poza zakresem pojedynczego zapytania)
  • wszystkie dane są w oryginalnym formacie, bez niejawnych konwersji typów.
  • dobra wydajność w wyszukiwaniu w wersjach
  • łatwe wycofanie. Po prostu wykonaj proste INSERT .. ON DUPLICATE KEY UPDATE .. oświadczenie w oryginalnej tabeli, używając danych z wersji, którą chcesz przywrócić.

Zasługi:

  • Trudno zaimplementować ręcznie.
  • Trudne (ale nie niemożliwe) zautomatyzowanie, jeśli chodzi o migracje baz danych / aktualizacje aplikacji.

Jak już wspomniano powyżej, doktryny versionable robi coś podobnego.

Opcja B:posiadanie centralnej tabeli dziennika zmian

przedmowa:zła praktyka, pokazana tylko w celu zilustrowania alternatywy.

To podejście w dużym stopniu opiera się na logice aplikacji, która powinna być ukryta w warstwie/modelu danych.

Masz centralną tabelę historii, która śledzi

  • Kto zrobił
  • kiedy
  • modyfikować, wstawiać lub usuwać
  • jakie dane
  • w którym polu
  • z którego stołu

Podobnie jak w innym podejściu, możesz również chcieć śledzić, które poszczególne zmiany danych należą do pojedynczej akcji / transakcji użytkownika iw jakiej kolejności.

Korzyści:

  • nie ma potrzeby utrzymywania synchronizacji z oryginalną tabelą podczas dodawania pól do tabeli lub tworzenia nowej tabeli. skaluje się przezroczyście.

Zasługi:

  • zła praktyka przy użyciu prostej wartości =magazynu kluczy w bazie danych
  • zła wydajność wyszukiwania z powodu niejawnych konwersji typu
  • może spowolnić ogólną wydajność aplikacji/bazy danych, gdy centralna tabela historii staje się wąskim gardłem z powodu blokad zapisu (dotyczy to tylko określonych silników z blokadami tabel, np. MyISAM)
  • Wdrożenie wycofywania zmian jest znacznie trudniejsze
  • możliwe błędy konwersji danych / utrata precyzji z powodu niejawnej konwersji typu
  • nie śledzi zmian, gdy uzyskujesz bezpośredni dostęp do bazy danych gdzieś w kodzie zamiast korzystać z modelu / warstwy danych i zapominasz, że w takim przypadku musisz ręcznie zapisywać w dzienniku wersji. Może być dużym problemem podczas pracy w zespole z innymi programistami.

Wniosek:

  • Opcja B może być bardzo przydatny w przypadku małych aplikacji jako prosty „wpadnij”, gdy służy tylko do rejestrowania zmian.
  • Jeśli chcesz cofnąć się w czasie i móc łatwo porównać różnice między historyczną wersją 123 do wersji 125 i/lub przywróć stare dane, a następnie Opcja A to trudna droga.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Railsy:Jak podzielić zapytanie zapisu/odczytu na bazę danych master/slave

  2. MySQL JOIN zwraca NULL pól

  3. Co to jest tabela nadrzędna i tabela podrzędna w bazie danych?

  4. MySQL Connector/Python - wstaw zmienną Pythona do tabeli MySQL

  5. Dostawcy członkostwa/roli ASP.NET dla MySQL?