Sprzyjanie niezmienności w projektowaniu baz danych

26

Jednym z elementów efektywnej Javy Joshua Blocha jest przekonanie, że klasy powinny pozwalać na mutację instancji jak najmniej, a najlepiej wcale.

Często dane obiektu są utrwalane w bazie danych jakiejkolwiek formy. Doprowadziło mnie to do myślenia o idei niezmienności w bazie danych, szczególnie dla tych tabel, które reprezentują pojedynczy byt w większym systemie.

Coś, z czym ostatnio eksperymentowałem, to pomysł zminimalizowania aktualizacji, które robię, do tabel wierszy reprezentujących te obiekty i próbowania zamiast tego wstawiania tak dużo, jak to możliwe.

Konkretny przykład czegoś, z czym ostatnio eksperymentowałem. Jeśli wiem, że później mogę dołączyć rekord z dodatkowymi danymi, utworzę kolejną tabelę, która to reprezentuje, podobnie jak dwie następujące definicje tabeli:

create table myObj (id integer, ...other_data... not null);
create table myObjSuppliment (id integer, myObjId integer, ...more_data... not null);

Miejmy nadzieję, że te nazwy nie są dosłowne, a jedynie w celu zademonstrowania pomysłu.

Czy jest to rozsądne podejście do modelowania trwałości danych? Czy warto próbować ograniczać aktualizacje wykonywane w tabeli, szczególnie w przypadku wypełniania wartości zerowych dla danych, które mogą nie istnieć podczas tworzenia rekordu? Czy zdarza się, że takie podejście może później spowodować silny ból?

Ed Carrel
źródło
7
Wydaje mi się, że jest to rozwiązanie bez problemu ... Powinieneś aktualizować, zamiast tworzyć rozbudowane dostosowania, aby uniknąć aktualizacji.
Fosco,
Myślę, że bardziej chodziło o to, żeby mieć na myśli intuicyjne rozwiązanie i chcieć uruchomić je jak największej liczby osób, a jednocześnie uświadomić sobie, że może to nie być najlepsze rozwiązanie mojego problemu. Mogę otworzyć inne pytanie dotyczące problemu, pod warunkiem, że nie znajdę go w innym miejscu.
Ed Carrel,
1
Mogą istnieć dobre powody, aby unikać aktualizacji baz danych. Jednak kiedy pojawiają się te przyczyny, jest to bardziej problem z optymalizacją i jako taki nie powinien być wykonywany bez dowodu na istnienie problemu.
dietbuddha,
6
Myślę, że istnieje silny argument za niezmiennością baz danych. Rozwiązuje wiele problemów. Myślę, że negatywne komentarze nie pochodzą od ludzi o otwartych umysłach. Aktualizacje w miejscu są przyczyną wielu problemów. Twierdziłbym, że mamy to wszystko do tyłu. Aktualizacje na miejscu to starsze rozwiązanie problemu, który już nie istnieje. Przechowywanie jest tanie. Dlaczego to robisz Ile systemów DB ma dzienniki kontroli, systemy kontroli wersji, potrzebuje rozproszonej replikacji, która jak wszyscy wiemy wymaga zdolności do obsługi opóźnień skalowania. Niezmienność rozwiązuje to wszystko.
cirrus
@Fosco Niektóre systemy są absolutnie wymagane, aby nigdy nie usuwać danych (w tym za pomocą UPDATE). Jak dokumentacja medyczna lekarza.
Izkata

Odpowiedzi:

25

Podstawowym celem niezmienności jest zapewnienie, że nie ma chwili, gdy dane w pamięci są w nieprawidłowym stanie. (Drugim jest fakt, że notacje matematyczne są w większości statyczne, a zatem niezmienne rzeczy są łatwiejsze do konceptualizacji i modelowania matematycznego). W pamięci, jeśli inny wątek próbuje czytać lub zapisywać dane podczas pracy, może to doprowadzić do uszkodzenia lub sam może być w skorumpowanym stanie. Jeśli masz wiele operacji przypisywania do pól obiektu, w aplikacji wielowątkowej inny wątek może próbować z nim pracować gdzieś pomiędzy - co może być złe.

Niezmienność rozwiązuje ten problem, pisząc najpierw wszystkie zmiany w nowym miejscu w pamięci, a następnie wykonując ostateczne zadanie w ramach jednego kroku polegającego na przepisaniu wskaźnika do obiektu w celu wskazania nowego obiektu - który na wszystkich procesorach jest atomowy operacja.

Bazy danych robią to samo przy użyciu transakcji atomowych : po uruchomieniu transakcji zapisuje wszystkie nowe aktualizacje w nowym miejscu na dysku. Po zakończeniu transakcji zmienia wskaźnik na dysku w miejsce, w którym znajdują się nowe aktualizacje - co robi w krótkim czasie, podczas którego inne procesy nie mogą go dotknąć.

