Kto używa NoSQL?

0

Zainspirowany komentarzem @somekind chciałbym zapytać, czy ktoś tu na forum, poza @Aventus, używa produkcyjnie lub prywatnie baz NoSQL? Pomijam tutaj bazy typu Redis, Elastic. Raczej chodzi mi o jakieś Mongo czy Raven. Jaki projekt? Jakie dane te bazy przechowują? Dlaczego wybór nie padł na SQL?

0

Ja przez całą swoją karierę pracuję z NoSQL'ami głównie z stajni Google'a:

  • Datastore 1st gen, Firestore real time + datastore mode
  • Firebase real time

Wszystkie dane aplikacyjne są tam trzymane, sesyjne, relacyjne, nie relacyjne, statyczne, implementacje drzew i ich przeszukiwania.
Dla małych części systemu jak CRUD czy części, gdzie jest główny ruch aplikacji.

Głównie ze względu na prostotę użycia, niezawodność, skalowalność, brak migracji, łatwość w blue/green i canary deployments przy bardzo zmiennej schemie, prototypowaniu.

0

Używałem:
Przechowywanie danych kartograficznych.
System do ustawiania spotkań pomiędzy użytkownikami - miał znaleźć terminy z najwyższym prawdopodobieństwem znalezienia terminu dogodnego dla wszystkich (ML)

Będę używał:
System do przechowywania informacji o usterkach i wykonanych naprawach statków powietrznych. (jak zespół pozwoli)

Dlaczego:
Bo mi kazali (1 przypadek, rozwiązanie powodujące mdłości...)
Bo nie potrzebowałem transakcyjności
Bo nie potrzebowałem liczyć agregatów
Bo wiedziałem co ma robić system, więc elastyczność RDBMS była mi niepotrzebna.
Bo jest taniej w chmurze i płaci się za faktyczne użycie, a nie za posiadanie serwera o jakichś tam parametrach
Bo w chmurze jest to usługa zarządzalna (w 100%)
Bo mapowanie obiektów na relacje i relacji na obiekty, żeby mieć po stronie aplikacji NoSQL, a po stronie bazy SQL jest bez sensu.

1

Korzystam produkcyjnie zarówno w pracy jak i w projektach pobocznych (zarówno komercyjne jak i prywatne aplikacje).

W pracy używamy Cosmos DB. Prywatnie używam RavenDB. Od wątku sprzed ponad roku nic się nie zmieniło, i nadal uważam że dokumentowe bazy NoSQL są najlepszym domyślnym wyborem. Nie będę się tutaj wdawał w szczegóły bo większość została już powiedziana w tamtym wątku który i tak wywołał burzliwą dyskusję, a nie chce mi się po raz kolejny prowadzić niekończące się debaty. Mogę tylko dodać po tym czasie że jeszcze bardziej utwierdziłem się w swoim przekonaniu, i jeśli zaczynałbym nowy projekt to musiałbym mieć dobre powody aby użyć baz relacyjnych, nie odwrotnie.

Bazy NoSQL są zwyczajnie wygodniejsze, i w pełni nadają się do większości przypadków jeśli się wie co się robi i potrafi się modelować swoją domenę. Tym zdaniem w zasadzie można podsumować czemu preferuję NoSQL (dokumentowe).

0

Używam MongoDB do danych które się nie zmieniają - czyli np. logi jakichś zdarzeń.

Wygodniej się te logi przechowuje / analizuje niż przy pomocy przechowywania w surowych plikach.

0

Używałem Datastax Cassandry produkcyjnie w poprzedniej firmie ~4 lata temu.

Dlaczego wybór nie padł na SQL?

Bo jeszcze zanim weszliśmy na produkcję mieliśmy ~5TB danych w jednym datacenter, jeśli mnie pamięć nie myli. Na starcie były dwa, albo trzy datacenter (już nie pamiętam dokładnie). Projekt dość specyficzny, do którego cały czas się dorzuca a usuwa przy bardzo rzadkich okazjach. W sumie sam jestem ciekaw do ilu TB im spuchło po latach.

0
Aventus napisał(a):

Bazy NoSQL są zwyczajnie wygodniejsze, i w pełni nadają się do większości przypadków jeśli się wie co się robi i potrafi się modelować swoją domenę. Tym zdaniem w zasadzie można podsumować czemu preferuję NoSQL (dokumentowe).

A jak obsługujesz jakąś transakcyjność na takim CosmosDB?
Bo tam można mieć transakcje tylko w ramach jednego kontenera i tej samej partycji.

Eventy + transactional outbox, czy coś innego?

Teoretycznie może dałoby się jakoś zamodelować domenę, żeby ograniczyć transakcje, ale weźmy prosty przykład - w aplikacji użytkownik może dokonać płatności i na jego konto zostaną dopisane "kredyty". Po potwierdzeniu płatności przez bramkę płatności musimy zmienić status płatności w systemie na "zakończona" i dopisać kredyty/zaktualizować plan użytkownika (whatever).

