2011-01-31 9 views
5

Un po 'comune logica di programmazione mi ritrovo attuazione spesso è qualcosa di simile al seguente pseudo-codice:Sincronizzazione database di Access in un Distributed App

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

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

Tuttavia, in un programma multi-threaded abbiamo una condizione di competizione Qui. Il thread A potrebbe controllare se è Database, trovare che non lo è, e quindi procedere a chiamare SomeCalculation(). Nel frattempo, la Discussione B controllerà anche se , , trova che non è, e inserire una voce duplicata.

Così, naturalmente, questo deve essere sincronizzata come:

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

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

Questo va bene, se non quello che se l'applicazione è un'applicazione distribuita, in esecuzione su più computer, i quali comunicano con lo stesso back macchina di database di fine? In questo caso, un Mutex è inutile, perché sincronizza solo una singola istanza dell'app con altri thread locali. Per fare questo, avremmo bisogno di una sorta di tecnica di sincronizzazione distribuita "globale". (Si supponga che i duplicati semplicemente non permettendo a Database non è una strategia praticabile.)

In generale, quali sono alcune soluzioni pratiche a questo problema?

mi rendo conto questa domanda è molto generica , ma io non voglio fare questa una domanda specifica per il linguaggio, perché questo è un problema che si presenta in più lingue e diverse tecnologie di database.

Ho volutamente evitato specificando se sto parlando di un RDBMS o SQL database, rispetto a qualcosa di simile a un database NoSQL, perché ancora una volta - Sto cercando risposte generalizzate basate su pratiche del settore. Ad esempio, questa situazione potrebbe essere risolta da Atomic Stored Procedure? O le transazioni atomiche? O è qualcosa che richiede qualcosa come un "Mutex distribuito"? O più in generale, questo problema viene generalmente risolto dal sistema di database o è qualcosa che l'applicazione stessa deve gestire?

Se risulta che questa domanda è impossibile rispondere a tutti senza ulteriori informazioni, per favore dimmi che posso modificarlo.

risposta

0

È ovviamente possibile spostare la parte "synch" sul livello DB stesso, utilizzando un blocco esclusivo su una risorsa specifica.

Questo è un po 'estremo (nella maggior parte dei casi, tentare l'inserimento e gestire l'eccezione quando si scopre che qualcuno ha già inserito la riga) sarebbe più adeguato, credo.

2

Un modo sicuro per evitare lo stomping dei dati consiste nel bloccare la riga di dati. Molti database ti permettono di farlo attraverso le transazioni. Alcuni non supportano le transazioni.

Tuttavia, questo è eccessivo per la maggior parte dei casi, in cui la contesa è bassa in generale. Si consiglia di leggere su Isolation levels per ottenere più sfondo sull'argomento.

Un approccio generale migliore è spesso Optimistic Concurrency. L'idea alla base è che ogni riga di dati include una firma, un timestamp funziona bene ma la firma non deve essere orientata al tempo. Ad esempio, potrebbe essere un valore hash. Questo è un approccio generale di gestione della concorrenza e non è limitato ai negozi relazionali.

L'app che modifica i dati prima legge la riga, quindi esegue tutti i calcoli necessari e quindi, a un certo punto, riporta i dati aggiornati all'archivio dati. Tramite la concorrenza ottimistica, l'app scrive l'aggiornamento con la clausola (espressa in SQL se si tratta di un database SQL) che la riga di dati deve essere aggiornata solo se la firma non è stata modificata nel frattempo. E ogni volta che una riga di dati viene aggiornata, anche la firma deve essere aggiornata.

Il risultato è che gli aggiornamenti non vengono calpestati. Ma per una spiegazione più rigorosa dei problemi di concorrenza, fare riferimento a quell'articolo sui livelli di isolamento di DB.

Tutti gli updaters distribuiti devono seguire la convenzione OCC (o qualcosa di più forte, come il blocco transazionale) affinché funzioni.

0

Bene, dal momento che si fa una domanda generale, cercherò di fornire un'altra opzione. Non è molto ortodosso, ma può essere utile: potresti "definire" una macchina o un processo responsabile di farlo. Per esempio:

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) 

Il trucco è quello di rendere defineResponsibleFor tornare sempre lo stesso valore indipendentemente chi sta chiamando. Quindi, se disponi di una gamma equa e distribuita di X e di un Definer equo, tutte le macchine avranno del lavoro da fare. E potresti usare il mutex di threading semplice per evitare condizioni di gara. Certo, ora devi occuparti della tolleranza ai guasti (se una macchina o un processo non è in funzione, il Definer deve conoscere e non definire alcun lavoro per esso). Ma dovresti farlo comunque ... :)

Problemi correlati