Jest to również dokładnie to samo, co twój pomysł tworzenia nowych tabel, z wyjątkiem bardziej automatycznych i bardziej elastycznych.

Tak więc, aby odpowiedzieć na twoje pytanie, tak, niezmienność jest dobra w bazach danych, ale nie, nie musisz tworzyć osobnych tabel tylko w tym celu; możesz po prostu użyć dowolnych poleceń transakcji atomowych dostępnych dla systemu bazy danych.

Rei Miyasaka
źródło
Dziękuję za odpowiedź. Właśnie z tej perspektywy musiałem zdać sobie sprawę, że moja intuicja myląco próbuje połączyć kilka różnych pomysłów w jeden wzór.
Ed Carrel,
8
Jest w tym coś więcej niż atmosfera. Argument, który widzę najczęściej na rzecz niezmienności w kontekście OOP, polega na tym, że obiekty niezmienne wymagają tylko potwierdzenia ich stanu tylko raz w konstruktorze. Jeśli są zmienne, każda metoda, która może zmienić ich stan, jest również wymagana do sprawdzenia, czy stan wynikowy jest nadal poprawny, co może znacznie zwiększyć złożoność klasy. Ten argument potencjalnie dotyczy również baz danych, ale jest znacznie słabszy, ponieważ reguły sprawdzania poprawności bazy danych są raczej deklaratywne niż proceduralne, więc nie trzeba ich duplikować dla każdego zapytania.
Dave Sherohman,
24

Zależy to od korzyści, jakich oczekujesz od niezmienności. Odpowiedź Rei Miyasaka dotyczyła jednego (unikanie nieprawidłowych stanów pośrednich), ale tutaj jest inny.

Mutacja jest czasem nazywana aktualizacją destrukcyjną : kiedy mutujesz obiekt, stary stan jest tracony (chyba że wykonasz dodatkowe kroki, aby go w jakiś sposób jawnie zachować). Natomiast w przypadku niezmiennych danych banalne jest jednoczesne reprezentowanie stanu zarówno przed jak i po pewnej operacji, lub reprezentowanie wielu stanów następczych. Wyobraź sobie, że próbujesz zaimplementować wyszukiwanie z pełną szerokością, mutując pojedynczy obiekt stanu.

Prawdopodobnie pojawia się to w świecie baz danych jako dane tymczasowe . Powiedz, że w zeszłym miesiącu korzystałeś z planu podstawowego, ale 16 dnia przeszedłeś na plan premium. Jeśli po prostu nadpisaliśmy jakieś pole wskazujące plan, na którym jesteś, możemy mieć trudności z prawidłowym rozliczeniem. Możemy również stracić możliwość analizowania trendów. (Hej, zobacz, co zrobiła ta lokalna kampania reklamowa!)

To właśnie przychodzi mi do głowy, gdy mówisz „niezmienność w projektowaniu baz danych”.

Ryan Culpepper
źródło
2
Nie zgadzam się z twoim trzecim akapitem. Jeśli chcesz mieć historię (dziennik kontroli, dziennik zmian planu itp.), Musisz utworzyć osobną tabelę do tego. Duplikowanie wszystkich 50 pól Customertabeli, aby pamiętać, że użytkownik zmienił plan, nie przynosi nic oprócz ogromnej wady wydajności, wolniejszego wybierania w czasie, bardziej skomplikowanego eksploracji danych (w porównaniu do dzienników) i większej ilości zmarnowanego miejsca.
Arseni Mourzenko
6
@MainMa: być może powinienem po prostu powiedzieć „idź przeczytać o tymczasowych bazach danych”. Mój przykład miał służyć jako szkic danych czasowych; Nie twierdzę, że to zawsze najlepszy sposób na reprezentowanie zmieniających się danych. Z drugiej strony, podczas gdy obsługa danych czasowych jest obecnie dość słaba, spodziewam się raczej tendencji do dostosowywania danych czasowych w samej bazie danych, zamiast przenoszenia ich do reprezentacji „drugiej klasy”, takich jak dzienniki zmian.
Ryan Culpepper
Co się stanie, jeśli utrzymamy historię zmian w tabeli audytu (na przykład wiosenny rozruch i hibernacja, na przykład dzięki tej funkcji)?
Mohammad Najar
14

Jeśli interesują Cię korzyści, jakie możesz uzyskać z niezmienności w bazie danych lub przynajmniej w bazie danych, która oferuje iluzję niezmienności, sprawdź Datomic.

