2012-06-18 26 views
22

Wiem, że nie mogę zablokować pojedynczego dokumentu mongody, w rzeczywistości nie ma możliwości zablokowania kolekcji.Nie można zablokować dokumentu mongodb. Co jeśli muszę?

Jednak mam ten scenariusz, w którym, jak sądzę, potrzebuję sposobu, aby zapobiec modyfikowaniu dokumentu przez więcej niż jeden wątek (lub proces, to nie ważne). Oto mój scenariusz.

Mam kolekcję, która zawiera obiekt typu A. Mam kod, który pobiera dokument typu A, dodaj element w tablicy, która jest właściwością dokumentu (a.arr.add(new Thing()), a następnie zapisz dokument ponownie mongodb. Ten kod jest równoległy, wiele wątków w moich aplikacjach może wykonywać tezy, a na razie nie ma sposobu, aby zapobiec temu, by wątki powodowały równoległe wykonywanie tych samych operacji na tym samym dokumencie. Jest to złe, ponieważ jeden z wątków mógłby nadpisać dzieła drugiego.

Używam wzorca repozytorium do abstrakcyjnego dostępu do kolekcji mongodb, więc mam tylko operacje CRUD w mojej dyspozycji.

Teraz, kiedy o tym myślę, może to ograniczenie schematu repozytorium, a nie ograniczenie monoubbu, które sprawia mi kłopoty. W każdym razie, w jaki sposób mogę uczynić ten kod "wątkiem bezpiecznym"? Sądzę, że istnieje dobrze znane rozwiązanie tego problemu, ale będąc nowym dla mongodb i schematu repozytorium, nie widzę go od razu.

Dzięki

Odpowiedz

4

Natknęliśmy się na to pytanie podczas pracy nad uaktualnieniami mongotb. W odróżnieniu od tego, kiedy zadawano to pytanie, teraz mongodb wspomaga blokowanie poziomu dokumentu po wyjęciu z pudełka.

Od: http://docs.mongodb.org/manual/faq/concurrency/

„Jak granulowany są zamki w MongoDB

Zmieniono w wersji 3.0

Począwszy od wersji 3.0, MongoDB statki z silnikiem przechowywania WiredTiger, który korzysta optymistyczne kontroli współbieżności?. dla większości operacji odczytu i zapisu WiredTiger używa tylko blokad intencji na poziomie globalnym, bazy danych i poziomów zbierania danych Kiedy mechanizm magazynujący wykryje konflikty między dwiema operacjami, powstanie konflikt zapisu powodujący, że MongoDB będzie przezroczyście ponawiał operację. "

+3

Co jeśli chcę zablokować podczas tworzenia obiektu dokumentu? – Chinni

6

"Doktor, to boli kiedy robię ten"

"Wtedy nie zrobić to!"

Zasadniczo to, co opisujesz, brzmi, jakbyś miał tam zależność szeregową - MongoDB lub cokolwiek, twój algorytm ma punkt, w którym operacja musi być serializowana. To będzie nieodłączne wąskie gardło, a jeśli absolutnie musisz to zrobić, musisz przygotować jakiś semafor, żeby go chronić.

Tak więc, miejsce, w którym należy szukać, znajduje się w twoim algorytmie. Czy możesz to wyeliminować? Mógłbyś, na przykład, poradzić sobie z jakimś rodzajem rozwiązywania konfliktów, jak na przykład "pobierz rekord na lokalną" aktualizację, zapis sklepu ", aby po sklepie nowy rekord był tym, który dostał się na tym kluczu?

+0

I Charlie, dzięki za odpowiedzi. Nie rozumiem proponowanego rozwiązania konfliktu. Zgadzam się, że muszę zmienić mój algorytm i mogę sobie wyobrazić jakieś rozwiązanie, ale uważam, że musi być pewne uzgodnione rozwiązanie tego problemu. Wydaje mi się, że jest to klasyczny problem wielu ludzi używających mongodb (lub prawdopodobnie jakiejkolwiek bazy danych). Jeśli byłaby to aktualizacja pamięci, wiedziałbym, jak użyć muteksu, aby "zablokować" zmienną, którą chcę zaktualizować, tak aby tylko jeden wątek aktualizował ją na raz. Domyślam się, że moje pytanie brzmi: jak inni programiści zwykle radzą sobie z tą sytuacją? –

12

Hej, jedynym sposobem, o którym teraz myślę, jest dodanie parametru statusu i użycie operacji findAndModify(), która umożliwia atomową modyfikację dokumentu. Trochę wolniej, ale powinno wystarczyć.

Załóżmy, że dodajesz atrybut statusu, a po pobraniu dokumentu zmień status z "IDLE" na "PRZETWARZANIE". Następnie aktualizujesz dokument i zapisujesz go ponownie w kolekcji, aktualizując status ponownie do "IDLE".

przykładem Kod:

var doc = db.runCommand({ 
       "findAndModify" : "COLLECTION_NAME", 
       "query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"}, 
       "update" : {"$set" : {"status" : "RUNNING"} } 
}).value 

Zmiana COLLECTION_NAME i ID_DOCUMENT do właściwej wartości. Domyślnie findAndModify() zwraca starą wartość, co oznacza, że ​​wartość statusu będzie nadal IDLE po stronie klienta. Więc kiedy skończysz z aktualizacją, zapisz/zaktualizuj wszystko ponownie.

Należy tylko pamiętać, że można modyfikować tylko jeden dokument na raz.

Mam nadzieję, że to pomaga.

+0