Można to rozwiązać własnie eventem z patternem "transactional outbox", czyli najpierw aktualizuję stan płatności i transakcyjnie publikuję event PaymentCompleted.
Następnie mam handler na ten event, który przypisuje użytkownikowi kredyty/odpowiedni plan. Ale tutaj w przypadku, gdy biznesowo to jest coś w stylu - kup pakiet za 100 PLN, a dostaniesz 100 kredytów do wykorzystania w aplikacji - powinniśmy zapewnić, że ta sama wiadomość nie zostanie obsłużona jeszcze raz (np. w sytuacji, gdy zapis do bazy się powiódł, ale nie udało się oznaczyć wiadomości w kolejce jako wykonaną).

1

@some_ONE: to temat rzeka, a w zasadzie wiele tematów w jednym poście zawarłeś. Pokrótce:

A jak obsługujesz jakąś transakcyjność na takim CosmosDB?

Ogólnie w przypadku baz dokumentowych mamy ten plus że można wrzucić jako rekord całą encję biznesową. I tutaj kłania się inny sposób modelowania i myślenia o takich obiektach, a z pomocą przychodzi idea agregatów znana z DDD. Czyli jakby cała "transakcja" ogranicza się do jednego dokumentu (agregatu). No ale ok, są przypadki gdzie faktycznie mamy system tak zamodelowany że jest potrzeba wrzucania wielu, być może niezależnych dokumentów. Jak sam już wspomniałeś taki Cosmos Ci tego nie zapewni jeśli wrzucasz to do różnych kontenerów albo pod różnymi partycjami, ale RavenDB już tak. Także to też kwestia dobrania odpowiedniej technologii NoSQL. Ale chcę podkreślić- przy odpowiednim modelowaniu transakcje na poziomie bazy nie są tak potrzebne jak się wydaje.

Zobacz sobie prezentację Modeling in a non-relational world w tym temacie.

Eventy + transactional outbox, czy coś innego?

To jest jedno z możliwych rozwiązań no ale to "waga ciężka" jeśli ktoś próbuje to zastosować tylko dla obejścia braku transakcji.Zwróć uwagę że przesunięcie tego na eventy nie sprawi magicznie że proces biznesowy nagle staje się transakcyjny. Co jeśli jakiś moduł zareaguje asynchronicznie na event i okaże się że operację trzeba cofnąć? Nagle trzeba to jakoś odpowiednio obsłużyć, np. jakąś sagą.

Jeśli chodzi o Twój przykład to też zależy. Jeśli używa się do całego procesu eventów to najbezpieczniejszym w tym przypadku byłoby zastosowanie event sorcingu- ale to nie jest dylemat natury stosowania baz NoSQL, a samego faktu stosowania eventów. Zresztą czym nie są systemy bankowe jak nie naturalnym event sourcingiem? :P Twój aktualny stan konta to nic innego jak suma wpłat i wypłat, czyli eventów jakie zaszły w przeszłości.

0
Aventus napisał(a):

Jeśli chodzi o Twój przykład to też zależy. Jeśli używa się do całego procesu eventów to najbezpieczniejszym w tym przypadku byłoby zastosowanie event sorcingu- ale to nie jest dylemat natury stosowania baz NoSQL, a samego faktu stosowania eventów. Zresztą czym nie są systemy bankowe jak nie naturalnym event sourcingiem? :P Twój aktualny stan konta to nic innego jak suma wpłat i wypłat, czyli eventów jakie zaszły w przeszłości.

No dobra, tylko piszesz, że bazy NoSQL według Ciebie powinny być domyślnym wyborem i zazwyczaj są najwygodniejsze.
Mój przykład należy raczej do prostszych i typowych w jakimś oprogramowaniu SaaS - czy to liczba maili, które użytkownik może wysłać, czy jakichś procesów które może uruchomić itp.

Obejrzę wystąpienie, które podesłałeś, ale nie wiem czy chciałbym to inaczej modelować. Mój przykład nie wydaje mi się dobry do pełnego embeddingu w ramach jednego dokumentu. Szczególnie, że liczba takich płatności dla pojedynczego użytkownika nie jest w żaden sposób ograniczona i dokument ciągle by rósł, a bazy mają swoje limity na rozmiar pojedynczego dokumentu.

W SQL bym założył transakcję, zaktualizował wpisy w 2 tabelach i nie zastanawiał się 3 dni jak to inaczej zamodelować.

Event sourcing? No spoko, ale czy warto się w niego pchać, tylko dlatego że w jednym miejscu systemu by rozwiązał jakiś problem spodowany brakiem transakcyjności bazy dokumentowej?

2

@some_ONE: Domyślny, nie znaczy jedyny słuszny. Jeżeli potrzebujesz transakcyjności, a czasami jest taki wymóg, to użycie bazy, która to obsługuje ma sens. Chyba, że spodziewasz się takiego ruchu, że trzeba sobie poradzić inaczej. Event sourcing nie załatwi wszystkiego. W systemie bankowym masz ten event sourcing wpisany w biznes (czyli stan twojego konta to różnica sum przychodów i rozchodów), to wciąż potrzebna jest transakcyjność w przypadku kiedy np. księgujesz jakąś transakcję kartą i musisz wiedzieć, czy stan konta pozwala na jej wykonanie. W przypadku SQL da się to zrobić prosto, ale kosztownie, czyli transakcja stable read na sprawdzenie salda i dodanie nowego wpisu w dzienniku.