Datomic to baza danych opracowana przez Richa Hickeya we współpracy z Think Relevance. Istnieje wiele filmów, w których wyjaśniają architekturę, cele i model danych. Wyszukaj infoq, w szczególności jeden zatytułowany Datomic, Baza danych jako wartość . W confreaks można znaleźć przemówienie Richa Hickeya na konferencji euroclojure w 2012 roku. Confreaks.com/videos/2077-euroclojure2012-day-2-keynote-the-datomic-architecture-and-data-model

W vimeo.com/53162418 jest dyskusja, która jest bardziej zorientowana na rozwój.

Oto kolejna od stuart halloway at.pscdn.net/008/00102/videoplatform/kv/121105techconf_close.html

  • Datomic to baza faktów w czasie, zwana układami odniesienia, w 5 krotkach [E, A, V, T, O]
    • E Identyfikator jednostki
    • Nazwa atrybutu w jednostce (może mieć nazw)
    • V Wartość atrybutu
    • T Identyfikator transakcji, dzięki czemu masz pojęcie o czasie.
    • O Jedna operacja potwierdzenia (wartość bieżąca lub bieżąca), odrzucenie (wartość z przeszłości);
  • Używa własnego formatu danych, zwanego EDN (Extensible Data Notation)
  • Transakcje są ACID
  • Używa danych jako języka zapytań, który jest deklaratywny jako zapytania rekurencyjne SQL +. Zapytania są reprezentowane przez struktury danych i rozszerzone w języku Jvm, nie musisz używać clojure.
  • Baza danych jest rozdzielona na 3 oddzielne usługi (procesy, maszyny):
    • Transakcja
    • Przechowywanie
    • Silnik zapytań.
  • Możesz osobno skalować każdą usługę.
  • To nie jest open source, ale jest darmowa (jak w piwie) wersja Datomic.
  • Możesz podać elastyczny schemat.
    • zestaw atrybutów jest otwarty
    • dodawaj nowe atrybuty w dowolnym momencie
    • brak sztywności definicji lub zapytania

Teraz, ponieważ informacje są przechowywane w czasie jako fakty:

  • wszystko, co robisz, to dodawanie faktów do bazy danych, nigdy ich nie usuwasz (z wyjątkiem przypadków wymaganych przez prawo)
  • możesz buforować wszystko na zawsze. Mechanizm zapytań działa na serwerze aplikacji jako baza danych w pamięci (w przypadku języków JVM języki inne niż JVM mają dostęp przez interfejs API REST).
  • możesz zapytać o czas w przeszłości.

Baza danych jest wartością i parametrem silnika zapytań, QE zarządza połączeniem i buforowaniem. Ponieważ możesz zobaczyć db jako wartość i niezmienną strukturę danych w pamięci, możesz połączyć ją z inną strukturą danych wykonaną z wartości „w przyszłości” i przekazać ją do QE i zapytania o przyszłe wartości, bez zmiany faktycznej bazy danych .

Rich Hickey ma projekt open source, o nazwie codeq , można go znaleźć w github Datomic / codeq, który rozszerza model git i przechowuje odniesienia do obiektów git w bazie danych wolnej od danych i tworzy zapytania dotyczące twojego kodu, widzi przykład użycia datomiki.

Możesz myśleć o datomice jako o ACID NoSQL, z bazami danych możesz modelować tabele lub dokumenty, sklepy Kv lub wykresy.

kisai
źródło
7

Pomysł unikania aktualizacji i preferowania wstawek jest jedną z myśli stojących za budowaniem magazynu danych jako źródła zdarzeń, pomysłem, który często znajdziesz w połączeniu z CQRS. W modelu źródłowym zdarzenia nie ma aktualizacji: agregat jest reprezentowany jako sekwencja jego „transformacji” (zdarzeń), w wyniku czego pamięć jest dostępna tylko z dopiskiem.
Ta strona zawiera ciekawe dyskusje na temat CQRS i pozyskiwania wydarzeń, jeśli jesteś tego ciekawy!

Mathias
źródło
CQRS i pozyskiwanie zdarzeń stają się obecnie najważniejsze.
Gulshan,
6

Ma to bardzo ścisły związek z tak zwanymi „powoli zmieniającymi się wymiarami” w świecie hurtowni danych, a tabelami „czasowymi” lub „dwuwymiarowymi” w innych domenach.

