Il presente articolo è stato tradotto automaticamente.
ASP.NET,
Come gestire dati relazionali in una Cache distribuita
Il Microsoft .net Framework è diventato popolare per lo sviluppo di una gamma di applicazioni di alto-transazione tra cui Web, service-oriented architecture (SOA), high-performance computing o grid computing e cloud computing. Tutte queste architetture di applicazione sono scalabili. Ma un grande collo di bottiglia si verifica nell'archivio dati — normalmente un database relazionale, che non è in grado di scala e di gestire il carico maggiore delle transazioni che il livello di applicazione può.
Di conseguenza, memorizzazione nella cache distribuita sta diventando molto popolare perché ti permette di memorizzare dati in una cache in memoria che è molto più rapido per accedere a qualsiasi database. Inoltre, memorizzazione nella cache distribuita fornisce scalabilità lineare tra più server di cache. Con scalabilità lineare, è possibile aggiungere ulteriori server di cache al cluster cache distribuita se è necessario gestire un grande carico di transazione. Gli incrementimentale guadagno in capacità transazionale non diminuisce se si aggiungono più server (è una linea retta in un grafico X-Y, dove x è il numero di server di cache e y è la capacità di transazione). Figura 1 illustrato come distribuite e memorizzazione nella cache si inserisce in una tipica applicazione ASP.NET o Windows Communication Foundation (WCF) e come esso fornisce scalabilità lineare.
Figura 1 Cache distribuita utilizzata in applicazioni Windows Communication Foundation o ASP.NET
In questo articolo, mi occuperò di come gli sviluppatori dovrebbero gestire relazioni di dati durante la memorizzazione nella cache dei dati.
Anche se la memorizzazione nella cache distribuita è grande, una sfida che presenta è come memorizzare nella cache i dati relazionali che ha varie relazioni tra gli elementi di dati. Questo è perché una cache distribuita vi fornisce un semplice, simile a Hashtable (chiave, valore) interfaccia dove la "chiave" identifica in modo univoco un elemento memorizzato nella cache e "valore" è il vostro dati o un oggetto. Tuttavia, l'applicazione ha probabilmente un modello a oggetti di dominio complesso con ereditarietà, aggregazione e altri tipi di relazioni.
Inoltre, una cache distribuita memorizza singoli elementi memorizzati nella cache separatamente, che è diverso dal database dove si hanno relazioni tra tabelle diverse. Di solito, non ci sono nessun rapportonavi tra i diversi elementi in una cache distribuita tipico memorizzati nella cache. Che rappresenta una sfida per gli sviluppatori di applicazioni .net. In altre parole, si sta affrontando la sfida di un sacco di informazioni all'interno dell'applicazione per assicurarsi che è possibile gestire relazioni correttamente nella cache e presso lo stesso vantaggio di prendere tempo di memorizzazione nella cache distribuita rapporto di monitoraggio.
Ti spiego come questi rapporti possono essere mappati e forniscono esempi di codice sorgente. L'effetto netto è che le applicazioni non devono tenere traccia di queste relazioni stesse. Piuttosto, la cache può venire a conoscenza dei loro e gestirli in modo indipendente.
Object Relation Mapping È buona
In primo luogo, assicurarsi che si trasformano i dati relazionali in un modello a oggetti di dominio. Senza questo passaggio, si avrebbe un tempo difficile gestire tutti i rapporti in una cache distribuita.
In qualsiasi applicazione .net, stai probabilmente usando DataReader (SqlDataReader, OracleDataReader o OleDbDataReader) o DataTable per recuperare i dati dal database, che è bene. Ma molti sviluppatori quindi accedere direttamente dati da questi oggetti (soprattutto DataTable) in tutta la loro applicazione. Alcuni lo fanno per pigrizia, perché non vogliono per creare oggetti di dominio personalizzato. Gli altri lo fanno perché credono che possono utilizzare le funzionalità di filtraggio intelligente nell'oggetto DataTable.
Consiglia vivamente di trasformare il vostro DataReader o datitavolo in un modello a oggetti di dominio. Questo semplifica notevolmente la progettazione dell'applicazione e consentono di utilizzare la cache distribuita in modo efficace. E una cache distribuita buona fornisce solitamente con un simil-SQL o LINQ query capacità così da non perdere le caratteristiche di filtraggio del DataTable.
Quando si utilizza un oggetto DataTable, si è costretti nella cache come un elemento memorizzato nella cache. Si noti che un gran numero di righe in un oggetto DataTable rallenta l'applicazione quando si memorizza nella cache.
È possibile trasformare il DataReader e DataTable in oggetti di dominio manualmente o utilizzare uno dei principali motori di Object Relation Mapping (ORM). Entity Framework di Microsoft è un tale motore. Un altro popolare è NHibernate, che è open source. Uno strumento ORM semplifica notevolmente il compito di trasformare i dati relazionali in un modello a oggetti di dominio.
CacheDependency aiuta a gestire le relazioni
Con un modello di oggetto di dominio, la prima domanda è come gestire tutte quelle relazioni nella cache. E la risposta è CacheDependency, che fa parte della Cache di ASP.NET e ora si trova anche in alcune soluzioni commerciali di memorizzazione nella cache distribuite.
CacheDependency consente di informare la cache su differisconoent tipi di relazioni tra elementi memorizzati nella cache e poi lasciare che la cache distribuita gestire l'integrità dei dati per loro. In sostanza, un CacheDependency consente di raccontare la cache che un elemento nella cache dipende da un altro elemento nella cache. Allora la cache distribuita può monitorare eventuali cambiamenti nell'elemento di destinazione e invalidare l'elemento di origine che dipende l'elemento di destinazione.
Diciamo che se punto dati a dipende da B, quindi se b è mai aggiornato o rimosso dalla cache, cache distribuita rimuove automaticamente a pure. CacheDependCity fornisce inoltre cascading capacità, quindi se a dipende da b e b dipende dalla c e poi c è aggiornato o rimosso, traduce in b viene automaticamente rimosso dalla cache distribuita. E quando ciò accade, A anche automaticamente viene rimosso dalla cache distribuita. Questo è chiamato il CSS CacheDependency.
Io uso CacheDependency più avanti in questo articolo per illustrare come gestire i rapporti in una cache distribuita.
Tre diversi tipi di relazioni
Prima di tutto, utilizziamo un modello di dati di esempio per scopi di discussione, mostrati Figura 2.
Modello di dati di esempio nella figura 2 per relazioni
Come si può vedere, in questo modello di dati, il cliente ha una relazione uno-a-molti con ordine; Prodotto ha una relazione uno-a-molti con ordine; e cliente e prodotto hanno una relazione molti-a-molti con l'altro attraverso la tabella ordini. Per il nostro modello di dati di esempio, Figura 3 Mostra il modello di oggetto equivalente che rappresenta le relazioni stesse.
Figura 3 esempio oggetto modello contro il modello di dati
Prima di andare nei dettagli dei tipi di rapporto diverso, voglio spiegare una cosa. A differenza del database, un modello di oggetti di dominio di applicazione ha sempre un oggetto primario che ha recuperato l'applicazione dal database e che quindi è il punto di partenza per l'applicazione durante una particolare transazione. Tutti gli altri oggetti sono visti come oggetto correlato a questo primario. Questo concetto è valido per tutti i tipi di relazioni e colpisce come si vede le relazioni in un modello di oggetto di dominio. Ora, procediamo.
Relazioni uno a uno e molti-a-uno
Sono simili relazioni uno a uno e molti-a-uno. In una relazione uno a uno tra tabella a e tabella B, una riga nella tabella a è correlata alla sola riga nella tabella B. Mantenete una chiave esterna nella tabella a o b che è la chiave primaria della tabella di altre.
Nel caso di una relazione molti-a-uno tra a e B, si deve tenere la chiave esterna nella tabella A. Questa chiave esterna è la chiave primaria della tabella B. Nelle relazioni uno a uno e molti-a-uno, la chiave esterna ha un vincolo unique per assicurarsi che non ci sono duplicati non ammessi.
Lo stesso rapporto può essere facilmente trasformato in un modello a oggetti di dominio. L'oggetto primario (spiegato in precedenza) mantiene un riferimento all'oggetto correlato. Figura 4 Mostra un esempio dell'oggetto primario, mantenendo le informazioni di relazione. Si noti che la classe Order contiene un riferimento alla classe cliente per indicare una relazione molti-a-uno. Lo stesso sarebbe accaduto anche in un rapporto uno a uno.
Figura 4 memorizzazione nella cache di un oggetto correlato separatamente
public void CacheOrder(Order order)
{
Cache cache = HttpRuntime.Cache;
DateTime absolutionExpiration = Cache.NoAbsoluteExpiration;
TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
CacheItemPriority priority = CacheItemPriority.Default;
if (order != null) {
// This will prevent Customer from being cached with Order
Customer cust = order.OrderingCustomer;
// Set orders to null so it doesn't get cached with Customer
cust.Orders = null;
string custKey = "Customer:CustomerId:" + cust.CustomerId;
cache.Add(custKey, cust, null,
absolutionExpiration,
slidingExpiration,
priority, null);
// Dependency ensures order is removed if Cust updated/removed
string[] keys = new string[1];
keys[0] = custKey;
CacheDependency dep = new CacheDependency(null, keys);
string orderKey = "Order:CustomerId:" + order.CustomerId
+ ":ProductId:" + order.ProductId;
// This will only cache Order object
cache.Add(orderKey, order, dep,
absolutionExpiration,
slidingExpiration,
priority, null);
}
}
Relazioni uno-a-molti (inverso di molti-a-uno)
Nel caso di una relazione uno-a-molti nel database tra le tabelle a e B, tabella B (cioè il lato"molti") mantiene la chiave esterna, che è in realtà la chiave primaria della tabella A, ma senza un vincolo unique sulla chiave esterna.
Nel caso di un modello di dominio di uno-a-molti oggetti, l'oggetto primario è il cliente e l'oggetto correlato è l'ordine. Così l'oggetto Customer contiene un insieme di oggetti Order. Figura 4 Mostra anche un esempio di questo rapporto tra i cliente e ordine gli oggetti.
Relazioni molti-a-molti
Nel caso di una relazione molti-a-molti tra le tabelle a e B, c'è sempre una tabella intermediaria AB. Nel nostro caso, l'ordine è la tabella intermediaria e ha due chiavi esterne. Uno è contro la tabella Customer per rappresentare una relazione molti-a-uno e l'altro è contro la tabella ordine per rappresentare ancora una relazione molti-a-uno.
Nel caso di una relazione molti-a-molti, il modello a oggetti è solitamente uno-a-molti dal punto di vista di un oggetto primario (definito in precedenza) che è cliente o prodotto. Il modello di oggetto contiene ora anche una relazione molti-a-uno tra l'oggetto intermediario (ordine, nel nostro caso) e l'altro oggetto (prodotto, nel nostro caso). Figura 4 Mostra anche un esempio di una relazione molti-a-molti, che in questo caso è tra cliente e prodotto oggetti, con l'oggetto ordine essendo un oggetto intermediario.
Come si può vedere, il modello a oggetti è dal punto di vista dell'oggetto Customer che è l'oggetto primario, e l'applicazione recuperati dal database. Tutti i relativi oggetti sono dal punto di vista di questo oggetto primario. Così un oggetto Customer avrà un insieme di oggetti Order, e ogni oggetto ordine conterrà un riferimento all'oggetto prodotto. L'oggetto prodotto probabilmente non deve contenere un insieme di tutti gli oggetti di ordine appartenenti all'oggetto prodotto poiché che non è necessario qui. Se il prodotto era l'oggetto primario, avrebbe un insieme di oggetti Order — ma se poi l'oggetto cliente avrebbe un insieme di oggetti Order.
Strategie di memorizzazione nella cache per i diversi tipi di relazione
Finora, ho discusso di come recuperare dati dal database, trasformarlo in un modello di oggetto di dominio e mantenere le relazioni stesse nel modello di dominio come il database — anche se dal punto di vista dell'oggetto primario. Ma se l'applicazione si vuole memorizzare dati in una cache distribuita, è necessario comprendere come gestire tutte queste relazioni nella cache. I'll go attraverso ogni caso.
In tutti i casi, è necessario assicurarsi che informazioni rapporto nella cache non sono perdute, che incidono o l'integrità dei dati o la capacità di recuperare gli oggetti correlati dalla cache più tardi.
Memorizzazione nella cache di relazioni uno a uno e molti-a-uno
Hai due opzioni qui:
- Cache oggetti con l'oggetto primario correlati: questa opzione presuppone che l'oggetto correlato non sta per essere modificato da un altro utente mentre è in cache, quindi è sicuro memorizzare nella cache come parte dell'oggetto primario come un elemento memorizzato nella cache. Se avete paura che questo non è il caso, allora non usare questa opzione.
- Cache oggetti correlati separatamente: questa opzione presuppone che l'oggetto correlato possa essere aggiornato da un altro utente mentre è in cache, quindi è meglio memorizzare nella cache gli oggetti primari e correlati come distinti elementi memorizzati nella cache. È necessario specificare le chiavi di cache univoco per ciascuno dei due oggetti. Inoltre, è possibile utilizzare la funzionalità di tag di una cache distribuita per contrassegnare l'oggetto correlato come avendo una relazione con l'oggetto primario. Poi si potrebbe recuperare in seguito attraverso l'etichetta.
Memorizzazione nella cache di uno-a-molti rapporti
Nel caso di relazioni uno-a-molti, l'oggetto primario è sempre dal lato"uno" (nel nostro esempio è l'oggetto Customer). L'oggetto primario contiene un insieme di oggetti Order. Ogni insieme di oggetti correlati rappresenta una relazione uno-a-molti. Qui avete tre opzioni di memorizzazione nella cache:
- Collezioni di oggetti correlati con l'oggetto primario di cache: questo ovviamente presuppone che gli oggetti correlati non verrà aggiornati o recuperati in modo indipendente da un altro utente mentre nella cache, quindi è sicuro memorizzare nella cache come parte dell'oggetto primario. In questo modo migliora le prestazioni perché è possibile recuperare tutto in chiamata una cache. Tuttavia, se l'insieme è di grande significato decine di migliaia di oggetti e raggiungendo in megabyte di dati, non otterrete il guadagno di prestazioni.
- Collezioni di oggetti correlati di cache separatamente: In questa situazione, si crede che altri utenti potrebbero voler recuperare la stesse raccolte dalla cache; Pertanto, ha senso per memorizzare nella cache l'insieme di oggetti correlati separatamente. Dovrebbe strutturare la chiave di cache in modo tale che sarete in grado di trovare questa raccolta sulla base di alcune informazioni circa l'oggetto primario. Tratterò la questione della memorizzazione nella cache di collezioni molto più dettagliatamente più avanti in questo articolo.
- Memorizzare nella cache tutti i singoli oggetti correlati da raccolte separatamente: In questa situazione, si crede che ogni singolo oggetto nell'insieme correlato possa essere aggiornato da altri utenti; quindi, voi non riesce a tenere un oggetto nell'insieme e deve memorizzare nella cache e separatamente. È possibile utilizzare la funzionalità di tag di una cache distribuita per identificare tutti gli oggetti in relazione con l'oggetto primario, così sarete in grado di recuperare la loro subito dopo, il.
Cache molti-a-molti rapporti
Davvero non esistono relazioni molti-a-molti in un modello di oggetto di dominio. Invece, essi siete rappresentati da relazioni uno-a-molti, con l'eccezione che l'oggetto intermediario (ordine, nel nostro caso) contiene un riferimento all'oggetto lato (prodotto, nel nostro caso). In un puro uno-a-molti, questo riferimento non esisterebbe.
Gestione delle collezioni
Il tema di come gestire collezioni è interessante, perché spesso recuperare un insieme di oggetti dal database e si desidera essere in grado di collezioni di cache in modo efficiente.
Supponiamo che abbiate un scenario dove richiesta tutti i vostri clienti di New York-based e non vi aspettate di eventuali nuovi clienti per essere aggiunto da New York il giorno successivo o in pochi minuti. Tenete a mente non sono cache dati per settimane e mesi, solo per un minuto o un paio d'ore nella maggior parte dei casi. In alcune situazioni potrebbe cache per molti giorni.
Ci sono diverse strategie per la memorizzazione nella cache di raccolte, di memorizzazione nella cache come spiegherò.
Un'intera collezione come un elemento di cache
In questo caso, sapete che voi non aggiungendo alcun più clienti da New York e che gli altri utenti non sono l'accesso e modifica i dati del cliente di New York durante il periodo in cui i dati verranno essere memorizzata nella cache. Pertanto, si può memorizzare nella cache l'intero insieme dei clienti di New York come un elemento memorizzato nella cache. Qui, potete fare i criteri di ricerca o la parte di query SQL della chiave della cache. Ogni volta che si desidera recuperare i clienti che vengono da New York, basta andare alla cache e dire, "Give me l'insieme che contiene i clienti di New York."
Figura 5 Mostra come un'intera collezione di oggetti correlati ordine viene memorizzato nella cache.
Figura 5 un insieme correlato di memorizzazione nella cache separatamente
public void CacheCustomer(Customer cust)
{
Cache cache = HttpRuntime.Cache;
DateTime absolutionExpiration = Cache.NoAbsoluteExpiration;
TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
CacheItemPriority priority = CacheItemPriority.Default;
if (cust != null)
{
string key = "Customer:CustomerId:" + cust.CustomerId;
// Let's preserve it to cache separately
IList<Order> orderList = cust.Orders;
// So it doesn't get cached as part of Customer
cust.Orders = null;
// This will only cache Customer object
cache.Add(key, cust, null,
absolutionExpiration,
slidingExpiration,
priority, null);
// See that this key is also Customer based
key = "Customer:CustomerId:" + cust.CustomerId + ":Orders";
cache.Add(key, orderList, null,
absolutionExpiration,
slidingExpiration,
priority, null);
}
}
Cache separatamente ogni elemento dell'insieme
Ora passiamo alla seconda strategia. In questo scenario, voi o altri utenti desidera individualmente recuperare e modificare un cliente di New York. Ma la strategia precedente richiederebbe tutti a recuperare l'intera raccolta dalla cache, modificare questo uno cliente, rimetterlo nella raccolta e nella cache l'insieme ancora una volta. Se fate questo abbastanza frequentemente, diventerà impraticabile per motivi di prestazioni.
Così, in questo caso, non tenere la raccolta di tutti i clienti di New York come un elemento memorizzato nella cache. È rompere la collezione e memorizzare ogni oggetto Customer separatamente nella cache. È necessario raggruppare tutti questi oggetti Customer così in un momento successivo, che è possibile recuperare solo i loro tutti indietro in una chiamata come un insieme o un oggetto IDictionary. Il vantaggio di questo approccio è in grado di recuperare e modificare i singoli oggetti Customer. Figura 6 Mostra un esempio di come ciascuno dei relativi oggetti nell'insieme nella cache separatamente.
Figura 6 cache separatamente ogni elemento dell'insieme
public void CacheOrdersListItems(IList<Order> ordersList)
{
Cache cache = HttpRuntime.Cache;
DateTime absolutionExpiration = Cache.NoAbsoluteExpiration;
TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
CacheItemPriority priority = CacheItemPriority.Default;
foreach (Order order in ordersList)
{
string key = "Order:CustomerId:" + order.CustomerId
+ ":ProductId" + order.ProductId;
string[] keys = new string[1];
keys[0] = key;
// Dependency ensures Order is removed if Cust updated/removed
CacheDependency dep = new CacheDependency(null, keys);
Tag [] tagList = new Tag [1];
tagList[0] = new Tag ("customerId" + order.CustomerId);
// Tag allows us to find order with a customerId alone
cache.Add(key, order, dep,
absolutionExpiration,
slidingExpiration,
priority, null, tagList);
}
}
Tieni presente, tuttavia, che questa strategia presuppone che non clienti di New York sono state aggiunte al database nel periodo in cui sono stati memorizzati. In caso contrario, quando si scarica tutti i clienti di New York dalla cache, si otterrà solo una lista parziale. Analogamente, se un cliente viene eliminato dal database, ma non dalla cache, avrai stantio elenco dei clienti.
Gestione delle raccolte dove gli oggetti vengono aggiunti o eliminati
La terza opzione per la gestione delle collezioni è dove pensate che nuovi clienti possono essere aggiunti da New York o qualche cliente esistente può essere eliminato. In questo caso, qualunque cosa si memorizza nella cache è solo dati parziali o vecchi dati. Forse la raccolta aveva solo 100 clienti e aggiunto due più oggi. Quei due non sarà parte della cache. Tuttavia, quando si scarica tutti i clienti da New York, si desidera che i dati corretti: volete 102 risultati, non 100.
Il modo per garantire che questo accade è di effettuare una chiamata al database. Ottenere tutti gli ID per i clienti e fare un confronto per vedere che quelli sono nella cache e quelli che non sono. Individualmente recuperare dal database di coloro che non sono nella cache e aggiungerli alla cache. Come potete vedere, questo non è un processo veloce; si sta facendo più chiamate di database e cache multiple. Con una cache distribuita che fornisce funzionalità di base, se avete una collezione di 1.000 clienti e vengono aggiunti 50 nuovi, si finirà per fare chiamate di database 51 e 101 cache distribuita. In questo caso, potrebbe essere più veloce solo per recuperare l'insieme dal database in una sola chiamata.
Ma se la cache distribuita fornisce operazioni di massa, si farebbe un database chiamata per recuperare ID, una cache distribuita chiamata per vedere quale ID esistono nella cache, una chiamata per aggiungere tutti i nuovi clienti per la cache e uno a recuperare l'intero insieme di clienti dalla cache. Questo sarebbe un totale di tre cache distribuita chiamate e chiamate in un database, e che non è male a tutti. E, se non nuovi clienti sono stati aggiunti (che sarebbe il caso il 90% del tempo), questo sarebbe ridotto a un database chiamata e due chiamate di cache distribuita.
Prestazioni e scalabilità
Ho ricoperto varie situazioni che si presentano quando l'applicazione recupera dati relazionali dal database, esso si trasforma in un modello di oggetto di dominio e quindi vuole memorizzare nella cache gli oggetti. Lo scopo del mio articolo è quello di evidenziare tutte quelle aree dove le relazioni tra oggetti possono influenzare come memorizzare nella cache gli oggetti e come successivamente modificare o rimuoverli dalla cache.
Memorizzazione nella cache distribuita è un ottimo modo per migliorare le prestazioni e la scalabilità dell'applicazione. E, poiché le applicazioni trattano con dati relazionali la maggior parte del tempo, spero che questo articolo ha fornito alcune informazioni sul come si dovrebbero gestire dati relazionali in una cache distribuita.
Iqbal Khan è il Presidente e la tecnologia evangelist di Alachisoft, che fornisce NCache e StorageEdge (alachisoft.com). NCache è una cache distribuita per .net e Java e migliora la scalabilità e le prestazioni dell'applicazione. StorageEdge è un fornitore RBS per SharePoint e aiuta a ottimizza l'archiviazione e le prestazioni di SharePoint. Khan ha ricevuto la sua laurea in informatica presso l'Università dell'Indiana, Bloomington, nel 1990. Contattarlo al iqbal@alachisoft.com.
Grazie all'esperto tecnica seguente per la revisione di questo articolo: Damian Edwards