Pytanie, czy warto się pchać w NoSQL bo przecież gdzieś może być potrzebna transakcyjność, można odwrócić - czy warto rezygnować ze skalowalności, wygody, wydajności, bo gdzieś kiedyś, być może, będzie potrzeba użyć w jednym miejscu transakcji?

0
piotrpo napisał(a):

Domyślny, nie znaczy jedyny słuszny. Jeżeli potrzebujesz transakcyjności, a czasami jest taki wymóg, to użycie bazy, która to obsługuje ma sens.

No to mój przykład to wystarczający powód do użycia bazy transakcyjnej czy jeszcze nie?
A jak nie to, jak byście taką sytuację obsłużyli w bazie dokumentowej.

czy warto rezygnować ze skalowalności, wygody, wydajności

Ale mając SQL wcale nie rezygnujesz ze skalowalności i wydajności :(
Nie problem na SQLu osiągnąć wyniki rzędu kilka tysięcy transakcji na sekundę. Później zawsze można dokładać read-repliki albo sharding. A myślę że w zdecydowanej większości aplikacji biznesowych te kilka tysięcy to liczba, który nigdy nie będzie stanowiła problemu.

1

@some_ONE:

Mój przykład należy raczej do prostszych i typowych w jakimś oprogramowaniu SaaS

W "prostych" i "typowych" bazach bardzo często tych transakcji nie trzeba mieć, wbrew temu co się wydaje. Powtarzam to w kółko ale jest tak dla tego że naprawdę trzeba zrozumieć że modelowanie baz dokumentowych (a raczej- modelowanie domeny która później jest po prostu "wrzucana" do bazy NoSQL) to inny sposób myślenia, i przerzucenie się na taki sposób myślenia całkowicie zmienia perspektywę. Rzeczy które wydają się niezbędne, a które znane są z baz relacyjnych, nagle okazują się potrzebne tylko w wyjątkowych przypadkach. Także te wątpliwości i przekonanie że "przecież muszę tu mieć transakcję" bierze się z ograniczonej perspektywy. Nie piszę tego złośliwie bo ja sam kilka lat temu zaczynałem wchodzić w świat NoSQL, i również nie potrafiłem sobie wyobrazić jak można mieć dane bez transakcji (chociaż jak wspomnę niżej, NoSQL != brak transakcji) czy szczególnie- bez referential integrity. To trochę jak z przejściem z kodu proceduralnego na OOP, czy z OOP na FP. Trzeba to przetestować na własnej skórze, i mieć dobre wzorce, żeby w pełni zrozumieć ale i nie popełnić głupot.

No dobra, tylko piszesz, że bazy NoSQL według Ciebie powinny być domyślnym wyborem

Tak jak napisał @piotrpo- "domyślny" to nie jedyny słuszny.

i zazwyczaj są najwygodniejsze.

Wygodniejsze pod względem użytkowania. Wyobraź sobie że chcesz zaczynać z NoSQL. Masz aplikację, postawiłeś bazę. Instalujesz w swojej aplikacji jakąś paczkę SDK dla tej bazy. W większości baz NoSQL jedyne co musisz zrobić aby zapisać obiekt to coś w tym stylu (z drobnymi szczegółami typu utworzenie kontenera itp):

var person = new Person();
var client = new MyDatabaseClient("conn string");
await client.Save(person);

Nie ma opcji żebyś tak szybko i wygodnie mógł to zrobić z jakąkolwiek bazą SQL, i z jakimkolwiek ORM.

Mój przykład należy raczej do prostszych i typowych w jakimś oprogramowaniu SaaS - czy to liczba maili, które użytkownik może wysłać, czy jakichś procesów które może uruchomić itp.

Nie rozumiem co masz na myśli przez wysłanie maili albo uruchomienie procesów. No ale nie wdawajmy się w szczegóły konkretnych scenariuszy. Wydajesz się popełniać podstawowy błąd myślowy- użyłeś Cosmos jako przykładu bazy która ma ograniczone transakcje, a sugerujesz jakby wszystkie bazy NoSQL takie były. NoSQL ma to do siebie że każda baza robi pewne rzeczy inaczej, i są bazy w pełni wspierające transakcje. Jeśli bardzo potrzebujesz transakcji, użyj bazy NoSQL z transakcjami, proste.

Szczególnie, że liczba takich płatności dla pojedynczego użytkownika nie jest w żaden sposób ograniczona i dokument ciągle by rósł

To nie chodzi o to żeby mieć jeden wielki dokument, tylko inaczej zamodelować dane procesy biznesowe. Akurat w przykładzie z płatnościami eventy dobrze pasują, bo jak już napisałem dla systemów finansowych jest to coś "naturalnego".

W SQL bym założył transakcję, zaktualizował wpisy w 2 tabelach i nie zastanawiał się 3 dni jak to inaczej zamodelować.

Cały paradoks Twojego scenariusza polega na tym że w świecie rzeczywistym transakcje finansowe nie działają tak że jest kilka wpisów dokonanych w bazie transakcyjnie, i nagle w mgnieniu oka pieniądze z jednego konta są pobrane, i na inne konto wpłacone. Tak więc pisząc o potrzebie transakcyjności użyłeś scenariusza, która w rzeczywistości tej transakcyjności (w rozumieniu transakcyjności bazodanowej) nie wykorzystuje.

Event sourcing? No spoko, ale czy warto się w niego pchać, tylko dlatego że w jednym miejscu systemu by rozwiązał jakiś problem spodowany brakiem transakcyjności bazy dokumentowej?

Chyba opacznie mnie zrozumiałeś. Ja sam napisałem że używanie *eventów *może być przerostem formy nad treścią, tylko po to aby ominąć brak transakcyjności.Wspomniałem event sourcing tak na marginesie- że jeśli już masz jakiś system gdzie są zapisywane eventy, i na tych eventach wszystko się opiera, to wtedy event sourcing wydaje się dobrą opcją. Ale to było tylko na marginesie, i w żadnym razie nie jest sugestią aby stosować event sourcing jako alternatywę dla transakcji!

0

Jak dla mnie to większym problemem przy nosqlu jest brak kluczy obcych, a nie brak transakcji.

1

Od prawie 2 lat używam tylko CosmosDB, problemów z tranzakcjami nie miałem, ale ja też specyficzne rzeczy robię.

omenomn2 napisał(a):

Jak dla mnie to większym problemem przy nosqlu jest brak kluczy obcych, a nie brak transakcji.

To może być problem tylko wówczas, gdy próbuje się używać bazy nosql jak relacyjnej i emulować tabelki przy pomocy kolekcji/kontenerów/czy jak to się zwie w różnych nosqlach.
Pewnie nawet jakiś fejkowy ORM do tego jest potrzebny. :)