Podstawowa konstrukcja to:

  1. Zawsze używaj wygenerowanego klucza zastępczego jako klucza podstawowego.
  2. Unikalny identyfikator tego, co opisujesz, staje się „kluczem logicznym”.
  3. Każdy wiersz powinien mieć co najmniej znacznik czasu „ValidFrom” i opcjonalnie znacznik czasu „ValidTo”, a jeszcze bardziej opcjonalnie flagę „Najnowsza wersja”.
  4. Przy „tworzeniu” logicznej jednostki wstawiasz nowy wiersz z „Valid From” bieżącego znacznika czasu. Opcjonalne ValidTo ustawione na „na zawsze” (9999-12-31 23:59:59), a ostatnia wersja na „True”.
  5. Po kolejnej aktualizacji jednostki logicznej. Musisz przynajmniej wstawić nowy wiersz jak wyżej. Może być również konieczne dostosowanie ValidTo w poprzedniej wersji do „now () - 1 sekunda”, a najnowszej wersji do „False”
    1. Po logicznym usunięciu (działa tylko ze znacznikiem czasu ValidTo!) Ustawiasz flagę ValidTo w bieżącym wierszu na „teraz () -1 sekundę”).

Zaletą tego schematu jest to, że można odtworzyć „stan” logicznej jednostki w dowolnym momencie, masz historię swojej jednostki w czasie i minimalizujesz rywalizację, jeśli twoja „logiczna jednostka” jest intensywnie używana.

Wady polegają na tym, że przechowujesz o wiele więcej danych i potrzebujesz więcej indeksów (przynajmniej na kluczu logicznym + ValidFrom + ValidTo). Indeks klucza logicznego + najnowsza wersja znacznie przyspiesza większość zapytań. To także komplikuje twój SQL!

To, czy warto to zrobić, chyba że naprawdę musisz prowadzić historię i mieć obowiązek odtworzenia stanu swoich bytów w danym momencie, zależy od ciebie.

James Anderson
źródło
1

Innym możliwym powodem istnienia niezmiennej bazy danych byłoby wsparcie lepszego przetwarzania równoległego. Aktualizacje zdarzające się poza kolejnością mogą trwale zepsuć dane, dlatego musi wystąpić blokowanie, które zniszczy wydajność równoległą. Wiele wstawek zdarzeń może iść w dowolnej kolejności, a stan przynajmniej będzie miał rację, o ile wszystkie zdarzenia zostaną ostatecznie przetworzone. Jest to jednak tak trudne w praktyce w porównaniu do robienia aktualizacji bazy danych, że trzeba by naprawdę potrzebować dużo równoległości, aby rozważyć robienie tego w ten sposób - nie polecam tego.

psr
źródło
0

Oświadczenie: Jestem prawie nowy w DB: p

To powiedziawszy, takie podejście do satelitowania danych ma bezpośredni wpływ na wydajność:

  • Dobry mniejszy ruch na głównym stole
  • Dobre mniejsze rzędy na głównym stole
  • Złe wymaganie danych satelitarnych oznacza, że ​​konieczne jest ponowne sprawdzenie
  • Złe miejsce zajmuje więcej, jeśli wszystkie obiekty istnieją w obu tabelach

w zależności od twoich wymagań możesz to zaakceptować lub nie, ale z pewnością warto to rozważyć.

Matthieu M.
źródło
-1

Nie rozumiem, jak można nazwać twój plan „niezmiennym”.

Co się stanie, gdy zmieni się wartość przechowywana w tabeli dodatkowej? Wygląda na to, że musisz wykonać aktualizację na tym stole.

Aby baza danych była naprawdę niezmienna, musiałaby być utrzymywana wyłącznie przez „INSERTS”. W tym celu potrzebujesz metody identyfikacji „bieżącego” wiersza. To prawie zawsze kończy się okropnie nieefektywnie. Musisz albo skopiować wszystkie poprzednie niezmienione wartości, albo połączyć aktualny stan z kilku rekordów podczas zapytania. Wybór bieżącego wiersza zwykle wymaga okropnie niechlujnego kodu SQL, takiego jak ( where updTime = (SELECT max(updTime) from myTab where id = ?).

Ten problem pojawia się często w DataWarehousing, gdzie trzeba przechowywać historię danych w czasie i mieć możliwość wyboru stanu dla dowolnego punktu w czasie. Rozwiązaniem są zwykle tabele „wymiarowe”. Jednak podczas rozwiązywania problemu DW, który był przedstawicielem handlowym w styczniu ubiegłego roku. Nie zapewniają one żadnych korzyści, jakie mają niezmienne klasy Java.

Na bardziej filozoficznej nucie; istnieją bazy danych do przechowywania „stanu” (saldo bankowe, zużycie energii elektrycznej, punkty brownie na StackOverflow itp.). Próbowanie stworzenia „bezstanowej” bazy danych wydaje się raczej bezcelowe.

James Anderson
źródło
W przypadku pojedynczego rekordu WHERE id = {} ORDER BY updTime DESC LIMIT 1generalnie nie jest zbyt nieefektywny.
Izkata
@Izkata - spróbuj umieścić na środku trzy-stolikowego stolika :-)
James Anderson