2011-01-31 12 views
5

Częstym nieco logiki programowania znajdę się wdrożenie często jest czymś poniższym pseudo-kod:Synchronizacja bazy danych Access w Ukazuje App

Let X = some value 
Let Database = some external Database handle 

if !Database.contains(X): 
    SomeCalculation() 
    Database.insert(X) 

Jednak w wielowątkowym programie mamy sytuacji wyścigu tutaj. Wątek A może sprawdzić, czy X jest w Database, znaleźć, że nie jest, a następnie przejść do wywołania SomeCalculation(). W międzyczasie wątek B sprawdzi także, czy X znajduje się w Database, że nie jest i wstawi zduplikowany wpis.

Tak oczywiście, to musi być zsynchronizowane jak:

Let X = some value 
Let Database = some external Database handle 

LockMutex() 
if !Database.contains(X): 
    SomeCalculation() 
    Database.insert(X) 
UnlockMutex() 

To jest w porządku, z wyjątkiem tego, co w przypadku gdy wniosek jest rozproszoną aplikację, działa na wielu komputerach, z których wszystkie są połączone z tym samym Back koniec maszyny bazy danych? W tym przypadku Mutex jest bezużyteczny, ponieważ synchronizuje tylko jedno wystąpienie aplikacji z innymi lokalnymi wątkami. Aby to zadziałało, potrzebowalibyśmy jakiejś "globalnej" techniki rozproszonej synchronizacji. (Załóżmy, że proste blokowanie duplikatów w Database nie jest wykonalną strategią.)

Jakie są praktyczne rozwiązania tego problemu?

uświadamiam sobie to pytanie jest bardzo generic, ale nie chcę, aby to pytanie język specyficzny, ponieważ jest to problem, który pojawia się w wielu językach i wielu technologii baz danych.

Celowo uniknąłem określenia, czy mówię o RDBMS lub bazie danych SQL, w przeciwieństwie do czegoś takiego jak baza danych NoSQL, ponieważ znowu - szukam ogólnych odpowiedzi w oparciu o praktyki w branży. Na przykład, czy jest to sytuacja, którą może rozwiązać Atomic Stored Procedures? Lub transakcje atomowe? Czy jest to coś, co wymaga czegoś w rodzaju "Distributed Mutex"? Lub bardziej ogólnie, czy problem ten jest zazwyczaj rozwiązywany przez system baz danych, czy jest to coś, co powinna poradzić sobie sama Aplikacja?

Jeśli okaże się, że to jest niemożliwe, aby odpowiedzieć w ogóle bez dodatkowych informacji, proszę powiedz mi, że mogę go zmodyfikować.

Odpowiedz

0

Oczywiście można przenieść część "zsynchronizowaną" z samą warstwą DB, używając wyłącznego blokowania określonego zasobu.

To jest trochę ekstremalne (w większości przypadków próba wstawienia i zarządzania wyjątkiem, kiedy odkryjesz, że ktoś już wstawił wiersz) byłaby bardziej odpowiednia, jak sądzę.

2

Jednym ze sposobów zabezpieczenia danych przed stąpaniem jest zablokowanie wiersza danych. Wiele baz danych pozwala na to, za pośrednictwem transakcji. Niektóre nie obsługują transakcji.

Jest to jednak przesada w większości przypadków, gdy ogólny poziom rywalizacji jest niski. Możesz przeczytać na temat Isolation levels, aby uzyskać więcej informacji na ten temat.

Lepszym podejściem ogólnym jest często Optimistic Concurrency. Ideą jest to, że każdy wiersz danych zawiera sygnaturę, znacznik czasu działa dobrze, ale podpis nie musi być zorientowany na czas. Może to być na przykład wartość mieszania. Jest to ogólne podejście do zarządzania współbieżnością i nie jest ograniczone do magazynów relacyjnych.

Aplikacja zmieniająca dane najpierw odczytuje wiersz, a następnie wykonuje wszystkie wymagane obliczenia, a następnie w pewnym momencie zapisuje zaktualizowane dane z powrotem do magazynu danych. Poprzez współbieżność optymistyczną aplikacja zapisuje aktualizację z zastrzeżeniem (wyrażonym w SQL, jeśli jest to baza danych SQL), że wiersz danych musi zostać zaktualizowany tylko wtedy, gdy podpis nie został zmieniony w międzyczasie. Za każdym razem, gdy wiersz danych jest aktualizowany, podpis musi również zostać zaktualizowany.

Powoduje to, że aktualizacje nie są kompresowane. Ale dla bardziej rygorystycznego wyjaśnienia zagadnień współbieżności, odwołaj się do tego artykułu na temat poziomów izolacji DB.

Wszystkie aktualizacje rozproszone muszą być zgodne z konwencją OCC (lub czymś silniejszym, np. Blokowanie transakcyjne), aby to działało.

0

Cóż, ponieważ zadajesz ogólne pytanie, postaram się podać inną opcję. To nie jest zbyt ortodoksyjne, ale może być użyteczne: możesz "zdefiniować" maszynę lub proces odpowiedzialny za to. Na przykład:

Let X = some value 
Let Database = some external Database handle 

xResposible = Definer.defineResponsibleFor(X); 

if(xResposible == me) 
    if !Database.contains(X): 
     SomeCalculation() 
     Database.insert(X) 

Trick o to, aby defineResponsibleFor zawsze zwraca tę samą wartość niezależnie od tego, kto dzwoni. Tak więc, jeśli masz sprawiedliwy rozproszony zakres X i uczciwy Definer, wszystkie maszyny będą miały do ​​zrobienia. Możesz użyć prostego muteksa, aby uniknąć sytuacji wyścigowej. Oczywiście, teraz musisz zadbać o tolerancję na błędy (jeśli maszyna lub proces nie działa, twój Definer musi znać i nie definiować dla niego żadnej pracy). Ale powinieneś zrobić to anyawy ... :)

Powiązane problemy