Wiem, że są tutaj podobne pytania, ale albo mówią mi, żebym przełączył się z powrotem na zwykłe systemy RDBMS, jeśli potrzebuję transakcji, albo użyj operacji atomowych lub zatwierdzania dwufazowego . Drugie rozwiązanie wydaje się najlepszym wyborem. Trzeci, za którym nie chcę podążać, ponieważ wydaje się, że wiele rzeczy może pójść nie tak i nie mogę tego przetestować pod każdym względem. Mam trudności z refaktoryzacją mojego projektu, aby wykonać operacje atomowe. Nie wiem, czy wynika to z mojego ograniczonego punktu widzenia (do tej pory pracowałem tylko z bazami danych SQL), czy też faktycznie nie można tego zrobić.
Chcielibyśmy przeprowadzić pilotażowe testy MongoDB w naszej firmie. Wybraliśmy stosunkowo prosty projekt - bramkę SMS. Pozwala naszemu oprogramowaniu na wysyłanie wiadomości SMS do sieci komórkowej, a bramka wykonuje brudną robotę: faktycznie komunikuje się z dostawcami za pośrednictwem różnych protokołów komunikacyjnych. Brama zarządza również fakturowaniem wiadomości. Każdy klient zgłaszający się do usługi musi wykupić kilka kredytów. System automatycznie zmniejsza saldo użytkownika w przypadku wysłania wiadomości i odmawia dostępu, jeśli saldo jest niewystarczające. Ponieważ jesteśmy klientami zewnętrznych dostawców usług SMS, możemy również mieć u nich własne salda. Te również musimy śledzić.
Zacząłem się zastanawiać, jak mogę przechowywać wymagane dane w MongoDB, jeśli zredukuję trochę złożoności (rozliczenia zewnętrzne, wysyłanie SMS-ów w kolejce). Pochodząc ze świata SQL, stworzyłbym osobną tabelę dla użytkowników, drugą dla wiadomości SMS i jedną do przechowywania transakcji dotyczących salda użytkowników. Powiedzmy, że tworzę osobne kolekcje dla wszystkich w MongoDB.
Wyobraź sobie zadanie wysyłania wiadomości SMS z następującymi krokami w tym uproszczonym systemie:
sprawdzić, czy użytkownik ma wystarczającą równowagę; odmów dostępu, jeśli nie ma wystarczających środków
wysłać i zapisać wiadomość w kolekcji SMS ze szczegółami i kosztem (w systemie na żywo wiadomość miałaby
status
atrybut, a zadanie odebrałoby ją do doręczenia i ustalało cenę SMS-a zgodnie z jego aktualnym stanem)zmniejszyć saldo użytkowników o koszt wysłanej wiadomości
zarejestruj transakcję w kolekcji transakcji
Jaki jest z tym problem? MongoDB może wykonywać niepodzielne aktualizacje tylko w jednym dokumencie. W poprzednim przepływie może się zdarzyć, że wkradnie się jakiś błąd i wiadomość zostanie zapisana w bazie danych, ale saldo użytkownika nie jest aktualizowane i / lub transakcja nie jest rejestrowana.
Wpadłem na dwa pomysły:
Utwórz pojedynczą kolekcję dla użytkowników i przechowuj saldo jako pole, transakcje związane z użytkownikiem i komunikaty jako dokumenty podrzędne w dokumencie użytkownika. Ponieważ możemy aktualizować dokumenty w sposób atomowy, w rzeczywistości rozwiązuje to problem transakcji. Wady: jeśli użytkownik wyśle wiele wiadomości SMS, rozmiar dokumentu może stać się duży i może zostać osiągnięty limit 4 MB dokumentów. Może uda mi się stworzyć dokumenty historyczne w takich scenariuszach, ale nie sądzę, żeby to był dobry pomysł. Nie wiem też, jak szybki byłby system, gdybym wypychał coraz więcej danych do tego samego dużego dokumentu.
Utwórz jedną kolekcję dla użytkowników i jedną dla transakcji. Mogą być dwa rodzaje transakcji: zakup kredytowy z dodatnią zmianą salda oraz wiadomości wysłane z ujemną zmianą salda. Transakcja może mieć dokument podrzędny; na przykład w wysyłanych wiadomościach szczegóły SMS-a mogą być osadzone w transakcji. Wady: nie przechowuję aktualnego salda użytkownika, więc muszę je obliczać za każdym razem, gdy użytkownik próbuje wysłać wiadomość, aby stwierdzić, czy wiadomość może przejść, czy nie. Obawiam się, że te obliczenia mogą stać się powolne w miarę wzrostu liczby przechowywanych transakcji.
Jestem trochę zdezorientowany, którą metodę wybrać. Czy są inne rozwiązania? Nie udało mi się znaleźć w internecie żadnych sprawdzonych metod dotyczących rozwiązywania tego typu problemów. Myślę, że wielu programistów, którzy próbują zaznajomić się ze światem NoSQL, ma na początku podobne problemy.
źródło
Odpowiedzi:
Począwszy od 4.0, MongoDB będzie obsługiwać wielodokumentowe transakcje ACID. Plan polega na tym, aby najpierw włączyć te we wdrożeniach zestawu replik, a następnie klastry podzielone na fragmenty. Transakcje w MongoDB będą wyglądać tak, jak transakcje, które programiści znają z relacyjnych baz danych - będą składać się z wielu instrukcji, z podobną semantyką i składnią (jak
start_transaction
icommit_transaction
). Co ważne, zmiany w MongoDB, które umożliwiają transakcje, nie wpływają na wydajność obciążeń, które ich nie wymagają.Więcej informacji można znaleźć tutaj .
Posiadanie transakcji rozproszonych nie oznacza, że należy modelować dane tak, jak w tabelarycznych relacyjnych bazach danych. Wykorzystaj możliwości modelu dokumentu i postępuj zgodnie z dobrymi i zalecanymi praktykami modelowania danych.
źródło
Życie bez transakcji
Transakcje obsługują właściwości ACID , ale chociaż nie ma transakcji w
MongoDB
, mamy operacje atomowe. Cóż, niepodzielne operacje oznaczają, że kiedy pracujesz nad pojedynczym dokumentem, praca ta zostanie zakończona, zanim ktokolwiek inny zobaczy dokument. Zobaczą wszystkie wprowadzone przez nas zmiany lub żadną z nich. Używając operacji atomowych, często można osiągnąć to samo, co osiągnęlibyśmy przy użyciu transakcji w relacyjnej bazie danych. Powodem jest to, że w relacyjnej bazie danych musimy wprowadzić zmiany w wielu tabelach. Zwykle stoły, które trzeba połączyć, więc chcemy to zrobić od razu. Aby to zrobić, ponieważ istnieje wiele tabel, będziemy musieli rozpocząć transakcję i wykonać wszystkie aktualizacje, a następnie zakończyć transakcję. Ale zMongoDB
, zamierzamy osadzić dane, ponieważ zamierzamy je wstępnie łączyć w dokumentach i są to te bogate dokumenty, które mają hierarchię. Często możemy osiągnąć to samo. Na przykład w przykładzie na blogu, jeśli chcieliśmy mieć pewność, że zaktualizowaliśmy post na blogu atomowo, możemy to zrobić, ponieważ możemy zaktualizować cały wpis na blogu jednocześnie. Gdyby to była grupa tabel relacyjnych, prawdopodobnie musielibyśmy otworzyć transakcję, abyśmy mogli zaktualizować zbiór postów i kolekcję komentarzy.Więc jakie są nasze podejścia, które możemy przyjąć,
MongoDB
aby przezwyciężyć brak transakcji?Update
,findAndModify
,$addToSet
(W ramach aktualizacji) i$push
(w ramach aktualizacji) operacje działają atomowo w ramach jednego dokumentu.źródło
Sprawdź to , autorstwa Tokutka. Opracowują wtyczkę do Mongo, która obiecuje nie tylko transakcje, ale także wzrost wydajności.
źródło
Skoncentruj się na rzeczy: jeśli integralność transakcyjna jest koniecznością , nie używaj MongoDB, ale używaj tylko komponentów w systemie obsługujących transakcje. Niezwykle trudno jest zbudować coś na wierzchu komponentu, aby zapewnić podobną funkcjonalność dla komponentów niezgodnych z ACID. W zależności od indywidualnych przypadków może mieć sens rozdzielenie działań na czynności transakcyjne i nietransakcyjne w jakiś sposób ...
źródło
To naprawdę nie jest problem. Błąd, o którym wspomniałeś, to błąd logiczny (błąd) lub błąd we / wy (sieć, awaria dysku). Tego rodzaju błąd może pozostawić zarówno sklepy bez transakcji, jak i sklepy transakcyjne w stanie niespójnym. Na przykład, jeśli wysłał już SMS, ale podczas zapisywania wiadomości wystąpił błąd - nie może cofnąć wysyłania SMS-ów, co oznacza, że nie zostanie zalogowany, saldo użytkownika nie zostanie zmniejszone itp.
Prawdziwy problem polega na tym, że użytkownik może wykorzystać sytuację wyścigu i wysłać więcej wiadomości, niż pozwala na to jego saldo. Dotyczy to również RDBMS, chyba że wysyłasz SMS-a wewnątrz transakcji z blokowaniem pola salda (co byłoby wielkim wąskim gardłem). Jako możliwe rozwiązanie dla MongoDB byłoby użycie
findAndModify
najpierw do zmniejszenia salda i sprawdzenia go, jeśli jest ujemne, nie zezwalaj na wysyłanie i zwrot kwoty (przyrost atomowy). Jeśli wynik jest pozytywny, kontynuuj wysyłanie, aw przypadku niepowodzenia zwróć kwotę. Zbieranie historii sald może być również utrzymywane, aby pomóc naprawić / zweryfikować saldo.źródło
Projekt jest prosty, ale musisz obsługiwać transakcje o zapłatę, co utrudnia całość. Na przykład złożony system portalu z setkami kolekcji (forum, czat, ogłoszenia itp.) Jest w pewnym sensie prostszy, ponieważ jeśli stracisz wpis na forum lub czacie, nikogo to nie obchodzi. Z drugiej strony, jeśli stracisz transakcję płatniczą, to poważny problem.
Jeśli więc naprawdę chcesz mieć projekt pilotażowy dla MongoDB, wybierz taki, który jest prosty pod tym względem.
źródło
Transakcje są nieobecne w MongoDB z ważnych powodów. To jedna z tych rzeczy, które sprawiają, że MongoDB jest szybsze.
W twoim przypadku, jeśli transakcja jest koniecznością, mongo wydaje się nie pasować.
Może to być RDMBS + MongoDB, ale zwiększy to złożoność i utrudni zarządzanie i obsługę aplikacji.
źródło
To prawdopodobnie najlepszy blog, jaki znalazłem na temat implementacji funkcji typu transakcyjnego dla mongodb.!
Flaga synchronizacji: najlepsza do kopiowania danych z dokumentu głównego
Kolejka zadań: bardzo ogólnego przeznaczenia, rozwiązuje 95% spraw. Większość systemów i tak musi mieć co najmniej jedną kolejkę zadań!
Dwufazowe zatwierdzenie: ta technika zapewnia, że każda jednostka zawsze ma wszystkie informacje potrzebne do uzyskania spójnego stanu
Log Reconciliation: najsolidniejsza technika, idealna dla systemów finansowych
Wersjonowanie: zapewnia izolację i obsługuje złożone struktury
Przeczytaj to, aby uzyskać więcej informacji: https://dzone.com/articles/how-implement-robust-and
źródło
Jest już późno, ale myślę, że pomoże to w przyszłości. Używam Redis do tworzenia kolejki w celu rozwiązania tego problemu.
Wymaganie:
Poniższy obraz przedstawia 2 akcje, które należy wykonać jednocześnie, ale faza 2 i faza 3 działania 1 muszą zakończyć się przed rozpoczęciem fazy 2 działania 2 lub odwrotnie (Faza może być żądaniem interfejsu API REST, żądaniem bazy danych lub wykonaniem kodu javascript ... ).
W jaki sposób kolejka pomaga
Kolejka upewnij się, że każdy kod blokowy między funkcjami
lock()
irelease()
w wielu nie będzie działał w tym samym czasie, spraw, aby były izolowane.Jak zbudować kolejkę
Skoncentruję się tylko na tym, jak uniknąć części warunkowej wyścigu podczas budowania kolejki na stronie zaplecza. Jeśli nie znasz podstawowej idei kolejki, przyjdź tutaj .
Poniższy kod pokazuje tylko koncepcję, którą musisz zaimplementować w prawidłowy sposób.
Ale musisz
isRunning()
setStateToRelease()
setStateToRunning()
odizolować się od siebie, bo inaczej znowu staniesz w obliczu rasy. Aby to zrobić, wybrałem Redis dla celów ACID i skalowalny. DokumentRedis mówi o swojej transakcji:
P / s:
używam Redis, ponieważ moja usługa już go używa, możesz w tym celu skorzystać z dowolnego innego sposobu wsparcia izolacji.
Kod
action_domain
w moim kodzie jest powyżej, gdy potrzebujesz tylko wywołania akcji 1 przez użytkownika. Działanie blokowe 2 użytkownika A, nie blokuj innego użytkownika. Pomysł polega na umieszczeniu unikalnego klucza do zamka każdego użytkownika.źródło
Transakcje są teraz dostępne w MongoDB 4.0. Próbka tutaj
źródło