0

To może być problem tylko wówczas, gdy próbuje się używać bazy nosql jak relacyjnej i emulować tabelki przy pomocy kolekcji/kontenerów/czy jak to się zwie w różnych nosqlach.

W każdej relacji "wiele do wielu" i w różnych innych sytuacjach.
Np. jak mamy usera, który ma posty, ale te posty mają też kategorię, a kategorie trzymamy w drzewku oddzielnie i np. chcemy, żeby po usunięciu kategorii usuwał się post. Klucz obcy nam to rozwiązuje. W nosqlu musimy pisać dodatkowy kod, który będzie odpowiedzialny za usuwanie zbędnych rekordów, albo kiedy chcemy zwrócić nazwę kategorii dla każdego postu, to ciężko jest z drzewka wydostać odpowiednią kategorię po id.

0
Aventus napisał(a):

W "prostych" i "typowych" bazach bardzo często tych transakcji nie trzeba mieć, wbrew temu co się wydaje. Powtarzam to w kółko ale jest tak dla tego że naprawdę trzeba zrozumieć że modelowanie baz dokumentowych (a raczej- modelowanie domeny która później jest po prostu "wrzucana" do bazy NoSQL) to inny sposób myślenia, i przerzucenie się na taki sposób myślenia całkowicie zmienia perspektywę. Rzeczy które wydają się niezbędne, a które znane są z baz relacyjnych, nagle okazują się potrzebne tylko w wyjątkowych przypadkach. Także te wątpliwości i przekonanie że "przecież muszę tu mieć transakcję" bierze się z ograniczonej perspektywy. Nie piszę tego złośliwie bo ja sam kilka lat temu zaczynałem wchodzić w świat NoSQL, i również nie potrafiłem sobie wyobrazić jak można mieć dane bez transakcji (chociaż jak wspomnę niżej, NoSQL != brak transakcji) czy szczególnie- bez referential integrity. To trochę jak z przejściem z kodu proceduralnego na OOP, czy z OOP na FP. Trzeba to przetestować na własnej skórze, i mieć dobre wzorce, żeby w pełni zrozumieć ale i nie popełnić głupot.

Bardzo możliwe i dlatego chciałem się dowiedzieć jak można obsłużyć scenariusz, który przedstawiłem bez wsparcia transakcyjności.
Skupiłem się na CosmosDB, bo pisałeś, że używasz go teraz w pracy, czyli masz z nim doświadczenie i ja też z niego korzystałem :)

Mój przykład należy raczej do prostszych i typowych w jakimś oprogramowaniu SaaS - czy to liczba maili, które użytkownik może wysłać, czy jakichś procesów które może uruchomić itp.

