Il miglior approccio per scrivere su un database con due server web

0

Ho un problema di architettura / prestazioni.

Il problema:

  • Un'applicazionebasatasulvotocon:1loadbalancer,2serverwebeundatabase
  • 10candidatie200.000utentivotanti.
  • Possonoessercipiùdi100utentichevotanoallostessotempo.
  • Ildatabasepuòconteneresolo1scritturacontemporaneamenteenonsupportaletransazioni.Sequestovalorevienesuperato,ildatabaserispondeimmediatamenteconuncodicedierroreXXXXX.
  • Unutentedovrebbericevereunarispostaimmediatainbaseallaqualeèstataaccettatalavotazione.

Ladomanda,qualèl'approcciomiglioreperevitareall'utentediottenerel'erroreesostenereilcaricorichiesto?(senzamodificareildatabasesottostante)

PS:nonhopiùdettagli,èunproblemagenerico.

Soluzionepossibile:

  • Senonèpossibileaggiungerepiùrisorse,usaentrambeleWS,masolounascriveràsulBD.
  • Aggiungiunacodaesternapergestirelemissioni
  • UnodeiWSfunzioneràcomeun"Lavoratore", leggerà la coda e scriverà sul BD con concurrency = 1.
posta Fabio Cardoso 25.06.2016 - 11:11
fonte

5 risposte

2

Leggendo le specifiche, il punto principale del tuo requisito è:

An user should receive an immediate response that vote was accepted.

Per riuscirci, puoi separare vote-taking-part . Ciascuna delle due istanze deve parlare con un componente responsabile di ciò.

Quindi si presenta il problema, questo componente diventa il collo di bottiglia della tua architettura. Non avrebbe senso separare il traffico 50/50 su due server diversi che attendono entrambi su un componente usato / condiviso comune.

Ciò rende necessario che il componente sia in grado di fornire risposte veloci senza dover attendere (troppo tempo) su altri componenti.

I) Un modo corretto per ottenere risposte rapide verrebbe utilizzato (come hai già detto) in una coda di messaggi.

Il vote-taker consegna la richiesta di voto in entrata a queue e potrebbe rispondere immediatamente al cliente, che il suo voto è stato ricevuto.

Missione compiuta.

Non è necessario cambiare database.

II) A seconda delle tue richieste, potresti utilizzare alcuni negozi di valori-chiave in-memory come redis o hazelcast per prendere i voti. Il modo in cui trasferisci i dati nel tuo database è secondario.

III) Avendo introdotto Hazelcast , c'è anche la possibilità di usare il suo meccanismo di blocco per bloccare il componente in grado di scrivere sul db.

IV) a seconda delle richieste in tempo reale : LMAX forse qualcosa che stai cercando (su SE-Radio )

Ci sono molte possibilità.

    
risposta data 25.06.2016 - 18:37
fonte
2

The database can only hold 1 write concurrently and does not support transactions. If this value is exceeded, the database responds immediately with an error code XXXXX.

Questo è estremamente bizzarro per qualsiasi database moderno, ma accettiamo la premessa.

