Jak śledzić relacje rekordów w NoSQL?


118

Próbuję znaleźć odpowiednik kluczy obcych i indeksów w bazach danych NoSQL KVP lub Document. Ponieważ nie ma tabel przestawnych (do dodawania kluczy oznaczających relację między dwoma obiektami), jestem naprawdę zaskoczony, w jaki sposób można byłoby odzyskać dane w sposób, który byłby przydatny dla normalnych stron internetowych.

Powiedzmy, że mam użytkownika, który zostawia wiele komentarzy w całej witrynie. Jedynym sposobem, w jaki mogę wymyślić, aby śledzić komentarze użytkowników, jest

  1. Osadź je w obiekcie użytkownika (co wydaje się całkiem bezużyteczne)
  2. Utwórz i utrzymuj user_id:commentswartość zawierającą listę kluczy każdego komentarza [komentarz: 34, komentarz: 197, itd.], Abym mógł je pobrać w razie potrzeby.

Jednak biorąc pod uwagę drugi przykład, wkrótce trafisz na mur z cegły, gdy użyjesz go do śledzenia innych rzeczy, takich jak klucz o nazwie „active_comments”, który może zawierać 30 milionów identyfikatorów, co sprawia, że zapytanie każdej strony kosztuje TONĘ aktywne komentarze. Byłby również bardzo podatny na warunki wyścigowe, ponieważ wiele stron może próbować aktualizować go w tym samym czasie.

Jak mogę śledzić relacje, takie jak poniższe, w bazie danych NoSQL?

  • Wszystkie komentarze użytkownika
  • Wszystkie aktywne komentarze
  • Wszystkie posty oznaczone [słowo kluczowe]
  • Wszyscy uczniowie w klubie - lub wszystkie kluby, do których należy uczeń

A może źle o tym myślę?


Nie ma jednego sposobu, aby to zrobić w bazach danych NoSQL, to pytanie jest raczej spokrewnione z pytaniem, jak mam śledzić relacje w programach C.
kamień kamieniarski

3
Wow, myślę, że szum na temat zastąpienia RDBMS przez NoSQL jest niemożliwy.
Xeoncross

11
Tak, NoSQL jest zdecydowanie przesadzony. Nie twierdzę, że nowe technologie nie są przydatne w odpowiednich okolicznościach, ale myślenie, że zastąpią RDBMS, jest śmieszne. Zobacz en.wikipedia.org/wiki/Hype_cycle
Bill Karwin,

1
Czy nie miałbyś po prostu zbioru „użytkowników” i zbioru komentarzy. A potem każdy komentarz jako właściwość „autor”, której wartością jest odwołanie z powrotem do identyfikatora użytkownika?
CodeFinity,

Odpowiedzi:


187

Wszystkie odpowiedzi na pytanie, jak przechowywać asocjacje „wiele do wielu” w sposób „NoSQL” sprowadzają się do tego samego: nadmiarowego przechowywania danych.

W NoSQL nie projektujesz bazy danych na podstawie relacji między jednostkami danych. Projektujesz swoją bazę danych na podstawie zapytań, które będziesz wykonywać względem niej. Użyj tych samych kryteriów, których użyłbyś do denormalizacji relacyjnej bazy danych: jeśli ważniejsze jest, aby dane były spójne (pomyśl o wartościach na liście oddzielonej przecinkami zamiast o znormalizowanej tabeli), zrób to w ten sposób.

Ale to nieuchronnie optymalizuje się dla jednego rodzaju zapytań (np. Komentarze dowolnego użytkownika do danego artykułu) kosztem innych typów zapytań (komentarze do dowolnego artykułu danego użytkownika). Jeśli Twoja aplikacja wymaga jednakowej optymalizacji obu typów zapytań, nie powinieneś denormalizować. Podobnie, nie powinieneś używać rozwiązania NoSQL, jeśli chcesz używać danych w sposób relacyjny.

Istnieje ryzyko, że w przypadku denormalizacji i nadmiarowości zbędne zestawy danych nie będą ze sobą zsynchronizowane. Nazywa się to anomalią . Kiedy używasz znormalizowanej relacyjnej bazy danych, RDBMS może zapobiegać anomaliom. W zdenormalizowanej bazie danych lub w NoSQL odpowiedzialność za pisanie kodu aplikacji w celu zapobiegania anomaliom spoczywa na użytkowniku.