Nie rozumiem co masz na myśli przez wysłanie maili albo uruchomienie procesów. No ale nie wdawajmy się w szczegóły konkretnych scenariuszy. Wydajesz się popełniać podstawowy błąd myślowy- użyłeś Cosmos jako przykładu bazy która ma ograniczone transakcje, a sugerujesz jakby wszystkie bazy NoSQL takie były. NoSQL ma to do siebie że każda baza robi pewne rzeczy inaczej, i są bazy w pełni wspierające transakcje. Jeśli bardzo potrzebujesz transakcji, użyj bazy NoSQL z transakcjami, proste.

To były tylko przykłady aplikacji, gdzie użytkownik kupuje jakiś pakiet i dostaje pulę zasobów/operacji do wykorzystania. Tak jak wcześniej pisałem o tym, że po potwierdzeniu płatności dodaję użytkownikowi jakieś abstrakcyjne "kredyty" do wykorzystnia. Ale tak, konkretne scenariusze nie mają znaczenia.

W SQL bym założył transakcję, zaktualizował wpisy w 2 tabelach i nie zastanawiał się 3 dni jak to inaczej zamodelować.

Cały paradoks Twojego scenariusza polega na tym że w świecie rzeczywistym transakcje finansowe nie działają tak że jest kilka wpisów dokonanych w bazie transakcyjnie, i nagle w mgnieniu oka pieniądze z jednego konta są pobrane, i na inne konto wpłacone. Tak więc pisząc o potrzebie transakcyjności użyłeś scenariusza, która w rzeczywistości tej transakcyjności (w rozumieniu transakcyjności bazodanowej) nie wykorzystuje.

Nie mam doświadczenia z systemami finansowymi, więc może być tak jak mówisz. Tylko ciągle mi się wydaje, że ja mam dużo prostszy przypadek i to jak tak na prawdę pod spodem działają transakcje finansowe nie ma dużego znaczenia.

Z bramki płatności dostaję informację, że użytkownik dokonał opłaty -> u mnie w systemie oznaczam płatność jako zakończoną i dopisuję do konta użytkownika 100 tych abstrakcyjnych kredytów. To powinno się wykonać albo razem albo wcale. I tyle.

I w zasadzie tylko tego dotyczyło moje pytanie. Jak to można sensownie zamodelować/obsłużyć w przypadku bazy CosmosDB :)
Jeden sposób, który wczoraj na szybko mi wpadł do głowy to było oznaczenie statusu płatności jako zakończona i opublikowanie eventu przy pomocy patternu transactional outbox. Dochodzi nam wtedy aspekt asynchroniczności, ale jestem zabezpieczony przed udanym zapisem statusu płatności, a nieudanym zwiększeniem salda użytkownika.

Inny to może tak jak wspomniał @somekind, zamiast emulować tableki (czyli w moim przykładzie kolekcje/kontenery Payments i Users) mógłbym to trzymać jakoś przy sobie w obrębie tej samej partycj. Wtedy mogę to zapisać atomowo, ale tutaj z kolei trzeba uważać, bo pojedyncza partycja ma jakieś swoje limity w Cosmosie, już nie pamiętam czy 20GB czy 50GB.

3

@some_ONE:

Jak to można sensownie zamodelować/obsłużyć w przypadku bazy CosmosD

Fajnie że to wspomniałeś, bo podkreśla tą różnicę w rozumowaniu o której pisałem wcześniej. Tutaj trzeba zaznaczyć że to kwestia modelowania domenowego. Baza ma tutaj drugorzędne znaczenie. Jeśli miałbym pisać system dla takiego scenariusza jaki opisałeś, to w pierwszej kolejności skupił bym się na procesie biznesowym. I tak, w tym konkretnym przypadku zapewne zastosowałbym eventy, właśnie dla tego że są naturalnym odzwierciedleniem domeny.

Z bramki płatności dostaję informację, że użytkownik dokonał opłaty -> u mnie w systemie oznaczam płatność jako zakończoną i dopisuję do konta użytkownika 100 tych abstrakcyjnych kredytów. To powinno się wykonać albo razem albo wcale. I tyle.

No i ja się nie zgadzam. Emitujesz event PaymentReceived z kwotą. Następnie możesz asynchronicznie zaktualizować widok (to bardzo ważne- to jest tylko widok, a nie "source of truth") aktualnego stanu konta użytkownika. Tak samo możesz asynchronicznie zaktualizować widok płatności (jeśli jest potrzeba coś takiego w ogóle mieć). Tutaj też odsyłam do ciekawej prezentacji, tym razem o modelowaniu agregatów w DDD, ale myślę że ma to znaczenie również w kontekście tego co omawiamy. All our aggregates are wrong

Widzisz co próbuję przekazać? To co tutaj omawiamy nie ma zbyt wiele wspólnego z bazami danych. Myślę że warto skończyć ten temat bo zaczyna się offtopic. Jeśli chciałbyś podyskutować o modelowaniu domeny to zachęcam założyć oddzielny wątek do tego.

0

Przecież są bazy NoSQL będące zgodne z ACID, chociażby ArangoDB

1