Ad un livello superiore, ci sono 2 approcci alle modifiche simultanee dei dati:

  1. Pessimistico - Questo è basato sul blocco, che acquisisce un blocco su una risorsa prima di modificarlo e rilascia il blocco in seguito. Se un altro processo ha quella risorsa bloccata, le richieste per acquisire un blocco su quella stessa risorsa saranno block . Questo in pratica serializza gli aggiornamenti a questa risorsa. Sono intenzionalmente vago quando dico risorse, perché è piuttosto ambiguo. In modo semplicistico, una risorsa di solito è una riga in una tabella di database, ma può anche essere pagine / blocchi, intervalli o intere tabelle (le specifiche variano da un DBMS all'altro).

  2. Ottimistico - I lock non vengono mai acquisiti, ma quando si aggiornano i dati, c'è una implicita it has it-not-changed-since-i-read-it e se la stipulazione è violata, verrà generato un errore. (A volte questo è implementato confrontando letteralmente vecchi valori con nuovi valori, o confrontando un codice hash, il numero di versione, ecc. I dettagli sono irrilevanti a questo livello).

Analisi del problema

Dato che si sta verificando un errore negli inserimenti simultanei, sospetto che tu abbia o meno l'ultimo, una strategia di concorrenza ottimistica in atto, o che tu abbia qualche DBW stupido che semplicemente non supporta gli aggiornamenti simultanei di sorta. Se si utilizza la concorrenza ottimistica, quindi, che è stata impostata, è un caso di utilizzo inadeguato. L'ottimista è, beh, ottimista. Si presuppone che gli aggiornamenti e le modifiche sovrapposte siano rari, quindi si ottimizza per il caso comune (lettura molto efficiente) a scapito di casi rari (modifiche in conflitto). Se si verifica un collo di bottiglia in cui gli inserimenti non funzionano regolarmente a causa di un caso del genere, il rapporto lettura-scrittura è più adatto al blocco pessimistico.

Breve storia, qualunque sia la ragione, la soluzione è la stessa: questi inserti devono essere serializzati. Alcune di queste risposte su questa domanda suggeriscono code di messaggi o dropping su un singolo server (il che, a proposito, non credo che risolverà davvero il problema ...), usando Hazelcast per fare serrature cross-server, ecc. sono tutte varianti della stessa identica cosa: serializza gli inserti!

Soluzione consigliata

Il fatto è che qualsiasi DBMS compatibile con ACID gestirà questo automaticamente per te. Se si sta utilizzando un DBMS compatibile con ACID, ma utilizzando la concorrenza ottimistica (una sorta di campo version , probabilmente?), Si consiglia di farlo. Passa alla semplice insert / update / delete senza alcuna versione e verifica se questo risolve il tuo problema.

Se non si utilizza un DBMS compatibile con ACID, la mia raccomandazione è di passare a un database migliore. Sì, puoi risolverlo con altri mezzi (come Hazelcast, coda di messaggi, ecc.), Ma aggiungono singoli punti di errore o reinventano il blocco del database. Francamente, questo mi spaventa. Il corretto blocco del database è un argomento estremamente complicato con gli eserciti di ingegneri che lo hanno progettato da PhD. Vorrei mai voler re-inventarlo da solo. Questo è uno spreco del mio tempo. Sarebbe più economico cambiare i database.

Oltre a

Per domande come questa, sarebbe bene specificare il software del database che stai usando. Sono scettico sul fatto che il tuo database abbia davvero questa limitazione. È come un'autostrada che consente solo un'auto alla volta.

    
risposta data 25.06.2016 - 19:45
fonte
1

Non ha senso introdurre un cluster di server di app fronteggiato da un sistema di bilanciamento del carico, se tutti sono puntati su un "database" comune che non è progettato per gestire la concorrenza. Non lo chiamerei affatto un database perché questo implica che gestisce la concorrenza come ci si aspetta da un moderno DBMS.

Poiché il requisito è che non è possibile modificare quella parte dell'architettura, è necessario aggiungere funzionalità ai server di app per gestire la concorrenza. La mia ipotesi è che quando diciamo "votando" ciò significa che qualsiasi cosa viene votata, ogni voto non dipende da come gli altri stanno votando. Ad esempio, quando una persona prende un voto per X, lo fa indipendentemente dal numero di altre persone che stanno votando per X. Se questa ipotesi non è vera, allora c'è molto più lavoro necessario.

Se i voti sono espressi senza la dipendenza sopra menzionata, allora è abbastanza semplice. All'interno dell'app Web si assicura che sia in grado di gestire il numero di sessioni che si prevede (100) e il lavoro di ciascuna sessione è di accedere all'utente, accettare il proprio voto e quindi passare il voto al back-end. La sessione webapp deve gestire la possibilità che l'errore ritorni e riprovare semplicemente fino al successo. Non importa quale sia l'ordine o il momento specifico in cui i voti entrano, è solo importante che ognuno sia infine ricevuto e contato. Naturalmente, è necessario che sia presente la registrazione di ciascuna sessione e dei voti in modo che i voti possano essere riconciliati e assicurarsi che i voti acquisiti alla fine siano coerenti con i voti inviati dalle sessioni di app Web.

Se l'ipotesi che ho menzionato non è vera e il modo in cui le persone votano ha una dipendenza dagli altri voti, allora l'app web deve gestire la comunicazione e il blocco delle sessioni inter. Non ci vuole molto tempo per prendere ogni voto e scriverlo sul back-end, ma l'app web deve garantire che solo una sessione stia scrivendo e tutte le altre sessioni siano bloccate mentre accade, e assicurarsi che tutto le sessioni hanno "visto" il voto appena lanciato prima di consentire a qualsiasi altra sessione di procedere. Ci sono molti modi per farlo, ma sarei tentato di utilizzare un DB embedded leggero che supporti le transazioni, la registrazione e la scrittura simultanea, ma poi trasferire tutti i voti al back-end al termine del voto.

Non guarderei all'accodamento dei messaggi perché se ogni voto potrebbe cambiare in base agli altri voti, l'accodamento dei messaggi non ti darà questo. È solo uno spooler che garantisce l'accettazione del voto e assicura che venga elaborato. Non penso che tu abbia bisogno di quel livello di complessità per prendere voti semplici, perché non ci vuole molto tempo per scrivere ogni voto sul back-end e gestire l'errore e riprovare sarà sufficiente.

    
risposta data 26.06.2016 - 17:30
fonte
0

The database can only hold 1 write concurrently and does not support transactions

La mancanza di transazioni è il problema principale. Suggerirei:

  1. Se possibile, aggiorna a un RDBMS diverso capace di transazione, ad es. Postgres . hai davvero bisogno ACID proprietà (o almeno un RDBMS capace di bloccare), quindi è l'unica soluzione onesta e professionale.

  2. altrimenti, "simula" un meccanismo di transazione o "serializzazione". Se il tuo codice è in esecuzione su Linux, potresti avvolgere il codice facendo la richiesta attorno ad un semaforo condiviso (vedi sem_overview (7) ...) o alcuni file lock (vedi lockf (3) o flock (2) ) sullo stesso file (se entrambi i server Web sono eseguiti sullo stesso computer) o hanno altri processi che forniscono tale sincronizzazione.

  3. Avere un modo per rilevare le richieste web che dovrebbero scrivere nel database e reindirizzare tutte a stesso server web (ad esempio quello a sinistra sul figura) all'interno del servizio di bilanciamento del carico.

  4. Rimuovi il bilanciamento del carico e uno dei server web, quindi fai in modo che un singolo websever parli al DBMS.

Essere consapevoli del fatto che la mancanza di transazioni (o almeno di blocchi) nell'RDBMS rallenterebbe significativamente il sito Web (assumendo che il costo dell'ACID-in RDBMS sia trascurabile); fondamentalmente, soluzioni 2 e amp; 3 equivalgono a (4) disabilitare il bilanciamento del web e uno dei server Web.

    
risposta data 25.06.2016 - 12:24
fonte
0

proprio come gli altri risponditori, mi piacerebbe davvero sapere quale sia il database.

per rispondere alla tua domanda, vorrei utilizzare un Distributed Lock Manager per garantire che solo un processo possa scrivere a questo database ... ZooKeeper è una delle implementazioni più popolari al momento in cui scrivi questo ... Redis RedLock sembra molto promettente.

    
risposta data 27.06.2016 - 00:56
fonte

Leggi altre domande sui tag