2012-03-24 19 views
16

Mam system napisany przy użyciu Codeigniter i jako baza danych wykorzystująca MySQL. System ma użytkownika, grupy użytkowników z różnymi uprawnieniami itp. Mają wiele tabel mysql, które mają wiele do wielu relacji.Wszystkie zmiany danych przechowuj ze wszystkimi szczegółami (takimi jak Stackoverflow).

Niektóre z tabel mam:

  • przedmiotów
  • kontrakty
  • klienci
  • produkty
  • product_features
  • zlecenia
  • order_features
  • lub der_products
  • etc ...

Obecnie jestem zalogowaniu każdą zmianę na danych dla tych tabel, które wykonanych przez użytkowników. Użytkownicy mogą zmieniać te dane ze względu na ich uprawnienia. Przechowywanie zmiana dzienników tylko prosty formularz jak

A user changed product features with id of A8767 
B user added new customer with id 56 
C user edited content of orderlist 
A user added new product (id: A8767) to order (id: or67) 
... 

chcę zachować wszystkie zmiany, które wykonane z każdym szczególe, jak edycja historii zapytania Stackoverflow. Mogę myśleć o projekcie log_table, aby zachować wszystkie zmiany danych z różnych tabel. Czy jest jakiś sposób, samouczek, silnik, wtyczka do tego? Tylko mogę myśleć, że robię duplikat każdego stołu i zapisywać zmiany na nich, ale nie sądzę, że jest to dobry sposób.

+4

Możesz przeczytać artykuł Wikipedii o wolno zmieniających się wymiarach. –

+1

związane: http://stackoverflow.com/questions/762405/database-data-versioning – Kaii

+0

@ ta.speot.is dzięki będę spojrzeć – safarov

Odpowiedz

18

Myślałem o tym już od jakiegoś czasu i mogę wymyślić tylko dwa sposoby na zrobienie tego. Oba mogą pracować w pełni przezroczysto, gdy zostaną utworzone w abstrakcyjnej warstwie danych/modelu.

Nawiasem mówiąc, istnieje implementacja "wersjonowalnych" danych tabeli w doktrynie mapowania ORM. Zobacz ten example in their docs. Może to pasuje do twoich potrzeb, ale nie pasuje do moich. Wydaje się, że usuwa wszystkie dane historii po usunięciu oryginalnego rekordu, co sprawia, że ​​naprawdę nie jest to bezpieczne.

Wariant A: mieć kopię każdej tabeli do przechowywania danych rewizji

Powiedzmy, że masz prostą tabelę kontaktowy:

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

można utworzyć kopię tej tabeli i dodać rewizji dane:

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) 
) 

śledzić INSERT i UPDATE użyciu AFTER wyzwalaczy. Przy każdej nowej wersji danych w oryginale, wstaw kopię nowych danych do tabeli zmian i odpowiednio ustaw modyfikację type.

Aby zalogować się do wersji DELETE pod kątem bezpieczeństwa, należy również wstawić nowy wiersz do tabeli historii! W tym celu należy użyć wyzwalacza BEFORE DELETE i zapisać najnowsze wartości, zanim zostaną usunięte. W przeciwnym razie będziesz musiał usunąć każde ograniczenie NOT NULL w tabeli historii.

kilka ważnych wskazówek dotyczących tego wdrożenia

  • dla tabeli historii należy usunąć każdy UNIQUE KEY (tu: PRIMARY KEY) z tabeli rewizyjnej bo masz ten sam klucz kilka razy dla każdej rewizji danych .
  • Po utworzeniu schematu i danych w oryginalnej tabeli za pomocą aktualizacji (na przykład aktualizacji oprogramowania) należy upewnić się, że te same dane lub poprawki schematu zostaną zastosowane do tabeli historii i jej danych. W przeciwnym razie wystąpią problemy, gdy powrócisz do starszej wersji zestawu rekordów.
  • W rzeczywistej implementacji chcesz wiedzieć, który użytkownik zmodyfikował dane. Aby było to bezpieczne pod względem integralnym, rekord użytkownika nie powinien nigdy zostać usunięty z tabeli użytkowników. Powinieneś po prostu ustawić konto wyłączone z flagą.
  • Zwykle działanie pojedynczego użytkownika obejmuje więcej niż jedną tabelę. W rzeczywistej implementacji trzeba również śledzić, które zmiany w wielu tabelach należą do transakcji jednego użytkownika, a także w jakiej kolejności. W prawdziwym przypadku należy odwrócić wszystkie zmiany pojedynczej transakcji, w odwrotnej kolejności. Wymagałoby to dodatkowej tabeli poprawek, która śledzi użytkowników i transakcje i zawiera luźną relację do wszystkich poszczególnych wersji w tabelach historii.

