Nie. CouchDB używa modelu „optymistycznej współbieżności”. Mówiąc najprościej, oznacza to po prostu, że wysyłasz wersję dokumentu wraz z aktualizacją, a CouchDB odrzuca zmianę, jeśli aktualna wersja dokumentu nie odpowiada temu, co wysłałeś.
To naprawdę zwodniczo proste. Możesz zmienić ramy wielu normalnych scenariuszy opartych na transakcjach dla CouchDB. Jednak podczas nauki CouchDB musisz wyrzucić swoją wiedzę o domenie RDBMS. Pomocne jest podejście do problemów z wyższego poziomu, zamiast próbować dopasować Couch do świata opartego na SQL.
Śledzenie zapasów
Problem, który nakreśliłeś, to przede wszystkim problem z zapasami. Jeśli masz dokument opisujący towar, który zawiera pole „Dostępna ilość”, możesz rozwiązać następujące problemy związane ze współbieżnością:
- Pobierz dokument, zanotuj
_rev
właściwość, którą przesyła CouchDB
- Zmniejsz pole ilości, jeśli jest większe od zera
- Odeślij zaktualizowany dokument, korzystając z
_rev
właściwości
- Jeśli numer
_rev
pasuje do aktualnie zapisanego numeru, gotowe!
- Jeśli występuje konflikt (kiedy
_rev
nie pasuje), pobierz najnowszą wersję dokumentu
W tym przypadku należy przemyśleć dwa możliwe scenariusze awarii. Jeśli najnowsza wersja dokumentu zawiera liczbę 0, postępujesz z nią tak, jak w przypadku systemu RDBMS i ostrzegasz użytkownika, że nie może faktycznie kupić tego, co chciał kupić. Jeśli najnowsza wersja dokumentu zawiera liczbę większą niż 0, po prostu powtórz operację ze zaktualizowanymi danymi i zacznij od początku. To zmusza cię do wykonania nieco więcej pracy niż RDBMS i może być trochę irytujące, jeśli występują częste, sprzeczne aktualizacje.
Odpowiedź, której właśnie udzieliłem, zakłada, że zamierzasz robić rzeczy w CouchDB w taki sam sposób, jak w RDBMS. Mogę podejść do tego problemu nieco inaczej:
Zacząłbym od dokumentu „produktu głównego”, który zawiera wszystkie dane deskryptora (nazwa, zdjęcie, opis, cena itp.). Następnie dodałbym dokument „kwitu inwentaryzacyjnego” dla każdej konkretnej instancji z polami product_key
i claimed_by
. Jeśli sprzedajesz model młota i masz ich 20 do sprzedania, możesz mieć dokumenty z kluczami, takimi jak hammer-1
:hammer-2
itd, aby reprezentować każdą dostępną młotek.
Następnie utworzyłbym widok, który daje mi listę dostępnych młotków z funkcją zmniejszania, która pozwala mi zobaczyć „sumę”. Są one całkowicie poza mankietem, ale powinny dać ci wyobrażenie o tym, jak wyglądałby widok roboczy.
Mapa
function(doc)
{
if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) {
emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev });
}
}
To daje mi listę dostępnych „biletów” według klucza produktu. Mógłbym złapać ich grupę, gdy ktoś chce kupić młotek, a następnie iterować, wysyłając aktualizacje (używając id
i_rev
), aż z powodzeniem zdobędę jeden (wcześniej zgłoszone bilety spowodują błąd aktualizacji).
Zmniejszyć
function (keys, values, combine) {
return values.length;
}
Ta funkcja redukcji po prostu zwraca całkowitą liczbę nieodebranych inventory_ticket
elementów, dzięki czemu można określić, ile „młotków” jest dostępnych do zakupu.
Ostrzeżenia
To rozwiązanie reprezentuje około 3,5 minuty całkowitego myślenia nad konkretnym problemem, który przedstawiłeś. Mogą istnieć lepsze sposoby na zrobienie tego! To powiedziawszy, znacznie zmniejsza liczbę konfliktów aktualizacji i ogranicza potrzebę reagowania na konflikt za pomocą nowej aktualizacji. W tym modelu wielu użytkowników nie będzie próbowało zmieniać danych w głównym wpisie produktu. W najgorszym przypadku wielu użytkowników będzie próbowało odebrać jeden bilet, a jeśli złapałeś kilka z nich z widoku, po prostu przejdź do następnego biletu i spróbuj ponownie.
Źródła: https://wiki.apache.org/couchdb/Frequently_asked_questions#How_do_I_use_transactions_with_CouchDB.3F
Poszerzenie odpowiedzi MrKurt. W przypadku wielu scenariuszy nie musisz zamawiać w kolejności biletów giełdowych. Zamiast wybierać pierwszy bilet, możesz wybrać losowo spośród pozostałych biletów. Biorąc pod uwagę dużą liczbę biletów i dużą liczbę jednoczesnych żądań, uzyskasz znacznie mniejszą rywalizację o te bilety w porównaniu do wszystkich, którzy próbują zdobyć pierwszy bilet.
źródło
Wzorzec projektowy dla niespełnionych transakcji polega na stworzeniu „napięcia” w systemie. W popularnym przykładzie użycia transakcji na koncie bankowym musisz zaktualizować sumę dla obu zaangażowanych kont:
Skanowanie w poszukiwaniu napięcia powinno być wykonywane w procesie zaplecza dla wszystkich „dokumentów dotyczących napięcia”, aby skrócić czasy napięć w systemie. W powyższym przykładzie przewidywana niespójność w krótkim czasie wystąpi, gdy pierwsze konto zostanie zaktualizowane, a drugie nie zostanie jeszcze zaktualizowane. Należy to wziąć pod uwagę w ten sam sposób, w jaki będziesz radzić sobie z ostateczną konsekwencją, jeśli Twoja Couchdb jest dystrybuowana.
Inna możliwa implementacja pozwala całkowicie uniknąć transakcji: po prostu przechowuj dokumenty dotyczące napięcia i oceń stan swojego systemu, oceniając każdy dokument dotyczący napięcia. W powyższym przykładzie oznaczałoby to, że suma dla rachunku jest określana tylko jako suma wartości w dokumentach transakcji, w których ten rachunek jest zaangażowany. W Couchdb można to bardzo ładnie modelować jako widok mapy / zmniejszania.
źródło
Nie, CouchDB generalnie nie nadaje się do aplikacji transakcyjnych, ponieważ nie obsługuje operacji atomowych w środowisku klastrowym / replikowanym.
CouchDB poświęcił możliwości transakcyjne na rzecz skalowalności. Aby mieć atomowe operacje, potrzebujesz centralnego systemu koordynacji, który ogranicza twoją skalowalność.
Jeśli możesz zagwarantować, że masz tylko jedną instancję CouchDB lub że wszyscy modyfikujący określony dokument łączą się z tą samą instancją CouchDB, możesz użyć systemu wykrywania konfliktów do stworzenia pewnego rodzaju atomowości przy użyciu metod opisanych powyżej, ale jeśli później skalujesz w górę do klastra lub skorzystaj z usługi hostowanej, takiej jak Cloudant, zepsuje się i będziesz musiał przerobić tę część systemu.
Tak więc moja sugestia byłaby taka, aby użyć czegoś innego niż CouchDB do sald konta, w ten sposób będzie o wiele łatwiej.
źródło
W odpowiedzi na problem OP Couch prawdopodobnie nie jest tutaj najlepszym wyborem. Korzystanie z widoków to świetny sposób na śledzenie zapasów, ale ograniczenie do 0 jest mniej więcej niemożliwe. Problemem jest stan wyścigu, kiedy czytasz wynik widoku, decydujesz, że możesz użyć elementu „młotek-1”, a następnie napisz dokument, aby go użyć. Problem polega na tym, że nie ma atomowego sposobu, aby napisać dokument tylko po to, aby użyć młotka, jeśli wynikiem widoku jest> 0 młotków-1. Jeśli 100 użytkowników jednocześnie zapyta widok i zobaczy 1 młotek-1, wszyscy mogą napisać dokument, aby użyć młotka 1, co daje -99 młotków-1. W praktyce stan wyścigu będzie dość mały - naprawdę mały, jeśli twoja baza danych działa na serwerze lokalnym. Ale po skalowaniu i posiadaniu zewnętrznego serwera bazy danych lub klastra problem stanie się znacznie bardziej zauważalny.
Aktualizacja odpowiedzi MrKurt (może być po prostu datowana lub mógł nie wiedzieć o niektórych funkcjach CouchDB)
Widok to dobry sposób na obsługę takich rzeczy, jak salda / zapasy w CouchDB.
Nie musisz emitować dokumentu docid i rev w widoku. Przy pobieraniu wyników widoku oba te elementy są bezpłatne. Emitowanie ich - zwłaszcza w formacie rozwlekłym, takim jak słownik - spowoduje niepotrzebne powiększenie widoku.
Prosty widok do śledzenia sald zapasów powinien wyglądać bardziej tak (również z góry mojej głowy)
Funkcja redukcji jest jeszcze prostsza
Wykorzystuje wbudowaną funkcję redukcji która po prostu sumuje wartości wszystkich wierszy z pasującymi kluczami.
W tym widoku każdy dokument może mieć element „InventoryChange”, który odwzorowuje klucz produktu na zmianę w jego całkowitym stanie magazynowym. to znaczy.
Dodałby 10 hammer_1234's i 25 saw_4321's.
Spaliłby 5 młotów z inwentarza.
W tym modelu nigdy nie aktualizujesz żadnych danych, a jedynie dodajesz. Oznacza to, że nie ma możliwości wystąpienia konfliktów aktualizacji. Wszystkie problemy transakcyjne związane z aktualizacją danych odchodzą :)
Kolejną fajną rzeczą w tym modelu jest to, że KAŻDY dokument w bazie danych może zarówno dodawać, jak i odejmować pozycje z inwentarza. Dokumenty te mogą zawierać różnego rodzaju inne dane. Możesz mieć dokument „Wysyłka” zawierający zbiór danych o dacie i godzinie odbioru, magazynie, odbierającym pracowniku itp. I tak długo, jak ten dokument określa InventoryChange, zaktualizuje on stan zapasów. Podobnie jak dokument „Sale”, dokument „DamagedItem” itp. Patrząc na każdy dokument, czytają bardzo wyraźnie. A widok radzi sobie z całą ciężką pracą.
źródło
Właściwie w pewnym sensie możesz. Przyjrzyj się interfejsowi API dokumentów HTTP i przewiń w dół do nagłówka „Modyfikuj wiele dokumentów za pomocą jednego żądania”.
Zasadniczo możesz utworzyć / zaktualizować / usunąć kilka dokumentów w jednym żądaniu postu do URI / {dbname} / _ bulk_docs i albo wszystkie się powiodą, albo wszystkie nie. Dokument ostrzega jednak, że to zachowanie może ulec zmianie w przyszłości.
EDYCJA: Zgodnie z przewidywaniami, od wersji 0.9 dokumenty zbiorcze nie działają już w ten sposób.
źródło
Po prostu użyj lekkiego rozwiązania SQlite do transakcji, a gdy transakcja zostanie zakończona pomyślnie, zreplikuj ją i oznacz jako replikowaną w SQLite
Tabela SQLite
Możesz także usunąć transakcje, które zostały pomyślnie zreplikowane.
źródło