Używam teraz prywatnie NoSQL, ale szczerze, to dalej nie widzę dużej przewagi nad SQLem :/ Podobno są trzy stopnie pewności: przekonać siebie (1), przekonać przyjaciela (2), przekonać wroga (3). Ja jestem na etapie (1). Wcielę się więc w adwokata diabła:

  1. Wygoda. Piszemy encję, wrzucamy ją do JSONa i tyle. OK, tylko że z drugiej strony w jakimś EF Core wystarczą dwie komendy (jedna do stworzenia migracji i druga do jej wykonania) i już mamy odpowiednie tabelki w bazie. Wychodzi pewnie jakieś 10 sekund na wpisanie i wykonanie komend.

  2. Zmiany w schemie. Wydaje mi się, że przy NoSQL też trzeba robić jakieś skrypty zmian, tak jak przy SQL. No bo co, jak tych skryptów zmian nie będzie? Minie rok od napisania pierwszej wersji encji i okaże się, że stare encje nie maja połowy pól, które mają encje nowe. Pomijam tutaj kwestie typu zmiany nazw pól.

  3. Modelowanie. Z tego, co rozumiem, to przy denormalizacji danych mamy rozróżnienie na point-in-time data i current data. Przykładem point-in-time data jest np. adres dostawy w zamówieniu. Nie chcemy, aby po zmianie adresu klienta zmienił się adres dostawy zrealizowanych zamówień tego klienta, dlatego adres klienta kopiujemy do zamówienia. Tylko że... to samo robimy, gdy mamy relacyjną bazę danych. Denormalizować dane można też w relacyjnych bazach danych. NoSQL nie ma tutaj wyłączności na denormalizację. A co z current data, czyli danymi, których potrzebujemy zawsze w najnowszej wersji? No tu SQL ma przewagę, robimy joina i w zasadzie tyle. W NoSQL też można, ale... skoro i tak musimy robić joiny, to dlaczego nie użyć do tego czegoś, co się lepiej nadaje? Bazy dokumentowe są trochę ograniczone pod tym względem. W świecie NoSQL uważa się, że joiny powinny być "płytkie", tzn. jak mamy dokument A, który jest powiązany z dokumentem B, który jest powiązany z dokumentem C, to nie powinniśmy robić joina A->B->C, tylko co najwyżej A->B. Żródło. Oznacza to, że w dokumencie B musimy zawrzeć dane z C. Co z kolei oznacza, że musimy robić synchronizację dokumentu B z C. Czyli nie mamy już wielopoziomowych joinów, ale za to musimy robić synchronizację danych w dokumentach i tracimy single source of truth. Miało być łatwiej niż w SQL, a jest trudniej.

Takie tam moje przemyślenia po paru dniach korzystania z Raven DB.

2

@nobody01: dałem okejkę za podzielenie się doświadczeniami, chociaż uważam że większość problemów znów wynika ze złego podejścia i myślenia relacyjnego. Nie chcę tutaj robić off-topu, więc odpowiem krótko:

  1. Wygoda. Piszemy encję, wrzucamy ją do JSONa i tyle. OK, tylko że z drugiej strony w jakimś EF Core wystarczą dwie komendy (jedna do stworzenia migracji i druga do jej wykonania) i już mamy odpowiednie tabelki w bazie. Wychodzi pewnie jakieś 10 sekund na wpisanie i wykonanie komend.

To nie do końca prawda. Często trzeba się martwić o dopisanie wartości domyślnych, dla KAŻDEJ zmiany trzeba tworzyć migrację (dodanie pola do encji, utworzenie nowej encji, skasowanie istniejącej encji itp.). Poza tym chyba nigdy Ci się nie rypła migracja na jakimś środowisku przed-produkcyjnym lub produkcyjnym :P

  1. Zmiany w schemie. Wydaje mi się, że przy NoSQL też trzeba robić jakieś skrypty zmian, tak jak przy SQL. No bo co, jak tych skryptów zmian nie będzie? Minie rok od napisania pierwszej wersji encji i okaże się, że stare encje nie maja połowy pól, które mają encje nowe. Pomijam tutaj kwestie typu zmiany nazw pól.

Znów, to jest problem z modelowanie. Przy rozsądnym i przemyślanym modelowaniu rozważa się 10 razy zanim doda się jakieś nowe pole do obiektu. Nie stosuje się bezrefleksyjnego rozbudowania modelu domeny i łatania obiektów nowymi polami jak tylko zajdzie taka potrzeba na pierwszy rzut oka. Zresztą ten inny sposób myślenia nie jest typowy dla NoSQL- w event sourcingu też nie łata się bezrefleksyjnie eventów nowymi polami. Oczywiście bywa i tak że są jakieś zmiany które wymagają łatania, np. kiedy dodane jest nowe pole które w istniejących dokumentach nie może mieć wartości domyślnej.Wtedy łata się takie dane, a jak to zrobić zależy od konkretnej bazy. I tak, w tym przypadku update za pomocą SQL jest łatwiejszy, nie ma co do tego wątpliwości.

  1. Tylko że... to samo robimy, gdy mamy relacyjną bazę danych. Denormalizować dane można też w relacyjnych bazach danych. NoSQL nie ma tutaj wyłączności na denormalizację.