Można by pomyśleć, że byłoby wspaniale, gdyby baza danych NoSQL wykonała ciężką pracę polegającą na zapobieganiu anomaliom za Ciebie. Jest paradygmat, który może to zrobić - paradygmat relacyjny.


20
„nie powinieneś używać rozwiązania NoSQL, jeśli chcesz używać danych w sposób relacyjny” - jak więc inni używający NoSQL mogą sobie z tym poradzić? Jak możesz poznać wszystkie sposoby wykonywania zapytań dotyczących danych podczas pierwszego projektowania aplikacji? Na przykład Fox, mogę chcieć ostatnich komentarzy, komentarzy użytkownika, komentarzy według tagów, komentarzy do danego posta, komentarzy oznaczonych jako spam, aktywnych komentarzy, najwyżej ocenianych komentarzy itp.
Xeoncross

14
Dokładnie - nie ma czegoś takiego jak „to po prostu działa”, jak lubią twierdzić zwolennicy NoSQL. Albo wykonujesz kilka analiz z góry w celu modelowania danych relacyjnych, albo wykonujesz kilka analiz z góry dla zapytań o najwyższym priorytecie, albo wykonujesz wiele kosztownych refaktoryzacji w całym projekcie, odkrywając, które części projektu nie dostał wystarczającej analizy z góry
Bill Karwin,

1
Jeśli przechowujemy dane w sposób nadmiarowy, jak powinniśmy aktualizować? Na przykład zmienia swoje imię i napisał kilka komentarzy. Jego nazwa jest już zmieniona w kolekcji użytkownika, ale jak zmienić wszystkie nadmiarowo przechowywane nazwy w kolekcji komentarzy?
Mohammad Kermani

3
@ M98, Ach, znalazłeś słabość w tej strategii. Musisz wiedzieć o wszystkich miejscach, które musisz zaktualizować, a następnie napisać kod w swojej aplikacji, aby zaktualizować je wszystkie, gdy aktualizujesz którekolwiek. Powodzenia!
Bill Karwin

2
Ten sam problem występuje w przypadku zdenormalizowanej relacyjnej bazy danych.
Bill Karwin

5

Podejście couchDB sugeruje, aby emitować odpowiednie klasy rzeczy w fazie mapy i podsumowywać je w reduktorze. Tak więc możesz zmapować wszystkie komentarze i emitować 1dla danego użytkownika, a później wydrukować tylko te. Wymagałoby to jednak dużej ilości miejsca na dysku, aby zbudować trwałe widoki wszystkich danych możliwych do śledzenia w CouchDB. btw mają również tę stronę wiki o związkach: http://wiki.apache.org/couchdb/EntityRelationship .

Z drugiej strony Riak ma narzędzie do budowania relacji. To jest link. Możesz wprowadzić adres powiązanego (tutaj komentarza) dokumentu do dokumentu „root” (tutaj dokument użytkownika). Ma jedną sztuczkę. Jeśli jest rozpowszechniany, może być modyfikowany jednorazowo w wielu lokalizacjach. Spowoduje to konflikty iw rezultacie ogromne drzewo zegara wektorowego: / ..nie tak źle, nie tak dobrze.

Riak ma też jeszcze jeden „mechanizm”. Posiada 2-warstwową przestrzeń nazw kluczy, tzw. Bucket and key. Na przykład, jeśli mamy klub A, B i C oraz studenta StudentX, StudentY, możesz zachować następującą konwencję:

{ Key = {ClubA, StudentX}, Value = true }, 
{ Key = {ClubB, StudentX}, Value = true }, 
{ Key = {ClubA, StudentY}, Value = true }

i aby odczytać relację, po prostu wypisz klucze w podanych zasobnikach. Co z tym jest nie tak? Jest cholernie wolno. Rzemiosło nigdy nie było priorytetem. Mimo to jest coraz lepiej. przy okazji. nie marnujesz pamięci, ponieważ ten przykład {true}może być powiązany z jednym pełnym profilem Studenta X lub Y (tutaj konflikty nie są możliwe).

Jak widzisz NoSQL! = NoSQL. Musisz przyjrzeć się konkretnej implementacji i samodzielnie ją przetestować.

Wspomniane wcześniej magazyny kolumnowe wyglądają na dobrze dopasowane do relacji… ale wszystko zależy od Twoich potrzeb A, C i P;) Jeśli nie potrzebujesz A i masz mniej niż Peta bajtów, po prostu zostaw to, śmiało z MySql lub Postgres.

powodzenia