Możesz użyć prostej aktualizacji() w tym samym celu, który jest oficjalnym rozwiązaniem oferowanym na stronie MongoDB: http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/ Główne powikłanie tego rozwiązania jest jednak kod, który musisz napisać dla przypadku, gdy aktualizacja się nie powiedzie. To znaczy. ponów próbę aktualizacji. W zależności od kodu może być konieczne wystąpienie dalszych komplikacji, aby uniknąć skutków ubocznych podczas ponownej próby itp. –

+0

W jaki sposób inny klient czeka na zwolnienie blokady? np. w jaki sposób można otrzymać powiadomienie, gdy "status" się zmieni? – slezica

+0

Co jeśli chcę zablokować podczas tworzenia obiektu dokumentu? – Chinni

2

Odpowiadając na moje własne pytanie, ponieważ znalazłem rozwiązanie podczas przeprowadzania badań w Internecie.

Myślę, że potrzebuję użyć Optimistic Concurency Control.

Polega na dodaniu znacznika czasu, skrótu lub innego unikalnego identyfikatora (wykorzystam UUID) do każdego dokumentu. Unikalny identyfikator musi być modyfikowany za każdym razem, gdy dokument jest modyfikowany. przed aktualizacją dokumentu zrobię coś takiego (w pseudo-kod):

var oldUUID = doc.uuid; 
doc.uuid = new UUID(); 
BeginTransaction(); 
if (GetDocUUIDFromDatabase(doc.id) == oldUUID) 
{ 
    SaveToDatabase(doc); 
    Commit(); 
} 
else 
{ 
    // Document was modified in the DB since we read it. We can't save our changes. 
    RollBack(); 
    throw new ConcurencyException(); 
} 
+0

Yup, to jedna z metod rozwiązywania konfliktów. –

+0

Możesz to zrobić, ale używając operatorów atomowych, niektóre z pozostałych odpowiedzi opisują prawdopodobnie to, czego potrzebujesz (i jest atomowa, jak chcesz). Oto dokumentacja: http://www.mongodb.org/display/DOCS/Atomic+Operations – will

2

Alternatywą jest do zrobienia in place update

dla Ex:

http://www.mongodb.org/display/DOCS/Updating#comment-41821928

db.users.update({ level: "Sourcerer" }, { '$push' : { 'inventory' : 'magic wand'} }, false, true); 

który popchnie "magiczną różdżkę" do wszystkich tablic inwentaryzacji użytkownika "Sourcerer" użytkownika. Aktualizacja każdego dokumentu/użytkownika ma charakter atomowy.

-1

Brzmi jak chcesz używać operatorów atomowych MongoDB za: http://www.mongodb.org/display/DOCS/Atomic+Operations

+0

Problem z operatorami atomowymi polega na tym, że tak naprawdę nie pomagają mi one, ponieważ używałem wzorców repozytoriów, więc miałem tylko Operacje CRUD w mojej dyspozycji. –

3

klasyczne rozwiązanie, gdy chcesz zrobić coś wątku bezpieczne jest stosowanie blokad (muteksy). To jest również nazywane pesymistyczne blokowanie w przeciwieństwie do optymistyczne blokowanie opisane here.

Istnieją scenariusze, w których bardziej skuteczne jest pesymistyczne zamykanie (więcej szczegółów: here). Jest również znacznie łatwiejsze do wdrożenia (główną trudnością optymistycznego blokowania jest odzyskanie po kolizji).

MongoDB nie zapewnia mechanizmu blokady. Ale to może być łatwo realizowane na poziomie aplikacji (czyli w kodzie):

  1. blokada Acquire
  2. Przeczytaj dokument
  3. Modyfikuj dokument
  4. Zapis dokument
  5. blokada Release

Granularność blokady może być różna: globalna, specyficzna dla kolekcji, specyficzna dla rekordu/dokumentu. Im bardziej szczegółowa blokada, tym mniejsza kara za wykonanie.

+0

Jak czekasz na zamek? – slezica

+0

Nabycie akcji blokującej zazwyczaj oczekuje na blokadę, jeśli jest trzymana przez inny wątek. –

+5

To nie działa w aplikacji z wieloma instancjami. – rickchristie

0

Jeśli masz system z> 1 serwerami, potrzebujesz blokady dystrybucyjnej.

Preferuję używać Hazelcast.

Podczas zapisywania można uzyskać blokadę Hazelcast według identyfikatora podmiotu, pobrać i zaktualizować dane, a następnie zwolnić blokadę.

Jako przykład: https://github.com/azee/template-api/blob/master/template-rest/src/main/java/com/mycompany/template/scheduler/SchedulerJob.java

wystarczy użyć lock.lock() zamiast lock.tryLock()

tutaj można zobaczyć, jak skonfigurować Hazelcast w kontekście wiosny:

https://github.com/azee/template-api/blob/master/template-rest/src/main/resources/webContext.xml

1

Aktualizacja: Z MongoDB 3.2.2 używając realizację WiredTiger Storage jako domyślnego silnika, MongoDB użycie domyślnego blokowania na level.It dokument został wprowadzony w wersji 3.0, ale wykonane domyślnie w wersji 3.2.2. Dlatego MongoDB ma teraz blokowanie na poziomie dokumentu.

0

Zamiast pisać na pytanie w innym pytanie, staram się odpowiedzieć na to jedno: Zastanawiam się, czy to WiredTiger bagażu zajmie się problemem zwróciłem się tutaj: Limit inserts in mongodb

Powiązane problemy