Skąd przekonanie że ktokolwiek twierdzi że NoSQL promuje denomarmalizację jako coś typowego dla tych właśnie baz? Po prostu denormalizacja jest prostsza w tym przypadku, bo jak masz adres dostarczenia przesyłki to możesz go przechować w ramach dokumentu zamówienia (a więc w domenie- jako właściwość zamówienia), nie trzeba się bawić w tworzenie oddzielnych tabel itp.

A co z current data, czyli danymi, których potrzebujemy zawsze w najnowszej wersji? No tu SQL ma przewagę, robimy joina i w zasadzie tyle. W NoSQL też można, ale... skoro i tak musimy robić joiny, to dlaczego nie użyć do tego czegoś, co się lepiej nadaje?

Jeśli ma się scenariusz gdzie aby zbudować widok trzeba robić zawiłe joiny, to fakcznie. Z tym że ja w takim przypadku nawet przy stosowaniu SQL wolałbym mieć gotowe widoki zbudowane asynchronicznie (materialized view), tak aby były gotowe do zwrócenia kiedy tyko przyjdzie zapytanie.

Bazy dokumentowe są trochę ograniczone pod tym względem. W świecie NoSQL uważa się, że joiny powinny być "płytkie", tzn. jak mamy dokument A, który jest powiązany z dokumentem B, który jest powiązany z dokumentem C, to nie powinniśmy robić joina A->B->C, tylko co najwyżej A->B. Żródło. Oznacza to, że w dokumencie B musimy zawrzeć dane z C. Co z kolei oznacza, że musimy robić synchronizację dokumentu B z C. Czyli nie mamy już wielopoziomowych joinów, ale za to musimy robić synchronizację danych w dokumentach i tracimy single source of truth. Miało być łatwiej niż w SQL, a jest trudniej.

Znów się powtórzę- wszystko sprowadza się do odpowiedniego modelowania i ewentualnie stosowania gotowych modeli widoków. No nie da się tego zrozumieć inaczej niż poprzez praktykę. To tak jak z próbowaniem wytłumaczyć osobie początkującej z programowaniem sens używania interfejsów. Dopóki nie zderzą się z przypadkiem gdzie to faktycznie ma sens, to tego sensu nie będą widzieć.

1

@Aventus: Nie rozumiem do końca, o co chodzi z modelowaniem. Weźmy taki przykład.
Mamy sklep z jakimiś produktami. Klienci przy produkcie mogą złożyć jakieś zapytanie na temat produktu. Zapytanie to trafia do osoby, która zajmuje się produktami z danej grupy produktów. Przy modelowaniu relacyjnym mielibyśmy encje Product mającą referencję do ProductGroup, która miałaby referencję do jakiegoś ProductConsultant. Pod względem domeny zamodelowałbym to podobnie, tzn. każda z tych encji byłaby osobnym agregatem. No i teraz powiedzmy, że przy produkcie chcemy wyświetlić dane konsultanta dla tego produktu. Jak to zrobić?

  1. Join Product->ProductGroup->ProductConsultant, czyli to, czego teoretycznie powinnyśmy uniknąć. Rozwiązaniem jest widok zmaterializowany, tylko że moim zdaniem robienie widoków zmaterializowanych dla każdego takiego przypadku to trochę overkill, szczególnie w małych systemach. W SQLu wystarczyłby zwykły widok.
  2. Duplikowanie danych ProductConsultant w ProductGroup. Oznacza to, że te dwie encje musimy synchronizować ze sobą, bo będą się zdarzały sytuacje, że np. osoba X przestanie pracować albo pójdzie na urlop i jej obowiązki będzie musiał przejąć ktoś inny.
  3. Zmiana modelu domenowego. Tylko jak?
1

Ej no, ale jak masz dane z natury relacyjne, to proste, że rdbms będzie bardziej przystający :)

Nie wiem, co @Aventus mial na myśli pisząc o modelowaniu, ale biorąc ten przykład z Productami i Consultantami możesz chcieć zamodelować system w taki sposób, że są to encje zamknięte w niezależnych modułach - wtedy masz niezależne tabele/kolekcje. Może od strony Consultanta będziesz chciał mieć jakiś zdenormalizowany model Productu.

Już nie mówię nawet o jakichś cięższych zapytaniach czy raportowaniu - wtedy zazwyczaj rdbms klęka (chociaż oczywiście można to zrobić dobrze, w zależności od skali).

1

@nobody01: :
Łatwo wziąć jakiś prosty przykład, wyrwany z kontekstu całej aplikacji, i przedstawić go by poprzeć lub przeciwnie, skrytykować jakieś podejście. To tak jak opisałem w przykładzie z kimś początkującym w programowaniu i interfejsami. Nie potraktuję więc scenariusza który podałeś jako argument za SQL, lub przeciwko NoSQL, a po prostu jako pytanie- "jak to zrobić inaczej niż bym to zrobił w SQL?".

