Memorizzazione nella cache mediante dizionari in memoria. Stiamo sbagliando tutto?

6

Questo approccio è praticamente il modo accettato di fare qualsiasi cosa nella nostra azienda. Un semplice esempio: quando un dato di un cliente viene richiesto da un servizio, recuperiamo tutti i dati per quel cliente (parte rilevante del servizio) e lo salviamo in un dizionario in memoria, quindi lo servono da lì in seguito alle richieste (gestiamo servizi singleton). Qualsiasi aggiornamento va a DB, quindi aggiorna il dizionario in memoria. Sembra tutto semplice e innocuo, ma quando implementiamo regole aziendali più complicate, la cache non è più sincronizzata e dobbiamo fare i conti con bug difficili da trovare. A volte rimandiamo la scrittura al database, mantenendo i nuovi dati nella cache fino ad allora. Ci sono casi in cui memorizziamo milioni di righe in memoria perché la tabella ha molte relazioni con altre tabelle e dobbiamo mostrare rapidamente i dati aggregati.

Tutta questa gestione della cache è una parte importante della nostra base di codice e sento che questo non è il modo giusto per farlo. Tutta questa giocoleria aggiunge troppo rumore al codice e rende difficile capire la reale logica di business. Tuttavia, non penso che possiamo fornire i dati in un ragionevole lasso di tempo se dovessimo colpire il database ogni volta.

Sono scontento della situazione attuale ma non ho un'alternativa migliore. La mia unica soluzione sarebbe quella di usare la cache di 2 ° livello di NHibernate ma non ho quasi nessuna esperienza con esso. So che molte aziende usano Redis o MemCached pesantemente per ottenere prestazioni, ma non ho idea di come vorrei integrarle nel nostro sistema. Inoltre, non so se riescono a ottenere prestazioni migliori rispetto alle strutture e alle query dei dati in memoria.

Esistono approcci alternativi che dovrei esaminare?

    
posta user73983 29.11.2012 - 22:49
fonte

1 risposta

8

Prima l'ultima domanda: perché Redis / memcached?

No, non sono (di solito) più veloci dei semplici dizionari in-process. Il vantaggio arriva quando si hanno diversi processi di lavoro o persino molte macchine a livello di app. In tal caso, invece di ogni processo che ha una propria piccola cache, tutti condividono un'unica grande cache (distribuita). Con cache più grandi, ottieni rapporti di hit migliori.

Come puoi vedere, il livello della cache diventa una risorsa condivisa, proprio come il database, ma (si spera) più velocemente.

Ora, per la maggior parte: come evitare il caos?

Sembra che il tuo problema sia mantenere la cache coerente mentre allo stesso tempo la disaccoppia dal database. Vedo tre punti dolenti lì:

  1. invalidazione della cache. Questo è solo difficile. A volte la soluzione più semplice è aggiungere un ID di generazione a ogni record e utilizzarlo come parte della chiave di cache. Quando i dati vengono aggiornati, si ottiene un nuovo ID di generazione e la successiva query della cache non viene eseguita, quindi si accede al database e si aggiorna la cache. Ovviamente, la voce (ora inutilizzata) deve avere una ragionevole scadenza, quindi alla fine viene eliminata dalla cache.

  2. writeback. Dici che lavori sulla cache e aggiorni il database più tardi. Questo è pericoloso; la maggior parte delle architetture evita questa idea. Un passo nella giusta direzione sarebbe quello di contrassegnare ogni voce nuova o modificata nella cache come "sporca", in modo che possa essere svuotata nel database mediante un processo disaccoppiato. Un'idea migliore potrebbe essere quella di aggiungere a una coda di messaggi non appena viene modificata, rendendo effettivamente la scrittura nel database "in linea ma asincrona". Alla fine, penso che dovresti capire che questo non è un uso valido per una cache, questa è una "area di staging" che dovrebbe essere trattata con un'architettura diversa da un livello di cache.

  3. sincronizzazione dei processi: poiché la cache in-process è privata per ogni processo, qualsiasi modifica non viene propagata ad altri processi finché non vengono scaricati nel database. Questo potrebbe essere corretto sotto il design della tua app (tipo di isolamento della transazione del povero uomo), ma potrebbe avere risultati non voluti. Un'architettura molto più gestibile è un livello di cache che è solo un'API più veloce per il database, con le stesse proprietà condivise del database e altrettanto autorevole come tale. Per questo hai bisogno di cache fuori processo, come memcached o Redis.

risposta data 29.11.2012 - 23:59
fonte

Leggi altre domande sui tag