Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
La memorizzazione nella cache è una tecnica comune che ha l'obiettivo di migliorare le prestazioni e la scalabilità del sistema. Memorizza nella cache i dati copiando temporaneamente i dati a cui si accede di frequente in una risorsa di archiviazione veloce che si trova vicino all'applicazione. Se questa risorsa di archiviazione rapida dei dati si trova più vicina all'applicazione rispetto all'origine iniziale, la memorizzazione nella cache può migliorare significativamente i tempi di risposta per le applicazioni client fornendo i dati più rapidamente.
La memorizzazione nella cache è più efficace quando un'istanza client legge ripetutamente gli stessi dati, soprattutto se tutte le condizioni seguenti si applicano all'archivio dati originale:
- Rimane relativamente statico.
- È lento rispetto alla velocità della cache.
- È soggetto a un elevato livello di contesa.
- È lontano quando la latenza di rete può causare un rallentamento dell'accesso.
Memorizzazione nella cache nelle applicazioni distribuite
Le applicazioni distribuite implementano in genere una o entrambe le strategie seguenti durante la memorizzazione nella cache dei dati:
- Usano una cache privata, in cui i dati vengono mantenuti localmente nel computer che esegue un'istanza di un'applicazione o di un servizio.
- Usano una cache condivisa, che funge da origine comune a cui è possibile accedere da più processi e computer.
In entrambi i casi, la memorizzazione nella cache può essere eseguita sul lato client e sul lato server. La memorizzazione nella cache lato client viene eseguita dal processo che fornisce l'interfaccia utente per un sistema, ad esempio un Web browser o un'applicazione desktop. La memorizzazione nella cache lato server viene eseguita dal processo che fornisce i servizi aziendali in esecuzione in remoto.
Memorizzazione nella cache privata
Il tipo di cache più semplice è un archivio in memoria. Si trova nello spazio degli indirizzi di un singolo processo ed è accessibile direttamente dal codice eseguito in tale processo. Questo tipo di cache è rapido da accedere. Può anche offrire un mezzo efficace per archiviare quantità modeste di dati statici. Le dimensioni di una cache sono in genere vincolate dalla quantità di memoria disponibile nel computer che ospita il processo.
Se è necessario memorizzare nella cache più informazioni di quanto sia fisicamente possibile in memoria, è possibile scrivere dati memorizzati nella cache nel file system locale. Questo processo è più lento ad accedere rispetto ai dati contenuti in memoria, ma dovrebbe essere ancora più veloce e affidabile rispetto al recupero dei dati in una rete.
Se si dispone di più istanze di un'applicazione che usa questo modello in esecuzione simultaneamente, ogni istanza dell'applicazione ha una cache indipendente che contiene la propria copia dei dati.
Si pensi a una cache come uno snapshot dei dati originali in un determinato momento del passato. Se questi dati non sono statici, è probabile che diverse istanze dell'applicazione contengano versioni diverse dei dati nella cache. Pertanto, la stessa query eseguita da queste istanze può restituire risultati diversi, come illustrato nella figura 1.
Figura 1: Uso di una cache in memoria in istanze diverse di un'applicazione.
Memorizzazione nella cache condivisa
Se si usa una cache condivisa, può contribuire ad alleviare le preoccupazioni che i dati potrebbero differire in ogni cache, che possono verificarsi con la memorizzazione nella cache in memoria. La memorizzazione nella cache condivisa garantisce che diverse istanze dell'applicazione visualizzino la stessa visualizzazione dei dati memorizzati nella cache. Individua la cache in una posizione separata, in genere ospitata come parte di un servizio separato, come illustrato nella figura 2.
Figura 2: Uso di una cache condivisa.
Un importante vantaggio dell'approccio alla memorizzazione nella cache condivisa è la scalabilità che offre. Molti servizi di cache condivisa vengono implementati usando un cluster di server e usano software per distribuire i dati nel cluster in modo trasparente. Un'istanza dell'applicazione invia semplicemente una richiesta al servizio cache. L'infrastruttura sottostante determina la posizione dei dati memorizzati nella cache nel cluster. È possibile ridimensionare facilmente la cache aggiungendo altri server.
Esistono due svantaggi principali dell'approccio alla memorizzazione nella cache condivisa:
- L'accesso alla cache è più lento perché non viene più mantenuto in locale in ogni istanza dell'applicazione.
- Il requisito di implementare un servizio cache separato potrebbe aggiungere complessità alla soluzione.
Considerazioni sull'uso della memorizzazione nella cache
Le sezioni seguenti descrivono in modo più dettagliato le considerazioni relative alla progettazione e all'uso di una cache.
Decidere quando memorizzare i dati nella cache
La memorizzazione nella cache può migliorare notevolmente le prestazioni, la scalabilità e la disponibilità. Maggiore è il numero di dati disponibili e maggiore è il numero di utenti che devono accedere a questi dati, maggiore è il vantaggio della memorizzazione nella cache. La memorizzazione nella cache riduce la latenza e la contesa associata alla gestione di grandi volumi di richieste simultanee nell'archivio dati originale.
Ad esempio, un database potrebbe supportare un numero limitato di connessioni simultanee. Il recupero di dati da una cache condivisa, tuttavia, anziché dal database sottostante, consente a un'applicazione client di accedere a questi dati anche se il numero di connessioni disponibili è attualmente esaurito. Inoltre, se il database non è più disponibile, le applicazioni client potrebbero continuare usando i dati contenuti nella cache.
Prendere in considerazione la memorizzazione nella cache dei dati letti di frequente ma modificati raramente, ad esempio dati con una percentuale più elevata di operazioni di lettura rispetto alle operazioni di scrittura. Non è tuttavia consigliabile usare la cache come archivio autorevole di informazioni critiche. Assicurarsi invece che tutte le modifiche che l'applicazione non possa permettersi di perdere vengono sempre salvate in un archivio dati permanente. Se la cache non è disponibile, l'applicazione può continuare a funzionare usando l'archivio dati e non si perderanno informazioni importanti.
Determinare come memorizzare nella cache i dati in modo efficace
La chiave per l'uso di una cache consiste nel determinare i dati più appropriati da memorizzare nella cache e memorizzarli nella cache nel momento appropriato. I dati possono essere aggiunti alla cache su richiesta la prima volta che vengono recuperati da un'applicazione. L'applicazione deve recuperare i dati una sola volta dall'archivio dati e che l'accesso successivo può essere soddisfatto usando la cache.
In alternativa, una cache può essere parzialmente o completamente popolata con i dati in anticipo, in genere all'avvio dell'applicazione (un approccio noto come seeding). Tuttavia, potrebbe non essere consigliabile implementare il seeding per una cache di grandi dimensioni perché questo approccio può imporre un carico improvviso e elevato nell'archivio dati originale all'avvio dell'esecuzione dell'applicazione.
Spesso un'analisi dei modelli di utilizzo consente di decidere se precompilare completamente o parzialmente una cache e scegliere i dati da memorizzare nella cache. Ad esempio, è possibile eseguire il seeding della cache con i dati del profilo utente statico per i clienti che usano regolarmente l'applicazione (ad esempio ogni giorno), ma non per i clienti che usano l'applicazione solo una volta alla settimana.
La memorizzazione nella cache funziona in genere correttamente con i dati non modificabili o che cambiano raramente. Gli esempi includono informazioni di riferimento, ad esempio informazioni sui prodotti e sui prezzi in un'applicazione di e-commerce o risorse statiche condivise costose da costruire. Alcuni o tutti questi dati possono essere caricati nella cache all'avvio dell'applicazione per ridurre al minimo la domanda sulle risorse e migliorare le prestazioni. È anche possibile avere un processo in background che aggiorna periodicamente i dati di riferimento nella cache per assicurarsi che sia up-to-date. In alternativa, il processo in background può aggiornare la cache quando i dati di riferimento cambiano.
La memorizzazione nella cache è meno utile per i dati dinamici, anche se esistono alcune eccezioni a questa considerazione.Per altre informazioni, vedere la sezione Memorizzare nella cache dati altamente dinamici più avanti in questo articolo. Quando i dati originali cambiano regolarmente, le informazioni memorizzate nella cache diventano non aggiornate rapidamente o il sovraccarico della sincronizzazione della cache con l'archivio dati originale riduce l'efficacia della memorizzazione nella cache.
Una cache non deve includere i dati completi per un'entità. Ad esempio, se un elemento dati rappresenta un oggetto multivalore, ad esempio un cliente bancario con un nome, un indirizzo e un saldo del conto, alcuni di questi elementi potrebbero rimanere statici, ad esempio il nome e l'indirizzo. Altri elementi, ad esempio il saldo del conto, potrebbero essere più dinamici. In queste situazioni, può essere utile memorizzare nella cache le parti statiche dei dati e recuperare (o calcolare) solo le informazioni rimanenti quando sono necessarie.
È consigliabile eseguire test delle prestazioni e analisi dell'utilizzo per determinare se il caricamento prepopolato o su richiesta della cache o una combinazione di entrambi è appropriato. La decisione deve essere basata sulla volatilità e sul modello di utilizzo dei dati. L'utilizzo della cache e l'analisi delle prestazioni sono importanti nelle applicazioni che riscontrano carichi elevati e devono essere altamente scalabili. Negli scenari altamente scalabili, ad esempio, è possibile eseguire il seeding della cache per ridurre il carico nell'archivio dati nei momenti di picco.
La memorizzazione nella cache può essere usata anche per evitare calcoli ripetuti mentre l'applicazione è in esecuzione. Se un'operazione trasforma i dati o esegue un calcolo complesso, può salvare i risultati dell'operazione nella cache. Se in seguito è necessario lo stesso calcolo, l'applicazione può semplicemente recuperare i risultati dalla cache.
Un'applicazione può modificare i dati contenuti in una cache. È tuttavia consigliabile considerare la cache come archivio dati temporaneo che potrebbe scomparire in qualsiasi momento. Non archiviare dati preziosi solo nella cache; assicurarsi di mantenere anche le informazioni nell'archivio dati originale. Ciò significa che se la cache non è più disponibile, è possibile ridurre al minimo la possibilità di perdere dati.
Memorizzare nella cache dati altamente dinamici
Quando si archiviano informazioni in rapida modifica in un archivio dati permanente, può comportare un sovraccarico nel sistema. Si consideri, ad esempio, un dispositivo che segnala continuamente lo stato o altre misurazioni. Se un'applicazione sceglie di non memorizzare nella cache questi dati sulla base del fatto che le informazioni memorizzate nella cache saranno quasi sempre obsolete, la stessa considerazione potrebbe essere vera quando si archiviano e recuperano queste informazioni dall'archivio dati. Nel tempo necessario per salvare e recuperare questi dati, potrebbe essere stato modificato.
In una situazione di questo tipo, considerare i vantaggi dell'archiviazione delle informazioni dinamiche direttamente nella cache anziché nell'archivio dati permanente. Se i dati non sono critici e non richiedono il controllo, non è importante se la modifica occasionale viene persa.
Gestire la scadenza dei dati in una cache
Nella maggior parte dei casi, i dati contenuti in una cache sono una copia dei dati contenuti nell'archivio dati originale. I dati nell'archivio dati originale potrebbero cambiare dopo essere stati memorizzati nella cache, causando la disattivazione dei dati memorizzati nella cache. Molti sistemi di memorizzazione nella cache consentono di configurare la cache per la scadenza dei dati e ridurre il periodo per cui i dati potrebbero non essere aggiornati.
Quando i dati memorizzati nella cache scadono, vengono rimossi dalla cache e l'applicazione deve recuperare i dati dall'archivio dati originale (può riportare le informazioni appena recuperate nella cache). È possibile impostare un criterio di scadenza predefinito quando si configura la cache. In molti servizi della cache, è anche possibile stabilire il periodo di scadenza per singoli oggetti quando vengono archiviati a livello di codice nella cache. Alcune cache consentono di specificare il periodo di scadenza come valore assoluto o come valore scorrevole che fa sì che l'elemento venga rimosso dalla cache se non è accessibile entro l'ora specificata. Questa impostazione esegue l'override di tutti i criteri di scadenza a livello di cache, ma solo per gli oggetti specificati.
Note
Prendere in considerazione il periodo di scadenza per la cache e gli oggetti che contiene attentamente. Se si rende troppo breve, gli oggetti scadono troppo rapidamente e si riducono i vantaggi derivanti dall'uso della cache. Se si rende troppo lungo il periodo, si rischia che i dati diventino obsoleti.
È anche possibile che la cache venga riempita se i dati possono rimanere residenti per molto tempo. In questo caso, le richieste di aggiunta di nuovi elementi alla cache potrebbero causare la rimozione forzata di alcuni elementi in un processo noto come rimozione. I servizi di cache in genere eliminano i dati in modo meno recente, ma in genere è possibile eseguire l'override di questo criterio e impedire che gli elementi vengano rimossi. Tuttavia, se si adotta questo approccio, si rischia di superare la memoria disponibile nella cache. Un'applicazione che tenta di aggiungere un elemento alla cache avrà esito negativo con un'eccezione.
Alcune implementazioni di memorizzazione nella cache potrebbero fornire criteri di rimozione aggiuntivi. Esistono diversi tipi di criteri di rimozione. Questi includono:
- Un criterio usato più di recente (in attesa che i dati non vengano nuovamente richiesti).
- Un criterio first-in-first-out (i dati meno recenti vengono rimossi per primi).
- Criteri di rimozione espliciti basati su un evento attivato, ad esempio i dati da modificare.
Invalidare i dati in una cache lato client
I dati contenuti in una cache sul lato client vengono in genere considerati esterni agli auspici del servizio che fornisce i dati al client. Un servizio non può forzare direttamente un client ad aggiungere o rimuovere informazioni da una cache lato client.
Ciò significa che è possibile che un client usi una cache non configurata correttamente per continuare a usare informazioni obsolete. Ad esempio, se i criteri di scadenza della cache non sono implementati correttamente, un client potrebbe usare informazioni obsolete memorizzate nella cache in locale quando le informazioni nell'origine dati originale sono state modificate.
Se si compila un'applicazione Web che gestisce i dati tramite una connessione HTTP, è possibile forzare implicitamente un client Web (ad esempio un browser o un proxy Web) a recuperare le informazioni più recenti. Questa operazione può essere eseguita se una risorsa viene aggiornata da una modifica nell'URI della risorsa. I client Web usano in genere l'URI di una risorsa come chiave nella cache lato client, quindi se l'URI cambia, il client Web ignora le versioni precedentemente memorizzate nella cache di una risorsa e recupera invece la nuova versione.
Gestione della concorrenza in una cache
Le cache sono spesso progettate per essere condivise da più istanze di un'applicazione. Ogni istanza dell'applicazione può leggere e modificare i dati nella cache. Di conseguenza, gli stessi problemi di concorrenza che si verificano con qualsiasi archivio dati condiviso si applicano anche a una cache. In una situazione in cui un'applicazione deve modificare i dati contenuti nella cache, potrebbe essere necessario assicurarsi che gli aggiornamenti eseguiti da un'istanza dell'applicazione non sovrascrivano le modifiche apportate da un'altra istanza.
A seconda della natura dei dati e della probabilità di collisioni, è possibile adottare uno dei due approcci per la concorrenza:
- Optimistic. Immediatamente prima dell'aggiornamento dei dati, l'applicazione verifica se i dati nella cache sono stati modificati dopo il recupero. Se i dati sono ancora uguali, è possibile apportare la modifica. In caso contrario, l'applicazione deve decidere se aggiornarla. La logica di business che guida questa decisione è specifica dell'applicazione. Questo approccio è adatto per situazioni in cui gli aggiornamenti sono poco frequenti o in cui è improbabile che si verifichino conflitti.
- Pessimistic. Quando recupera i dati, l'applicazione lo blocca nella cache per impedire la modifica di un'altra istanza. Questo processo garantisce che i conflitti non possano verificarsi, ma possono anche bloccare altre istanze che devono elaborare gli stessi dati. La concorrenza pessimistica può influire sulla scalabilità di una soluzione ed è consigliata solo per le operazioni di breve durata. Questo approccio potrebbe essere appropriato per le situazioni in cui le collisioni sono più probabili, soprattutto se un'applicazione aggiorna più elementi nella cache e deve assicurarsi che queste modifiche vengano applicate in modo coerente.
Implementare disponibilità elevata e scalabilità e migliorare le prestazioni
Evitare di usare una cache come repository primario di dati; si tratta del ruolo dell'archivio dati originale da cui viene popolata la cache. L'archivio dati originale è responsabile della persistenza dei dati.
Prestare attenzione a non introdurre dipendenze critiche sulla disponibilità di un servizio cache condivisa nelle soluzioni. Un'applicazione deve essere in grado di continuare a funzionare se il servizio che fornisce la cache condivisa non è disponibile. L'applicazione non deve rispondere o non rispondere durante l'attesa della ripresa del servizio cache.
Pertanto, l'applicazione deve essere preparata per rilevare la disponibilità del servizio cache ed eseguire il fallback all'archivio dati originale se la cache non è accessibile. Il modello diCircuit-Breaker è utile per gestire questo scenario. Il servizio che fornisce la cache può essere recuperato e, una volta reso disponibile, la cache può essere ripopolata quando i dati vengono letti dall'archivio dati originale, seguendo una strategia come il modello Cache-aside.
Tuttavia, la scalabilità del sistema potrebbe essere influenzata se l'applicazione esegue il fallback all'archivio dati originale quando la cache è temporaneamente non disponibile. Durante il recupero dell'archivio dati, l'archivio dati originale potrebbe essere invaso da richieste di dati, con conseguente timeout e connessioni non riuscite.
Prendere in considerazione l'implementazione di una cache locale e privata in ogni istanza di un'applicazione, insieme alla cache condivisa a cui accedono tutte le istanze dell'applicazione. Quando l'applicazione recupera un elemento, può prima archiviare nella cache locale, quindi nella cache condivisa e infine nell'archivio dati originale. La cache locale può essere popolata usando i dati nella cache condivisa o nel database se la cache condivisa non è disponibile.
Questo approccio richiede un'attenta configurazione per impedire che la cache locale diventi troppo obsoleta rispetto alla cache condivisa. Tuttavia, la cache locale funge da buffer se la cache condivisa non è raggiungibile. La figura 3 mostra questa struttura.
Figura 3: Uso di una cache privata locale con una cache condivisa.
Per supportare cache di grandi dimensioni che contengono dati di durata relativamente lunga, alcuni servizi della cache offrono un'opzione a disponibilità elevata che implementa il failover automatico se la cache non è disponibile. Questo approccio comporta in genere la replica dei dati memorizzati nella cache archiviati in un server della cache primaria in un server cache secondario e il passaggio al server secondario se il server primario non riesce o la connettività viene persa.
Per ridurre la latenza associata alla scrittura in più destinazioni, la replica nel server secondario può verificarsi in modo asincrono quando i dati vengono scritti nella cache nel server primario. Questo approccio comporta la possibilità che alcune informazioni memorizzate nella cache vadano perse in caso di errore, ma la proporzione di questi dati deve essere ridotta rispetto alle dimensioni complessive della cache.
Se una cache condivisa è di grandi dimensioni, può essere utile partizionare i dati memorizzati nella cache tra i nodi per ridurre le probabilità di contesa e migliorare la scalabilità. Molte cache condivise supportano la possibilità di aggiungere (e rimuovere) nodi in modo dinamico e ribilanciare i dati tra le partizioni. Questo approccio può comportare il clustering, in cui la raccolta di nodi viene presentata alle applicazioni client come una cache singola senza problemi. Internamente, tuttavia, i dati vengono distribuiti tra i nodi seguendo una strategia di distribuzione predefinita che bilancia il carico in modo uniforme. Per altre informazioni sulle possibili strategie di partizionamento, vedere Indicazioni sul partizionamento dei dati.
Il clustering può anche aumentare la disponibilità della cache. Se un nodo non riesce, il resto della cache è ancora accessibile. Il clustering viene spesso usato insieme alla replica e al failover. Ogni nodo può essere replicato e la replica può essere portata rapidamente online in caso di errore del nodo.
Molte operazioni di lettura e scrittura prevedono probabilmente valori o oggetti dati singoli. Tuttavia, a volte potrebbe essere necessario archiviare o recuperare rapidamente grandi volumi di dati. Ad esempio, il seeding di una cache potrebbe comportare la scrittura di centinaia o migliaia di elementi nella cache. Un'applicazione potrebbe anche dover recuperare un numero elevato di elementi correlati dalla cache come parte della stessa richiesta.
Molte cache su larga scala forniscono operazioni batch per questi scopi. Ciò consente a un'applicazione client di creare un pacchetto di un volume elevato di elementi in una singola richiesta e riduce il sovraccarico associato all'esecuzione di un numero elevato di richieste di piccole dimensioni.
Memorizzazione nella cache e coerenza finale
Per il funzionamento del modello cache-aside, l'istanza dell'applicazione che popola la cache deve avere accesso alla versione più recente e coerente dei dati. In un sistema che implementa la coerenza finale ,ad esempio un archivio dati replicato, questo potrebbe non essere il caso.
Un'istanza di un'applicazione può modificare un elemento di dati e invalidare la versione memorizzata nella cache di tale elemento. Un'altra istanza dell'applicazione potrebbe tentare di leggere questo elemento da una cache, causando un mancato riscontro nella cache, in modo da leggere i dati dall'archivio dati e aggiungerlo alla cache. Tuttavia, se l'archivio dati non è stato completamente sincronizzato con le altre repliche, l'istanza dell'applicazione potrebbe leggere e popolare la cache con il valore precedente.
Per altre informazioni sulla gestione della coerenza dei dati, vedere Informazioni di base sulla coerenza dei dati.
Proteggere i dati memorizzati nella cache
Indipendentemente dal servizio cache usato, valutare come proteggere i dati contenuti nella cache da accessi non autorizzati. Esistono due problemi principali:
- Privacy dei dati nella cache.
- La privacy dei dati durante il flusso tra la cache e l'applicazione che usa la cache.
Per proteggere i dati nella cache, il servizio cache potrebbe implementare un meccanismo di autenticazione che richiede che le applicazioni specifichino i dettagli seguenti:
- Quali identità possono accedere ai dati nella cache.
- Quali operazioni (lettura e scrittura) che queste identità sono autorizzate a eseguire.
Per ridurre il sovraccarico associato alla lettura e alla scrittura dei dati, dopo che a un'identità è stato concesso l'accesso in scrittura o in lettura alla cache, tale identità può usare tutti i dati nella cache.
Se è necessario limitare l'accesso ai subset dei dati memorizzati nella cache, è possibile eseguire uno degli approcci seguenti:
- Suddividere la cache in partizioni (usando server cache diversi) e concedere l'accesso solo alle identità per le partizioni che devono essere autorizzate a usare.
- Crittografare i dati in ogni subset usando chiavi diverse e fornire le chiavi di crittografia solo alle identità che devono avere accesso a ogni subset. Un'applicazione client potrebbe comunque essere in grado di recuperare tutti i dati nella cache, ma sarà in grado di decrittografare solo i dati per i quali dispone delle chiavi.
È anche necessario proteggere i dati durante il flusso nella cache e all'esterno della cache. A tale scopo, è necessario dipendere dalle funzionalità di sicurezza fornite dall'infrastruttura di rete usata dalle applicazioni client per connettersi alla cache. Se la cache viene implementata usando un server sul sito all'interno della stessa organizzazione che ospita le applicazioni client, l'isolamento della rete stessa potrebbe non richiedere ulteriori passaggi. Se la cache si trova in remoto e richiede una connessione TCP o HTTP su una rete pubblica (ad esempio Internet), valutare la possibilità di implementare SSL.
Considerazioni sull'implementazione della memorizzazione nella cache in Azure
Cache Redis di Azure è un'implementazione della cache Redis open source eseguita come servizio in un data center di Azure. Fornisce un servizio di memorizzazione nella cache accessibile da qualsiasi applicazione di Azure, indipendentemente dal fatto che l'applicazione sia implementata come servizio cloud, un sito Web o all'interno di una macchina virtuale di Azure. Le cache possono essere condivise dalle applicazioni client con la chiave di accesso appropriata.
Cache Redis di Azure è una soluzione di memorizzazione nella cache ad alte prestazioni che offre disponibilità, scalabilità e sicurezza. In genere viene eseguito come servizio distribuito in uno o più computer dedicati. Tenta di archiviare tutte le informazioni che possono essere in memoria per garantire un accesso rapido. Questa architettura è progettata per offrire bassa latenza e velocità effettiva elevata riducendo la necessità di eseguire operazioni di I/O lente.
Cache Redis di Azure è compatibile con molte delle varie API usate dalle applicazioni client. Se sono già presenti applicazioni che usano già Cache Redis di Azure in esecuzione in locale, Cache Redis di Azure offre un percorso di migrazione rapido per la memorizzazione nella cache nel cloud.
Caratteristiche di Redis
Redis è più di un server cache semplice. Fornisce un database distribuito in memoria con un set di comandi completo che supporta molti scenari comuni. Questi sono descritti più avanti in questo documento, nella sezione Uso della memorizzazione nella cache redis. Questa sezione riepiloga alcune delle funzionalità principali fornite da Redis.
Redis come database in memoria
Redis supporta sia operazioni di lettura che scrittura. In Redis le scritture possono essere protette da un errore di sistema tramite l'archiviazione periodica in un file di snapshot locale o in un file di log di sola accodamento. Questa situazione non è il caso in molte cache, che devono essere considerate archivi dati transitori.
Tutte le scritture sono asincrone e non impediscono ai client di leggere e scrivere dati. Quando Redis inizia l'esecuzione, legge i dati dallo snapshot o dal file di log e lo usa per costruire la cache in memoria. Per altre informazioni, vedere Persistenza di Redis nel sito Web Redis.
Note
Redis non garantisce che tutte le scritture vengano salvate se si verifica un errore irreversibile, ma nel peggiore dei casi si potrebbero perdere solo pochi secondi di dati. Tenere presente che una cache non è progettata per fungere da origine dati autorevole ed è responsabilità delle applicazioni che usano la cache per garantire che i dati critici vengano salvati correttamente in un archivio dati appropriato. Per altre informazioni, vedere il modello Cache-aside.
Tipi di dati Redis
Redis è un archivio chiave-valore, in cui i valori possono contenere tipi semplici o strutture di dati complesse, ad esempio hash, elenchi e set. Supporta un set di operazioni atomica su questi tipi di dati. Le chiavi possono essere permanenti o contrassegnate con una durata limitata, a questo punto la chiave e il valore corrispondente vengono rimossi automaticamente dalla cache. Per altre informazioni sulle chiavi e i valori di Redis, visitare la pagina Introduzione ai tipi di dati e alle astrazioni Redis nel sito Web Redis.
Replica e clustering di Redis
Redis supporta la replica primaria/subordinata per garantire la disponibilità e mantenere la velocità effettiva. Le operazioni di scrittura in un nodo primario Redis vengono replicate in uno o più nodi subordinati. Le operazioni di lettura possono essere gestite dal database primario o da uno dei subordinati.
Se si dispone di una partizione di rete, i subordinati possono continuare a gestire i dati e quindi risincronizzare in modo trasparente con il database primario quando la connessione viene ristabilita. Per altri dettagli, visitare la pagina Replica nel sito Web Redis.
Redis offre anche il clustering, che consente di partizionare in modo trasparente i dati in partizioni tra server e distribuire il carico. Questa funzionalità migliora la scalabilità, perché è possibile aggiungere nuovi server Redis e ripartizionare i dati man mano che aumentano le dimensioni della cache.
Inoltre, ogni server nel cluster può essere replicato usando la replica primaria/subordinata. In questo modo si garantisce la disponibilità in ogni nodo del cluster. Per altre informazioni sul clustering e sul partizionamento orizzontale, visitare la pagina dell'esercitazione sul cluster Redis nel sito Web Redis.
Uso della memoria Redis
Una cache Redis ha una dimensione limitata che dipende dalle risorse disponibili nel computer host. Quando si configura un server Redis, è possibile specificare la quantità massima di memoria che può usare. È anche possibile configurare una chiave in una cache Redis per avere una scadenza, dopo la quale viene rimossa automaticamente dalla cache. Questa funzionalità consente di evitare che la cache in memoria venga riempita con dati obsoleti o non aggiornati.
Quando la memoria si riempie, Redis può rimuovere automaticamente le chiavi e i relativi valori in base a criteri diversi. Il valore predefinito è il meno recentemente usato (LRU), ma è possibile scegliere anche altre opzioni, come la rimozione delle chiavi in modo casuale o disabilitare completamente la rimozione. In tal caso, i tentativi di aggiungere elementi alla cache hanno esito negativo se è pieno. Per altre informazioni, vedere Usare Redis come cache LRU.
Transazioni e batch Redis
Redis consente a un'applicazione client di inviare una serie di operazioni che leggono e scrivono dati nella cache come transazione atomica. Tutti i comandi nella transazione sono garantiti per essere eseguiti in modo sequenziale, e nessun comando emesso da altri client simultanei si interpone tra di essi.
Tuttavia, queste transazioni non sono vere perché un database relazionale le eseguirà. L'elaborazione delle transazioni è costituita da due fasi: la prima è quando i comandi vengono accodati e il secondo è quando vengono eseguiti i comandi. Durante la fase di accodamento dei comandi, i comandi che costituiscono la transazione vengono inviati dal client. Se si verifica un certo tipo di errore a questo punto (ad esempio un errore di sintassi o il numero errato di parametri), Redis rifiuta di elaborare l'intera transazione e lo rimuove.
Durante la fase di esecuzione, Redis esegue ogni comando in coda in sequenza. Se un comando non riesce durante questa fase, Redis continua con il comando in coda successivo e non esegue il rollback degli effetti di tutti i comandi già eseguiti. Questa forma semplificata di transazione consente di mantenere le prestazioni ed evitare problemi di prestazioni causati da conflitti.
Redis implementa una forma di blocco ottimistico per facilitare la coerenza. Per informazioni dettagliate sulle transazioni e sul blocco con Redis, visitare la pagina Transazioni nel sito Web Redis.
Redis supporta anche l'invio in batch non transazionale delle richieste. Il protocollo Redis usato dai client per inviare comandi a un server Redis consente a un client di inviare una serie di operazioni come parte della stessa richiesta. Ciò consente di ridurre la frammentazione dei pacchetti nella rete. Mentre il batch elabora, ogni comando viene eseguito. Se uno di questi comandi non è valido, viene rifiutato (cosa che non succede con una transazione), ma i comandi rimanenti vengono comunque eseguiti. Non c'è inoltre alcuna garanzia sull'ordine in cui vengano eseguiti i comandi nel processo batch.
Sicurezza di Redis
Redis è incentrato esclusivamente sull'accesso rapido ai dati ed è progettato per l'esecuzione all'interno di un ambiente attendibile accessibile solo da client attendibili. Redis supporta un modello di sicurezza limitato basato sull'autenticazione della password. È possibile rimuovere completamente l'autenticazione, anche se non è consigliabile.
Tutti i client autenticati condividono la stessa password globale e hanno accesso alle stesse risorse. Se è necessaria una sicurezza di accesso più completa, è necessario implementare il proprio livello di sicurezza davanti al server Redis e tutte le richieste client devono passare attraverso questo livello aggiuntivo. Redis non deve essere esposto direttamente a client non attendibili o non autenticati.
È possibile limitare l'accesso ai comandi disabilitandoli o rinominandoli (e fornendo solo i client con privilegi con i nuovi nomi).
Redis non supporta direttamente alcuna forma di crittografia dei dati, quindi tutte le codifiche devono essere eseguite dalle applicazioni client. Inoltre, Redis non fornisce alcuna forma di sicurezza del trasporto. Se è necessario proteggere i dati man mano che passano attraverso la rete, è consigliabile implementare un proxy SSL.
Per altre informazioni, visitare la pagina relativa alla sicurezza di Redis nel sito Web redis.
Note
Cache Redis di Azure offre un proprio livello di sicurezza tramite il quale i client si connettono. I server Redis sottostanti non sono esposti alla rete pubblica.
Cache Redis di Azure
Cache Redis di Azure consente di accedere ai server Redis ospitati in un data center di Azure. Funge da facciata che fornisce controllo di accesso e sicurezza. È possibile effettuare il provisioning di una cache usando il portale di Azure.
Il portale fornisce diverse configurazioni predefinite. Queste configurazioni vanno da una cache da 53 GB in esecuzione come servizio dedicato che supporta le comunicazioni SSL (per la privacy) e la replica master/subordinata con un contratto di servizio (SLA) di 99,9% disponibilità, fino a una cache di 250 MB senza replica (nessuna garanzia di disponibilità) in esecuzione su hardware condiviso.
Usando il portale di Azure, è anche possibile configurare i criteri di rimozione della cache e controllare l'accesso alla cache aggiungendo utenti ai ruoli forniti. Questi ruoli, che definiscono le operazioni che i membri possono eseguire, includono Proprietario, Collaboratore e Lettore. Ad esempio, i membri del ruolo Proprietario hanno il controllo completo sulla cache (inclusa la sicurezza) e sul relativo contenuto, i membri del ruolo Collaboratore possono leggere e scrivere informazioni nella cache e i membri del ruolo Lettore possono recuperare solo i dati dalla cache.
La maggior parte delle attività amministrative viene eseguita tramite il portale di Azure. Per questo motivo, molti dei comandi amministrativi disponibili nella versione standard di Redis non sono disponibili, inclusa la possibilità di modificare la configurazione a livello di codice, arrestare il server Redis, configurare subordinati aggiuntivi o salvare forzatamente i dati su disco.
Il portale di Azure include una comoda visualizzazione grafica che consente di monitorare le prestazioni della cache. Ad esempio, è possibile visualizzare il numero di connessioni effettuate, il numero di richieste eseguite, il volume di letture e scritture e il numero di riscontri nella cache rispetto ai mancati riscontri nella cache. Usando queste informazioni, è possibile determinare l'efficacia della cache e, se necessario, passare a una configurazione diversa o modificare i criteri di rimozione.
Inoltre, è possibile creare avvisi che inviano messaggi di posta elettronica a un amministratore se una o più metriche critiche non rientrano in un intervallo previsto. Ad esempio, potrebbe essere necessario avvisare un amministratore se il numero di mancati riscontri nella cache supera un valore specificato nell'ultima ora, perché significa che la cache potrebbe essere troppo piccola o che i dati potrebbero essere rimossi troppo rapidamente.
È anche possibile monitorare l'utilizzo della CPU, della memoria e della rete per la cache.
Per altre informazioni ed esempi che illustrano come creare e configurare un'istanza di Cache Redis di Azure, visitare la pagina Lap around Azure Cache for Redis (Lap around Azure Cache for Redis) nel blog di Azure.
Memorizzazione nella cache dello stato della sessione e output HTML
Se si compilano ASP.NET applicazioni Web eseguite usando i ruoli Web di Azure, è possibile salvare le informazioni sullo stato della sessione e l'output HTML in una Cache Redis di Azure. Il provider di stato della sessione per Cache Redis di Azure consente di condividere informazioni di sessione tra istanze diverse di un'applicazione Web ASP.NET ed è molto utile nelle situazioni della Web farm in cui l'affinità client-server non è disponibile e la memorizzazione nella cache dei dati della sessione in memoria non sarebbe appropriata.
L'uso del provider di stato della sessione con Cache Redis di Azure offre diversi vantaggi, tra cui:
- Condivisione dello stato della sessione con un numero elevato di istanze di ASP.NET applicazioni Web.
- Miglioramento della scalabilità.
- Supporto dell'accesso controllato e simultaneo agli stessi dati sullo stato della sessione per più lettori e un singolo writer.
- Uso della compressione per risparmiare memoria e migliorare le prestazioni di rete.
Per altre informazioni, vedere ASP.NET provider di stato della sessione per Cache Redis di Azure.
Note
Non usare il provider di stato della sessione per Cache Redis di Azure con applicazioni ASP.NET eseguite all'esterno dell'ambiente Azure. La latenza di accesso alla cache dall'esterno di Azure può eliminare i vantaggi delle prestazioni dei dati di memorizzazione nella cache.
Analogamente, il provider di cache di output per Cache Redis di Azure consente di salvare le risposte HTTP generate da un'applicazione Web ASP.NET. L'uso del provider di cache di output con Cache Redis di Azure può migliorare i tempi di risposta delle applicazioni che eseguono il rendering di output HTML complesso. Le istanze dell'applicazione che generano risposte simili possono usare i frammenti di output condivisi nella cache anziché generare di nuovo questo output HTML. Per altre informazioni, vedere ASP.NET provider di cache di output per Cache Redis di Azure.
Creazione di una cache Redis personalizzata
Cache Redis di Azure funge da facciata ai server Redis sottostanti. Se è necessaria una configurazione avanzata non coperta dalla cache Redis di Azure (ad esempio una cache di dimensioni superiori a 53 GB), è possibile compilare e ospitare i propri server Redis usando Macchine virtuali di Azure.
Si tratta di un processo potenzialmente complesso perché potrebbe essere necessario creare diverse macchine virtuali per fungere da nodi primari e subordinati se si vuole implementare la replica. Inoltre, se si vuole creare un cluster, sono necessari più server primari e subordinati. Una topologia di replica cluster minima che offre un livello elevato di disponibilità e scalabilità comprende almeno sei macchine virtuali organizzate come tre coppie di server primari/subordinati (un cluster deve contenere almeno tre nodi primari).
Ogni coppia primaria/subordinata deve essere posizionata insieme per ridurre al minimo la latenza. Tuttavia, ogni set di coppie può essere in esecuzione in data center di Azure diversi che si trovano in aree diverse, se si desidera individuare i dati memorizzati nella cache vicino alle applicazioni che probabilmente lo usano. Per un esempio di compilazione e configurazione di un nodo Redis in esecuzione come macchina virtuale di Azure, vedere Esecuzione di Redis in una macchina virtuale Linux CentOS in Azure.
Note
Se si implementa la cache Redis in questo modo, si è responsabili del monitoraggio, della gestione e della protezione del servizio.
Partizionamento di una cache Redis
Il partizionamento della cache comporta la suddivisione della cache tra più computer. Questa struttura offre diversi vantaggi rispetto all'uso di un singolo server della cache, tra cui:
- Creazione di una cache molto più grande di quanto possa essere archiviata in un singolo server.
- Distribuzione dei dati tra server, miglioramento della disponibilità. Se un server ha esito negativo o diventa inaccessibile, i dati che contiene non sono disponibili, ma è comunque possibile accedere ai dati nei server rimanenti. Per una cache, questo non è fondamentale perché i dati memorizzati nella cache sono solo una copia temporanea dei dati contenuti in un database. I dati memorizzati nella cache in un server che diventano inaccessibili possono essere memorizzati nella cache in un server diverso.
- Distribuire il carico tra server, migliorando così le prestazioni e la scalabilità.
- Geolocazione dei dati vicino agli utenti che vi accedono, riducendo così la latenza.
Per una cache, la forma più comune di partizionamento è il partizionamento orizzontale. In questa strategia ogni partizione (o partizione) è una cache Redis a proprio diritto. I dati vengono indirizzati a una partizione specifica usando la logica di partizionamento orizzontale, che può usare diversi approcci per distribuire i dati. Il modello di partizionamento orizzontale fornisce altre informazioni sull'implementazione del partizionamento orizzontale.
Per implementare il partizionamento in una cache Redis, è possibile adottare uno degli approcci seguenti:
- Routing delle query sul lato server. In questa tecnica, un'applicazione client invia una richiesta a uno dei server Redis che comprendono la cache (probabilmente il server più vicino). Ogni server Redis archivia i metadati che descrivono la partizione contenuta e contiene anche informazioni sulle partizioni che si trovano in altri server. Il server Redis esamina la richiesta client. Se può essere risolto in locale, eseguirà l'operazione richiesta. In caso contrario, la richiesta verrà inoltrata al server appropriato. Questo modello viene implementato dal clustering Redis ed è descritto in modo più dettagliato nella pagina dell'esercitazione sul cluster Redis nel sito Web Redis. Il clustering Redis è trasparente per le applicazioni client e i server Redis aggiuntivi possono essere aggiunti al cluster (e i dati ripartizionati) senza dover riconfigurare i client.
- Partizionamento lato client. In questo modello, l'applicazione client contiene logica (possibilmente sotto forma di libreria) che instrada le richieste al server Redis appropriato. Questo approccio può essere usato con Cache Redis di Azure. Creare più cache di Azure per Redis (una per ogni partizione di dati) e implementare la logica lato client che instrada le richieste alla cache corretta. Se lo schema di partizionamento cambia (se vengono create altre cache di Azure per Redis, ad esempio), potrebbe essere necessario riconfigurare le applicazioni client.
- Partizionamento assistito da proxy. In questo schema, le applicazioni client inviano richieste a un servizio proxy intermedio che riconosce il modo in cui i dati vengono partizionati e quindi instrada la richiesta al server Redis appropriato. Questo approccio può essere usato anche con Cache Redis di Azure; il servizio proxy può essere implementato come servizio cloud di Azure. Questo approccio richiede un livello di complessità aggiuntivo per implementare il servizio e le richieste potrebbero richiedere più tempo rispetto all'uso del partizionamento lato client.
La pagina Partizionamento: come suddividere i dati tra più istanze di Redis nel sito Web Redis fornisce altre informazioni sull'implementazione del partizionamento con Redis.
Implementare le applicazioni client della cache Redis
Redis supporta applicazioni client scritte in numerosi linguaggi di programmazione. Se si compilano nuove applicazioni usando .NET Framework, è consigliabile usare la libreria client StackExchange.Redis. Questa libreria fornisce un modello a oggetti .NET Framework che astrae i dettagli per la connessione a un server Redis, l'invio di comandi e la ricezione di risposte. È disponibile in Visual Studio come pacchetto NuGet. È possibile usare questa stessa libreria per connettersi a cache Redis di Azure o a una cache Redis personalizzata ospitata in una macchina virtuale.
Per connettersi a un server Redis, usare il metodo statico Connect della ConnectionMultiplexer classe . La connessione creata da questo metodo è progettata per essere usata per tutta la durata dell'applicazione client e la stessa connessione può essere usata da più thread simultanei. Non riconnettersi e disconnettersi ogni volta che si esegue un'operazione Redis perché ciò può compromettere le prestazioni.
È possibile specificare i parametri di connessione, ad esempio l'indirizzo dell'host Redis e la password. Se si usa Cache Redis di Azure, la password è la chiave primaria o secondaria generata per Cache Redis di Azure usando il portale di Azure.
Dopo aver eseguito la connessione al server Redis, è possibile ottenere un handle nel database Redis che funge da cache. La connessione Redis fornisce il GetDatabase metodo per eseguire questa operazione. È quindi possibile recuperare elementi dalla cache e archiviare i dati nella cache usando i StringGet metodi e StringSet . Questi metodi prevedono una chiave come parametro e restituiscono l'elemento nella cache con un valore corrispondente (StringGet) o aggiungere l'elemento alla cache con questa chiave (StringSet).
A seconda del percorso del server Redis, molte operazioni potrebbero causare una latenza mentre una richiesta viene trasmessa al server e viene restituita una risposta al client. La libreria StackExchange fornisce versioni asincrone di molti dei metodi esposti per consentire alle applicazioni client di rimanere reattive. Questi metodi supportano il modello asincrono basato su attività in .NET Framework.
Il frammento di codice seguente mostra un metodo denominato RetrieveItem. Illustra un'implementazione del modello cache-aside basato su Redis e sulla libreria StackExchange. Il metodo accetta un valore di chiave stringa e tenta di recuperare l'elemento corrispondente dalla cache Redis chiamando il StringGetAsync metodo (la versione asincrona di StringGet).
Se l'elemento non viene trovato, viene recuperato dall'origine dati sottostante usando il GetItemFromDataSourceAsync metodo ( che è un metodo locale e non fa parte della libreria StackExchange). Viene quindi aggiunto alla cache usando il StringSetAsync metodo in modo che possa essere recuperato più rapidamente la volta successiva.
// Connect to the Azure Redis cache
ConfigurationOptions config = new ConfigurationOptions();
config.EndPoints.Add("<your DNS name>.redis.cache.windows.net");
config.Password = "<Redis cache key from management portal>";
ConnectionMultiplexer redisHostConnection = ConnectionMultiplexer.Connect(config);
IDatabase cache = redisHostConnection.GetDatabase();
...
private async Task<string> RetrieveItem(string itemKey)
{
// Attempt to retrieve the item from the Redis cache
string itemValue = await cache.StringGetAsync(itemKey);
// If the value returned is null, the item was not found in the cache
// So retrieve the item from the data source and add it to the cache
if (itemValue == null)
{
itemValue = await GetItemFromDataSourceAsync(itemKey);
await cache.StringSetAsync(itemKey, itemValue);
}
// Return the item
return itemValue;
}
I StringGet metodi e StringSet non sono limitati al recupero o all'archiviazione di valori stringa. Possono accettare qualsiasi elemento serializzato come matrice di byte. Se è necessario salvare un oggetto .NET, è possibile serializzarlo come flusso di byte e usare il StringSet metodo per scriverlo nella cache.
Analogamente, è possibile leggere un oggetto dalla cache usando il StringGet metodo e deserializzandolo come oggetto .NET. Il codice seguente illustra un set di metodi di estensione per l'interfaccia IDatabase (il GetDatabase metodo di una connessione Redis restituisce un IDatabase oggetto) e un codice di esempio che usa questi metodi per leggere e scrivere un BlogPost oggetto nella cache:
public static class RedisCacheExtensions
{
public static async Task<T> GetAsync<T>(this IDatabase cache, string key)
{
return Deserialize<T>(await cache.StringGetAsync(key));
}
public static async Task<object> GetAsync(this IDatabase cache, string key)
{
return Deserialize<object>(await cache.StringGetAsync(key));
}
public static async Task SetAsync(this IDatabase cache, string key, object value)
{
await cache.StringSetAsync(key, Serialize(value));
}
static byte[] Serialize(object o)
{
byte[] objectDataAsStream = null;
if (o != null)
{
var jsonString = JsonSerializer.Serialize(o);
objectDataAsStream = Encoding.ASCII.GetBytes(jsonString);
}
return objectDataAsStream;
}
static T Deserialize<T>(byte[] stream)
{
T result = default(T);
if (stream != null)
{
var jsonString = Encoding.ASCII.GetString(stream);
result = JsonSerializer.Deserialize<T>(jsonString);
}
return result;
}
}
Il codice seguente illustra un metodo denominato RetrieveBlogPost che usa questi metodi di estensione per leggere e scrivere un oggetto serializzabile BlogPost nella cache seguendo il modello cache-aside:
// The BlogPost type
public class BlogPost
{
private HashSet<string> tags;
public BlogPost(int id, string title, int score, IEnumerable<string> tags)
{
this.Id = id;
this.Title = title;
this.Score = score;
this.tags = new HashSet<string>(tags);
}
public int Id { get; set; }
public string Title { get; set; }
public int Score { get; set; }
public ICollection<string> Tags => this.tags;
}
...
private async Task<BlogPost> RetrieveBlogPost(string blogPostKey)
{
BlogPost blogPost = await cache.GetAsync<BlogPost>(blogPostKey);
if (blogPost == null)
{
blogPost = await GetBlogPostFromDataSourceAsync(blogPostKey);
await cache.SetAsync(blogPostKey, blogPost);
}
return blogPost;
}
Redis supporta la pipelining dei comandi se un'applicazione client invia più richieste asincrone. Redis può eseguire il multiplex delle richieste usando la stessa connessione anziché ricevere e rispondere ai comandi in una sequenza rigorosa.
Questo approccio consente di ridurre la latenza rendendo più efficiente l'uso della rete. Il frammento di codice seguente mostra un esempio che recupera i dettagli di due clienti contemporaneamente. Il codice invia due richieste e quindi esegue un'altra elaborazione (non visualizzata) prima di attendere di ricevere i risultati. Il Wait metodo dell'oggetto cache è simile al metodo .NET Framework Task.Wait :
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
var task1 = cache.StringGetAsync("customer:1");
var task2 = cache.StringGetAsync("customer:2");
...
var customer1 = cache.Wait(task1);
var customer2 = cache.Wait(task2);
Per altre informazioni sulla scrittura di applicazioni client che possono usare Cache Redis di Azure, vedere la documentazione di Cache Redis di Azure. Altre informazioni sono disponibili anche in StackExchange.Redis.
La pagina Pipeline e multiplexer nello stesso sito Web fornisce altre informazioni sulle operazioni asincrone e sulla pipelining con Redis e la libreria StackExchange.
Uso della memorizzazione nella cache redis
L'uso più semplice di Redis per i problemi di memorizzazione nella cache è costituito da coppie chiave-valore in cui il valore è una stringa non interpretata di lunghezza arbitraria che può contenere dati binari. Si tratta essenzialmente di una matrice di byte che può essere considerata come stringa. Questo scenario è stato illustrato nella sezione Implementare le applicazioni client di Cache Redis in precedenza in questo articolo.
Le chiavi contengono anche dati non interpretati, quindi è possibile usare qualsiasi informazione binaria come chiave. Più a lungo la chiave è, tuttavia, maggiore sarà lo spazio necessario per archiviare e più tempo ci vorrà per eseguire operazioni di ricerca. Per usabilità e facilità di manutenzione, progettare il keyspace con attenzione e usare chiavi significative (ma non dettagliate).
Ad esempio, usare chiavi strutturate come "customer:100" per rappresentare la chiave per il cliente con ID 100 anziché semplicemente "100". Questo schema consente di distinguere facilmente tra valori che archiviano tipi di dati diversi. Ad esempio, è anche possibile usare la chiave "orders:100" per rappresentare la chiave per l'ordine con ID 100.
Oltre alle stringhe binarie unidimensionali, un valore in una coppia chiave-valore Redis può contenere anche informazioni più strutturate, inclusi elenchi, set (ordinati e non ordinati) e hash. Redis fornisce un set di comandi completo in grado di modificare questi tipi e molti di questi comandi sono disponibili per le applicazioni .NET Framework tramite una libreria client come StackExchange. La pagina Introduzione ai tipi di dati e alle astrazioni Redis nel sito Web Redis offre una panoramica più dettagliata di questi tipi e dei comandi che è possibile usare per modificarli.
Questa sezione riepiloga alcuni casi d'uso comuni per questi tipi di dati e comandi.
Eseguire operazioni atomico e batch
Redis supporta una serie di operazioni atomiche get-and-set sui valori stringa. Queste operazioni rimuovono i possibili problemi di concorrenza che possono verificarsi, quando si usano comandi GET e SET separati. Le operazioni disponibili includono:
INCR,INCRBY,DECReDECRBY, che eseguono operazioni di incremento atomico e decremento su valori di dati numerici interi. La libreria StackExchange fornisce versioni di overload deiIDatabase.StringIncrementAsyncmetodi eIDatabase.StringDecrementAsyncper eseguire queste operazioni e restituire il valore risultante archiviato nella cache. Il frammento di codice seguente illustra come usare questi metodi:ConnectionMultiplexer redisHostConnection = ...; IDatabase cache = redisHostConnection.GetDatabase(); ... await cache.StringSetAsync("data:counter", 99); ... long oldValue = await cache.StringIncrementAsync("data:counter"); // Increment by 1 (the default) // oldValue should be 100 long newValue = await cache.StringDecrementAsync("data:counter", 50); // Decrement by 50 // newValue should be 50GETSET, che recupera il valore associato a una chiave e lo modifica in un nuovo valore. La libreria StackExchange rende disponibile questa operazione tramite ilIDatabase.StringGetSetAsyncmetodo . Il frammento di codice seguente mostra un esempio di questo metodo. Questo codice restituisce il valore corrente associato alla chiave "data:counter" dell'esempio precedente. Reimposta quindi il valore di questa chiave su zero, tutto come parte della stessa operazione:ConnectionMultiplexer redisHostConnection = ...; IDatabase cache = redisHostConnection.GetDatabase(); ... string oldValue = await cache.StringGetSetAsync("data:counter", 0);MGETeMSET, che può restituire o modificare un set di valori stringa come singola operazione. IIDatabase.StringGetAsyncmetodi eIDatabase.StringSetAsyncsono in overload per supportare questa funzionalità, come illustrato nell'esempio seguente:ConnectionMultiplexer redisHostConnection = ...; IDatabase cache = redisHostConnection.GetDatabase(); ... // Create a list of key-value pairs var keysAndValues = new List<KeyValuePair<RedisKey, RedisValue>>() { new KeyValuePair<RedisKey, RedisValue>("data:key1", "value1"), new KeyValuePair<RedisKey, RedisValue>("data:key99", "value2"), new KeyValuePair<RedisKey, RedisValue>("data:key322", "value3") }; // Store the list of key-value pairs in the cache cache.StringSet(keysAndValues.ToArray()); ... // Find all values that match a list of keys RedisKey[] keys = { "data:key1", "data:key99", "data:key322"}; // values should contain { "value1", "value2", "value3" } RedisValue[] values = cache.StringGet(keys);
È anche possibile combinare più operazioni in una singola transazione Redis, come descritto nella sezione Transazioni e batch Redis più indietro in questo articolo. La libreria StackExchange fornisce supporto per le transazioni tramite l'interfaccia ITransaction .
Per creare un ITransaction oggetto, usare il IDatabase.CreateTransaction metodo . I comandi vengono richiamati alla transazione usando i metodi forniti dall'oggetto ITransaction .
L'interfaccia ITransaction fornisce l'accesso a un set di metodi simili a quelli a cui si accede dall'interfaccia IDatabase , ad eccezione del fatto che tutti i metodi sono asincroni. Ciò significa che vengono eseguite solo quando viene richiamato il ITransaction.Execute metodo . Il valore restituito dal ITransaction.Execute metodo indica se la transazione è stata creata correttamente (true) o se ha avuto esito negativo (false).
Il frammento di codice seguente mostra un esempio che incrementa e decrementa due contatori come parte della stessa transazione:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
ITransaction transaction = cache.CreateTransaction();
var tx1 = transaction.StringIncrementAsync("data:counter1");
var tx2 = transaction.StringDecrementAsync("data:counter2");
bool result = transaction.Execute();
Console.WriteLine("Transaction {0}", result ? "succeeded" : "failed");
Console.WriteLine("Result of increment: {0}", tx1.Result);
Console.WriteLine("Result of decrement: {0}", tx2.Result);
Tenere presente che le transazioni Redis sono diverse dalle transazioni nei database relazionali. Il Execute metodo accoda semplicemente tutti i comandi che costituiscono la transazione da eseguire e, se uno di essi è in formato non valido, la transazione viene arrestata. Se tutti i comandi sono stati accodati correttamente, ogni comando viene eseguito in modo asincrono.
Se un comando ha esito negativo, gli altri continuano comunque l'elaborazione. Se è necessario verificare che un comando sia stato completato correttamente, è necessario recuperare i risultati del comando usando la proprietà Result dell'attività corrispondente, come illustrato nell'esempio precedente. La lettura della proprietà Result bloccherà il thread chiamante fino al completamento dell'attività.
Per altre informazioni, vedere Transazioni in Redis.
Quando si eseguono operazioni batch, è possibile usare l'interfaccia IBatch della libreria StackExchange. Questa interfaccia fornisce l'accesso a un set di metodi simili a quelli a cui si accede dall'interfaccia IDatabase , ad eccezione del fatto che tutti i metodi sono asincroni.
Si crea un IBatch oggetto usando il IDatabase.CreateBatch metodo e quindi si esegue il batch usando il IBatch.Execute metodo , come illustrato nell'esempio seguente. Questo codice imposta semplicemente un valore stringa, incrementa e decrementa gli stessi contatori usati nell'esempio precedente e visualizza i risultati:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
IBatch batch = cache.CreateBatch();
batch.StringSetAsync("data:key1", 11);
var t1 = batch.StringIncrementAsync("data:counter1");
var t2 = batch.StringDecrementAsync("data:counter2");
batch.Execute();
Console.WriteLine("{0}", t1.Result);
Console.WriteLine("{0}", t2.Result);
È importante comprendere che a differenza di una transazione, se un comando in un batch ha esito negativo perché è in formato non valido, gli altri comandi potrebbero comunque essere eseguiti. Il IBatch.Execute metodo non restituisce alcuna indicazione di esito positivo o negativo.
Eseguire operazioni di generazione e di dimenticare la cache
Redis supporta le operazioni fire e forget usando i flag di comando. In questo caso, il client avvia semplicemente un'operazione, ma non ha alcun interesse per il risultato e non attende il completamento del comando. L'esempio seguente illustra come eseguire il comando INCR come operazione fire e forget:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
await cache.StringSetAsync("data:key1", 99);
...
cache.StringIncrement("data:key1", flags: CommandFlags.FireAndForget);
Specificare automaticamente le chiavi in scadenza
Quando si archivia un elemento in una cache Redis, è possibile specificare un timeout dopo il quale l'elemento viene rimosso automaticamente dalla cache. È anche possibile eseguire una query su quanto più tempo ha una chiave prima della scadenza usando il TTL comando . Questo comando è disponibile per le applicazioni StackExchange usando il IDatabase.KeyTimeToLive metodo .
Il frammento di codice seguente illustra come impostare una scadenza di 20 secondi su una chiave ed eseguire una query sulla durata rimanente della chiave:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
// Add a key with an expiration time of 20 seconds
await cache.StringSetAsync("data:key1", 99, TimeSpan.FromSeconds(20));
...
// Query how much time a key has left to live
// If the key has already expired, the KeyTimeToLive function returns a null
TimeSpan? expiry = cache.KeyTimeToLive("data:key1");
È anche possibile impostare l'ora di scadenza su una data e un'ora specifiche usando il comando EXPIRE, disponibile nella libreria StackExchange come KeyExpireAsync metodo:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
// Add a key with an expiration date of midnight on 1st January 2015
await cache.StringSetAsync("data:key1", 99);
await cache.KeyExpireAsync("data:key1",
new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Utc));
...
Tip
È possibile rimuovere manualmente un elemento dalla cache usando il comando DEL, disponibile tramite la libreria StackExchange come IDatabase.KeyDeleteAsync metodo .
Usare i tag per correlare gli elementi memorizzati nella cache
Un set Redis è una raccolta di più elementi che condividono una singola chiave. È possibile creare un set usando il comando SADD. È possibile recuperare gli elementi in un set usando il comando SMEMBERS. La libreria StackExchange implementa il comando SADD con il IDatabase.SetAddAsync metodo e il comando SMEMBERS con il IDatabase.SetMembersAsync metodo .
È anche possibile combinare set esistenti per creare nuovi set usando i comandi SDIFF (differenza set), SINTER (intersezione set) e SUNION (imposta unione). La libreria StackExchange unifica queste operazioni nel IDatabase.SetCombineAsync metodo . Il primo parametro di questo metodo specifica l'operazione di impostazione da eseguire.
I frammenti di codice seguenti illustrano come i set possono essere utili per archiviare e recuperare rapidamente raccolte di elementi correlati. Questo codice usa il BlogPost tipo descritto nella sezione Implementare applicazioni client di Cache Redis in precedenza in questo articolo.
Un BlogPost oggetto contiene quattro campi, ovvero un ID, un titolo, un punteggio di classificazione e una raccolta di tag. Il primo frammento di codice mostra i dati di esempio usati per popolare un elenco di BlogPost oggetti C#:
List<string[]> tags = new List<string[]>
{
new[] { "iot","csharp" },
new[] { "iot","azure","csharp" },
new[] { "csharp","git","big data" },
new[] { "iot","git","database" },
new[] { "database","git" },
new[] { "csharp","database" },
new[] { "iot" },
new[] { "iot","database","git" },
new[] { "azure","database","big data","git","csharp" },
new[] { "azure" }
};
List<BlogPost> posts = new List<BlogPost>();
int blogKey = 0;
int numberOfPosts = 20;
Random random = new Random();
for (int i = 0; i < numberOfPosts; i++)
{
blogKey++;
posts.Add(new BlogPost(
blogKey, // Blog post ID
string.Format(CultureInfo.InvariantCulture, "Blog Post #{0}",
blogKey), // Blog post title
random.Next(100, 10000), // Ranking score
tags[i % tags.Count])); // Tags--assigned from a collection
// in the tags list
}
È possibile archiviare i tag per ogni BlogPost oggetto come set in una cache Redis e associare ogni set all'ID dell'oggetto BlogPost. Ciò consente a un'applicazione di trovare rapidamente tutti i tag che appartengono a un post di blog specifico. Per abilitare la ricerca nella direzione opposta e trovare tutti i post di blog che condividono un tag specifico, è possibile creare un altro set contenente i post di blog che fanno riferimento all'ID tag nella chiave:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
// Tags are easily represented as Redis Sets
foreach (BlogPost post in posts)
{
string redisKey = string.Format(CultureInfo.InvariantCulture,
"blog:posts:{0}:tags", post.Id);
// Add tags to the blog post in Redis
await cache.SetAddAsync(
redisKey, post.Tags.Select(s => (RedisValue)s).ToArray());
// Now do the inverse so we can figure out which blog posts have a given tag
foreach (var tag in post.Tags)
{
await cache.SetAddAsync(string.Format(CultureInfo.InvariantCulture,
"tag:{0}:blog:posts", tag), post.Id);
}
}
Queste strutture consentono di eseguire molte query comuni in modo molto efficiente. Ad esempio, è possibile trovare e visualizzare tutti i tag per il post di blog 1 come segue:
// Show the tags for blog post #1
foreach (var value in await cache.SetMembersAsync("blog:posts:1:tags"))
{
Console.WriteLine(value);
}
È possibile trovare tutti i tag comuni al post di blog 1 e al post di blog 2 eseguendo un'operazione di intersezione set, come indicato di seguito:
// Show the tags in common for blog posts #1 and #2
foreach (var value in await cache.SetCombineAsync(SetOperation.Intersect, new RedisKey[]
{ "blog:posts:1:tags", "blog:posts:2:tags" }))
{
Console.WriteLine(value);
}
Ed è possibile trovare tutti i post di blog che contengono un tag specifico:
// Show the ids of the blog posts that have the tag "iot".
foreach (var value in await cache.SetMembersAsync("tag:iot:blog:posts"))
{
Console.WriteLine(value);
}
Trovare gli elementi a cui si accede di recente
Un'attività comune necessaria per molte applicazioni consiste nel trovare gli elementi a cui si accede più di recente. Ad esempio, un sito di blog potrebbe voler visualizzare informazioni sui post di blog letti più di recente.
È possibile implementare questa funzionalità usando un elenco Redis. Un elenco Redis contiene più elementi che condividono la stessa chiave. L'elenco funge da coda doppia. È possibile eseguire il push degli elementi a una delle estremità dell'elenco usando i comandi LPUSH (push a sinistra) e RPUSH (push destro). È possibile recuperare elementi da una delle due estremità dell'elenco usando i comandi LPOP e RPOP. È anche possibile restituire un set di elementi usando i comandi LRANGE e RRANGE.
I frammenti di codice seguenti illustrano come eseguire queste operazioni usando la libreria StackExchange. Questo codice usa il BlogPost tipo degli esempi precedenti. Quando un post di blog viene letto da un utente, il IDatabase.ListLeftPushAsync metodo inserisce il titolo del post di blog in un elenco associato alla chiave "blog:recent_posts" nella cache Redis.
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
string redisKey = "blog:recent_posts";
BlogPost blogPost = ...; // Reference to the blog post that has just been read
await cache.ListLeftPushAsync(
redisKey, blogPost.Title); // Push the blog post onto the list
Man mano che vengono letti più post di blog, i loro titoli vengono inseriti nella stessa lista. L'elenco viene ordinato in base alla sequenza in cui vengono aggiunti i titoli. I post di blog letti più di recente sono verso la fine sinistra dell'elenco. Se lo stesso post di blog viene letto più volte, contiene più voci nell'elenco.
È possibile visualizzare i titoli dei post letti più di recente usando il IDatabase.ListRange metodo . Questo metodo accetta la chiave che contiene l'elenco, un punto iniziale e un punto finale. Il codice seguente recupera i titoli dei 10 post di blog (elementi da 0 a 9) alla fine più a sinistra dell'elenco:
// Show latest ten posts
foreach (string postTitle in await cache.ListRangeAsync(redisKey, 0, 9))
{
Console.WriteLine(postTitle);
}
Il ListRangeAsync metodo non rimuove elementi dall'elenco. A tale scopo, è possibile usare i IDatabase.ListLeftPopAsync metodi e IDatabase.ListRightPopAsync .
Per evitare che l'elenco aumenti a tempo indeterminato, è possibile eliminare periodicamente gli elementi tagliando l'elenco. Il frammento di codice seguente mostra come rimuovere tutti i cinque elementi più a sinistra dall'elenco:
await cache.ListTrimAsync(redisKey, 0, 5);
Implementare una classifica
Per impostazione predefinita, gli elementi di un set non vengono mantenuti in un ordine specifico. È possibile creare un set ordinato usando il comando ZADD (il IDatabase.SortedSetAdd metodo nella libreria StackExchange). Gli elementi vengono ordinati usando un valore numerico denominato punteggio, che viene fornito come parametro al comando.
Il frammento di codice seguente aggiunge il titolo di un post di blog a un elenco ordinato. In questo esempio, ogni post di blog include anche un campo score che contiene la classificazione del post di blog.
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
string redisKey = "blog:post_rankings";
BlogPost blogPost = ...; // Reference to a blog post that has just been rated
await cache.SortedSetAddAsync(redisKey, blogPost.Title, blogPost.Score);
È possibile recuperare i titoli e i punteggi dei post di blog in ordine di punteggio crescente usando il IDatabase.SortedSetRangeByRankWithScores metodo :
foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(redisKey))
{
Console.WriteLine(post);
}
Note
La libreria StackExchange fornisce anche il IDatabase.SortedSetRangeByRankAsync metodo , che restituisce i dati in ordine di punteggio, ma non restituisce i punteggi.
È anche possibile recuperare gli elementi in ordine decrescente di punteggi e limitare il numero di elementi restituiti fornendo parametri aggiuntivi al IDatabase.SortedSetRangeByRankWithScoresAsync metodo . Nell'esempio seguente vengono visualizzati i titoli e i punteggi dei primi 10 post di blog classificati:
foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(
redisKey, 0, 9, Order.Descending))
{
Console.WriteLine(post);
}
Nell'esempio seguente viene usato il IDatabase.SortedSetRangeByScoreWithScoresAsync metodo , che è possibile usare per limitare gli elementi restituiti a quelli che rientrano in un intervallo di punteggi specificato:
// Blog posts with scores between 5000 and 100000
foreach (var post in await cache.SortedSetRangeByScoreWithScoresAsync(
redisKey, 5000, 100000))
{
Console.WriteLine(post);
}
Messaggio tramite canali
Oltre a fungere da cache dei dati, un server Redis fornisce messaggi tramite un meccanismo di pubblicazione/sottoscrittore ad alte prestazioni. Le applicazioni client possono sottoscrivere un canale e altre applicazioni o servizi possono pubblicare messaggi nel canale. Le applicazioni sottoscritte possono quindi ricevere questi messaggi ed elaborarli.
Redis fornisce il comando SUBSCRIBE per le applicazioni client da usare per sottoscrivere i canali. Questo comando prevede il nome di uno o più canali in cui l'applicazione accetta messaggi. La libreria StackExchange include l'interfaccia ISubscription , che consente a un'applicazione .NET Framework di sottoscrivere e pubblicare nei canali.
Si crea un ISubscription oggetto usando il GetSubscriber metodo della connessione al server Redis. Quindi si ascoltano i messaggi in un canale usando il SubscribeAsync metodo di questo oggetto. L'esempio di codice seguente illustra come sottoscrivere un canale denominato "messages:blogPosts":
ConnectionMultiplexer redisHostConnection = ...;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
...
await subscriber.SubscribeAsync("messages:blogPosts", (channel, message) => Console.WriteLine("Title is: {0}", message));
Il primo parametro del Subscribe metodo è il nome del canale. Questo nome segue le stesse convenzioni usate dalle chiavi nella cache. Il nome può contenere dati binari, ma è consigliabile usare stringhe relativamente brevi e significative per garantire prestazioni ottimali e gestibilità.
Si noti anche che lo spazio dei nomi usato dai canali è separato da quello usato dalle chiavi. Ciò significa che è possibile avere canali e chiavi con lo stesso nome, anche se questo potrebbe rendere il codice dell'applicazione più difficile da gestire.
Il secondo parametro è un delegato action. Questo delegato viene eseguito in modo asincrono ogni volta che viene visualizzato un nuovo messaggio nel canale. Questo esempio visualizza semplicemente il messaggio nella console (il messaggio conterrà il titolo di un post di blog).
Per pubblicare in un canale, un'applicazione può usare il comando Redis PUBLISH. La libreria StackExchange fornisce il IServer.PublishAsync metodo per eseguire questa operazione. Il frammento di codice successivo mostra come pubblicare un messaggio nel canale "messages:blogPosts":
ConnectionMultiplexer redisHostConnection = ...;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
...
BlogPost blogPost = ...;
subscriber.PublishAsync("messages:blogPosts", blogPost.Title);
Esistono diversi punti da comprendere sul meccanismo di pubblicazione/sottoscrizione:
- Più sottoscrittori possono sottoscrivere lo stesso canale e riceveranno tutti i messaggi pubblicati in tale canale.
- I sottoscrittori ricevono solo messaggi pubblicati dopo la sottoscrizione. I canali non vengono memorizzati nel buffer e, dopo la pubblicazione di un messaggio, l'infrastruttura Redis esegue il push del messaggio a ogni sottoscrittore e quindi lo rimuove.
- Per impostazione predefinita, i messaggi vengono ricevuti dai sottoscrittori nell'ordine in cui vengono inviati. In un sistema altamente attivo con un numero elevato di messaggi e molti sottoscrittori e editori, il recapito sequenziale garantito dei messaggi può rallentare le prestazioni del sistema. Se ogni messaggio è indipendente e l'ordine non è importante, è possibile abilitare l'elaborazione simultanea dal sistema Redis, che può contribuire a migliorare la velocità di risposta. È possibile ottenere questo risultato in un client StackExchange impostando PreserveAsyncOrder della connessione utilizzata dal sottoscrittore su false:
ConnectionMultiplexer redisHostConnection = ...;
redisHostConnection.PreserveAsyncOrder = false;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
Considerazioni sulla serializzazione
Quando si sceglie un formato di serializzazione, considerare i compromessi tra prestazioni, interoperabilità, controllo delle versioni, compatibilità con sistemi esistenti, compressione dei dati e sovraccarico della memoria. Quando si valutano le prestazioni, tenere presente che i benchmark dipendono in modo elevato dal contesto. Potrebbero non riflettere il carico di lavoro effettivo e potrebbero non prendere in considerazione librerie o versioni più recenti. Non esiste un singolo serializzatore "più veloce" per tutti gli scenari.
Alcune opzioni da considerare includono:
Protocol Buffers (detto anche protobuf) è un formato di serializzazione sviluppato da Google per serializzare in modo efficiente i dati strutturati. Usa file di definizione fortemente tipizzato per definire le strutture dei messaggi. Questi file di definizione vengono quindi compilati in codice specifico del linguaggio per serializzare e deserializzare i messaggi. Protobuf può essere usato su meccanismi RPC esistenti oppure può generare un servizio RPC.
Apache Thrift usa un approccio simile, con file di definizione fortemente tipizzato e un passaggio di compilazione per generare il codice di serializzazione e i servizi RPC.
Apache Avro offre funzionalità simili a Buffer protocollo e Thrift, ma non esiste alcun passaggio di compilazione. I dati serializzati invece includono sempre uno schema che descrive la struttura.
JSON è uno standard aperto che usa campi di testo leggibili. Ha un ampio supporto multipiattaforma. JSON non usa schemi di messaggio. Essendo un formato basato su testo, non è molto efficiente in rete. In alcuni casi, tuttavia, potrebbe essere necessario restituire elementi memorizzati nella cache direttamente a un client tramite HTTP, nel qual caso l'archiviazione di JSON potrebbe ridurre il costo della deserializzazione da un altro formato e quindi la serializzazione in JSON.
binary JSON (BSON) è un formato di serializzazione binaria che usa una struttura simile a JSON. BSON è stato progettato per essere leggero, facile da analizzare e veloce da serializzare e deserializzare, rispetto a JSON. I payload sono paragonabili alle dimensioni di JSON. A seconda dei dati, un payload BSON potrebbe essere minore o maggiore di un payload JSON. BSON include alcuni tipi di dati aggiuntivi che non sono disponibili in JSON, in particolare BinData (per matrici di byte) e Date.
MessagePack è un formato di serializzazione binaria progettato per essere compatto per la trasmissione in rete. Non sono presenti schemi di messaggio o controllo del tipo di messaggio.
Bond è un framework multipiattaforma per l'uso di dati schematizzati. Supporta la serializzazione e la deserializzazione tra linguaggi. Le differenze rilevanti rispetto ad altri sistemi elencati di seguito sono il supporto per l'ereditarietà, gli alias di tipo e i generics.
gRPC è un sistema RPC open source sviluppato da Google. Per impostazione predefinita, usa i buffer del protocollo come linguaggio di definizione e il formato dell'interscambio dei messaggi sottostante.
Passaggi successivi
- documentazione di Azure Cache per Redis
- Domande frequenti su cache di Azure per Redis
- Modello asincrono basato su attività
- Documentazione di Redis
- StackExchange.Redis
- Guida al partizionamento dei dati
" output is necessary.)
I modelli seguenti potrebbero essere rilevanti anche per lo scenario in cui si implementa la memorizzazione nella cache nelle applicazioni:
Modello cache-aside: questo modello descrive come caricare i dati su richiesta in una cache da un archivio dati. Questo modello consente anche di mantenere la coerenza tra i dati contenuti nella cache e i dati nell'archivio dati originale.
Il modello di partizionamento orizzontale fornisce informazioni sull'implementazione del partizionamento orizzontale per migliorare la scalabilità durante l'archiviazione e l'accesso a grandi volumi di dati.