Korzyści:

  • całkowicie w bazie danych, niezależnie od kodu aplikacji. (cóż, nie w przypadku, gdy śledzenie transakcji użytkownika jest ważne, wymagałoby to logiki wykraczającej poza zakres pojedynczego zapytania):
  • wszystkie dane są w oryginalnym formacie, bez konwersji typu niejawnego.
  • dobra wydajność wyszukiwania w wersjach
  • łatwe wycofywanie. Po prostu wykonaj prostą instrukcję INSERT .. ON DUPLICATE KEY UPDATE .. w oryginalnej tabeli, używając danych z wersji, którą chcesz przywrócić.

wydarzenia:

  • Trudno realizować ręcznie.
  • twardy (ale nie niemożliwe) do automatyzacji, jeśli chodzi o migracje baz danych/aktualizacji aplikacji.

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


Wariant B: mają Centralna Tabela Zmian

przedmowę: złe praktyki, pokazany na ilustracji tylko pomocniczo.

Takie podejście ma mocno polegać na logice aplikacji, które powinny być ukryte w warstwie danych/model.

Masz Centralna Tabela historii, która śledzi na

  • zrobił
  • gdy
  • modyfikować, wstawić lub usunąć
  • jakie dane
  • , w którym pole które
  • stół

Podobnie jak w przypadku innych metod, możesz również chcieć śledzić, które indywidualne zmiany danych należą do czynności/transakcji jednego użytkownika i w jakiej kolejności.

Korzyści:

  • Nie trzeba zachować w synchronizacji z oryginalnym stole podczas dodawania pól do tabeli lub tworzenie nowej tabeli. skaluje się w sposób przezroczysty.

wydarzenia:

  • złych praktyk za pomocą prostego wartość = klucz przechowywać w bazie
  • złe wyniki wyszukiwania, z powodu ukrytych konwersji typu
  • może spowolnienie ogólną wydajność aplikacji/bazy danych, gdy centralna tabela historia staje się wąskim gardłem powodu blokad zapisu (dotyczy to tylko dla konkretnych silników z zamkami stołowych, czyli MyISAM)
  • Jest to znacznie trudniejsze do wdrożenia cofanie
  • możliwych błędów konwersji danych/straty precyzji ze względu na niejawna konwersja typu
  • nie śledzić zmiany, gdy bezpośredni dostęp do bazy danych gdzieś w kodzie zamiast przy użyciu modelu/warstwy danych i zapomnij, że w tym przypadku musisz ręcznie zapisać w dzienniku wersji. Może być dużym problemem podczas pracy w zespole z innymi programistami.

Wniosek:

  • Wariant B może być bardzo przydatny dla małych aplikacjach jak prostego "drop in", gdy jej tylko do rejestrowania zmian.
  • Jeśli chcesz cofnąć się w czasie i być w stanie łatwo porównać różnice między historycznym revison rewizji i/lub powrócić do starych danych, a następnie Wariant A jest trudne do zrobienia .
+1

Zasługa 1: To nie jest para wartości klucza. Merit 2: Możliwe, ale dzięki znacznikom czasu, object_ids i user_id możliwe byłoby śledzenie, kto działał na który obiekt. Zasada 3: Wycofanie powinno być zawarte w transakcjach, jeśli zmiana nie została zatwierdzona, a następnie brak wpisu w tabeli dziennika. Zasada 4: Odnoszą się do odpowiedzi merit 2. Zasługa 5: Jaka jest różnica, jeśli chodzi o model lub proste połączenie z aplikacji? Jeśli zostanie wprowadzona zmiana, może wywołać klasę TransactionLoggable z dowolnego miejsca, najlepiej jednak pozostawić ją w modelu, ale nie ograniczać się do niej. –

+0

@MikePurcell 1. jest. key = object_id, value = cokolwiek przechowujesz w pojedynczym polu danych 3. "rollback" odnosi się do ręcznego zwijania twoich danych do starszego revison. Podobnie jak wikipedia. nie mówię o zatwierdzaniu/wycofywaniu transakcji bazy danych. 5. nie powiedział, że nie jest niemożliwe przechowywanie danych historii, gdy programista chce. chciałem tylko powiedzieć, że możesz łatwo zapomnieć, że musisz również ręcznie zapisywać dane historii. Bardziej przydaje się, aby baza danych obsługiwała wersje w pełni przejrzyste. – Kaii

+0

@MikePurcell ogólnie * Opcja B * może być w porządku, gdy zmienia się tylko jej * rejestrowanie *. jeśli chcesz system, w którym możesz łatwo porównać jedną wersję do drugiej (niekoniecznie aktualną), wówczas * Opcja B * nie jest dobrze dopasowana. – Kaii

2

Co przy użyciu ogólnej tabeli aktualizacji uni. Pola tabeli powinien posiadać następujące wartości:

user,event,date,table,field,new value

  • użytkownika - który dokonał zmiany
  • wydarzenie - jako kod predefiniowanych zdarzeń (aktualizacja, Zapisz, Wstaw)
  • datę - gdy zmiana została dokonana
  • stół & pola - mogą być zlokalizowane auto z globalnym zapytania
  • wartość - wartość włożona

Wartości i wstawki można utworzyć za pomocą funkcji z ogólnego zapytania.