1
Riak niedawno wydał wersję 1.0, która dodaje obsługę indeksów pomocniczych podczas korzystania z zaplecza LevelDB. Bardzo cenna funkcja.
Jon L.,

4
  1. user: userid: comments jest rozsądnym podejściem - potraktuj to jako odpowiednik indeksu kolumny w SQL, z dodatkowym wymogiem, że nie możesz wykonywać zapytań dotyczących niezindeksowanych kolumn.

  2. Tutaj musisz pomyśleć o swoich wymaganiach. Lista zawierająca 30 milionów pozycji nie jest nierozsądna, ponieważ jest powolna, ale ponieważ jest niepraktyczne, aby cokolwiek z nią zrobić. Jeśli Twoim prawdziwym wymaganiem jest wyświetlenie kilku ostatnich komentarzy, lepiej przechowywać bardzo krótką listę, która jest aktualizowana za każdym razem, gdy dodawany jest komentarz - pamiętaj, że NoSQL nie ma wymagań normalizacji. Warunki wyścigu są problemem w przypadku list w podstawowym magazynie wartości kluczy, ale generalnie albo twoja platforma prawidłowo obsługuje listy, możesz zrobić coś z blokadami, albo tak naprawdę nie przejmujesz się nieudanymi aktualizacjami.

  3. Tak samo jak w przypadku komentarzy użytkowników - utwórz indeksowe słowo kluczowe: posty

  4. Więcej tego samego - prawdopodobnie lista klubów jako własność studenta i indeks na tym polu, aby uzyskać wszystkich członków klubu


Więc w zasadzie wszystko potrzebuje tylko list? Wydaje się, że powinno być bardziej wyrafinowane podejście niż tylko ręczne śledzenie ciągów identyfikatorów. Po pierwsze, możesz zajść tylko tak daleko, zanim osiągną duże rozmiary, aby były przydatne. Z drugiej strony, wszystkie główne projekty potomne technologii NoSQL (MongoDB, CouchDB, Membase itp.) Są nowymi projektami, więc może po prostu muszę dać im więcej czasu na wymyślenie lepszego sposobu śledzenia relacji.
Xeoncross

Jeśli używasz NoSQL (nierelacyjnych magazynów danych AKA), musisz przestać myśleć w kategoriach relacyjnych. Zastosowane podejście będzie się różnić w zależności od platformy, ale podstawowa idea zarządzania indeksami jest dość uniwersalna. Podane przykłady relacji są modelowane na dwa różne sposoby w NoSQL: 1) Pamięć masowa - w przeciwieństwie do SQL, kolumny mogą mieć wiele / złożonych wartości, więc obiekt potomny jest tylko częścią obiektu nadrzędnego. 2) Wyszukiwanie - Twoje długie listy są w rzeczywistości warunkiem możliwości wyszukiwania, co oznacza indeksowanie - możesz użyć prostej listy niestandardowej lub bardziej kompletnej wyszukiwarki.
Tom Clarkson

2

Ty masz

"user": {
    "userid": "unique value",
    "category": "student",
    "metainfo": "yada yada yada",
    "clubs": ["archery", "kendo"]
}

"comments": {
    "commentid": "unique value",
    "pageid": "unique value",
    "post-time": "ISO Date",
    "userid": "OP id -> THIS IS IMPORTANT"
}

"page": {
    "pageid": "unique value",
    "post-time": "ISO Date",
    "op-id": "user id",
    "tag": ["abc", "zxcv", "qwer"]
}

Cóż, w relacyjnej bazie danych normalną rzeczą do zrobienia w relacji jeden do wielu jest normalizacja danych. To jest to samo, co zrobiłbyś w bazie danych NoSQL. Po prostu zindeksuj pola, z których będziesz pobierać informacje.

Na przykład ważne dla Ciebie indeksy to

  • Komentarz.UserID
  • Comment.PageID
  • Comment.PostTime
  • Strona.Tag []

Jeśli korzystasz z NosDB (bazy danych NoSQL opartej na platformie .NET z obsługą SQL), Twoje zapytania będą wyglądać następująco

 SELECT * FROM Comments WHERE userid = That user’;

 SELECT * FROM Comments WHERE pageid = That user’;

 SELECT * FROM Comments WHERE post-time > DateTime('2016, 1, 1');

 SELECT * FROM Page WHERE tag = 'kendo'

Sprawdź wszystkie obsługiwane typy zapytań w ich ściągach SQL lub w dokumentacji.

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.