Mamy sklep z jakimiś produktami. Klienci przy produkcie mogą złożyć jakieś zapytanie na temat produktu. Zapytanie to trafia do osoby, która zajmuje się produktami z danej grupy produktów. Przy modelowaniu relacyjnym(...)

Widzisz, znów ten sam błąd myślowy- modelowanie relacyjne :) Dla czego baza ma cokolwiek tutaj narzucać? Najpierw skupmy się na zamodelowaniu domeny...

mielibyśmy encje Product mającą referencję do ProductGroup, która miałaby referencję do jakiegoś ProductConsultant. Pod względem domeny zamodelowałbym to podobnie, tzn. każda z tych encji byłaby osobnym agregatem. No i teraz powiedzmy, że przy produkcie chcemy wyświetlić dane konsultanta dla tego produktu. Jak to zrobić?

W tym konkretnym przypadku ja bym zakwestionował tę zależność- dla czego grupa produktów ma mieć powiązanie z konsultantem (specjalnie małe litery, bo mówimy o modelowaniu domeny a nie kodu per se). Wspomniałeś agregaty- dla czego zmiana agregatu ProductGroup miała by transakcyjnie angażować konsultanta? Co jeśli konsultant zostanie usunięty z systemu? Ja bym odwrócił tę zależność- to konsultant miałby do siebie przypisaną jedną lub więcej grup produktów (ciężko sobie wyobrazić że każdy konsultant zawsze odpowiadał by tylko za jedną grupę produktów). Wtedy na poziomie kodu (powtarzam że nadal nie mówimy nic o relacjach, bazach itp.) obiekt ProductConsultant miałby jakąś listę ID będących referencjami grup produktów.

  1. Join Product->ProductGroup->ProductConsultant, czyli to, czego teoretycznie powinnyśmy uniknąć. Rozwiązaniem jest widok zmaterializowany, tylko że moim zdaniem robienie widoków zmaterializowanych dla każdego takiego przypadku to trochę overkill, szczególnie w małych systemach. W SQLu wystarczyłby zwykły widok.

To prawda, w bardzo prostych aplikacjach tworzenie materialized views będzie zazwyczaj przerostem formy nad treścią. Ale jeśli mowa o prostych przypadkach, to dla czego w przypadku korzystania z baz NoSQL nie skorzystać z prostego podejścia. Wspomniałeś o tym że korzystasz z Raven DB, więc użyjmy tej bazy jako przykład:

var product = await session.Load<Product>(productId);
var consultant = await session.Query<ProductConsultant, ProductConsultants_ByProductGroup>()
    .Where(x => x.ProductGroup == product.Group)
    .ToListAsync();

return new ProductDetailsDto
{
  Name = product.Name,
  // ...
  ConsultantFullName = consultant.FullName
};

Czy naprawdę w prostej aplikacji będzie problemem wysłanie dwóch zapytań i złączenie tego w kodzie? W większości przypadków nie. Jeśli ktoś nie jest w stanie wymiernie wykazać, że w konkretnym przypadku wysyłanie dwóch zapytań powoduje problemy wydajnościowe, to problem nie istnieje. Sam stosuje tak proste rozwiązanie w jednej z moich komercyjnych aplikacji i zapewniam że świat się od tego nie zawalił, a zapytania zwracają wyniki w ciągu kilkudziesięciu milisekund. A jeśli to jednak nie jest prosta aplikacja bo skala jest o wiele większa i wpływa na przepustowość zapytań, to są do tego innego sposoby których przykładów nie chcę tutaj podawać ale wymienię:

  • Dedykowany indeks (indeks w rozumieniu RavenDB) który ładuje w locie powiązany dokument i łączy potrzebne informacje, w efekcie czego uzyskujemy coś w stylu SQL views
  • Przemodelowanie danych tak aby móc wykorzystać Lazy Operations
  • Wykorzystanie indeksu który zrzuci widok do nowej kolekcji
  • W dodatku w przypadku który podałem wyżej i przy wykorzystaniu dedykowanego indeksu można również skorzystać z projekcji po stronie bazy danych, a więc nie będzie trzeba "ręcznie" składać DTO w kodzie
  1. Duplikowanie danych ProductConsultant w ProductGroup. Oznacza to, że te dwie encje musimy synchronizować ze sobą, bo będą się zdarzały sytuacje, że np. osoba X przestanie pracować albo pójdzie na urlop i jej obowiązki będzie musiał przejąć ktoś inny.

Myślę że to co opisałem wyżej sprawia że ten problem nie ma już znaczenia.

2

Parę problemów, które dla rdbms są nietrywialne:

  1. Licznik wyświetleń/lajków filmu na YT albo zdjęcia na Insta
  2. Zapisywanie i agregowanie metryk
  3. Tworzenie kostek OLAP
  4. Wyszukiwarki, full-text search, fuzzy search
  5. Modele rekurencyjne, grafowe

1 użytkowników online, w tym zalogowanych: 0, gości: 1