Linguaggio di query a oggetti per cache distribuita

Autore: Iqbal Khan

NCache ti consente di creare una cache distribuita scalabile nel livello intermedio in modo da poter ridurre i viaggi costosi al database e migliorare notevolmente le prestazioni delle tue applicazioni. Migliora inoltre la scalabilità dell'applicazione perché è possibile trovare i dati utilizzati di frequente in questa cache altamente scalabile anziché in un singolo server di database che non è in grado di scalare molto bene.

La tua applicazione in genere utilizza una cache come Hashtable in cui tutto è archiviato in base a una chiave e devi avere questa chiave per recuperare un elemento. È come avere un database relazionale in cui puoi usare solo la chiave primaria per trovare i dati. Funziona bene in molte situazioni, ma in un'applicazione complessa della vita reale l'applicazione spesso ha bisogno di trovare dati basati su attributi diversi dalla chiave primaria. E, dal momento che stai conservando molti dei tuoi dati nella cache, sarebbe molto utile se potessi cercare anche nella cache in questo modo. NCache fornisce esattamente tale struttura.

In questo articolo parlerò di come NCache l'interrogazione degli oggetti funziona.

Ricerca cache chiavi restituite

NCache fornisce un Object Query Language (OQL) per consentirti di eseguire ricerche nella cache. Devi effettuare chiamate API e specificare una ricerca basata su questo OQL per recuperare una raccolta di oggetti dalla cache. Ecco cosa devi usare nella tua applicazione .NET per interrogare la cache.

public class Program
{
    public static void Main(string[] args)
    {
        NCache.InitializeCache("myReplicatedCache");
        String query = "SELECT NCacheQuerySample.Business.Product WHERE this.ProductID > 100";
        // Fetch the keys matching this search criteria
        ICollection keys = NCache.Cache.Search(query);
        if (keys.Count > 0)
        {
            IEnumerator ie = keys.GetEnumerator();
            while (ie.MoveNext())
            {
                String key = (String)ie.Current;
                Product prod = (Product)NCache.Cache.Get(key);

                HandleProduct(prod);
                Console.WriteLine("ProductID: {0}", prod.ProductID);
            }
        }
        NCache.Cache.Dispose();
    }
} 

Il codice precedente cerca nella cache e restituisce una raccolta di chiavi. Quindi, esegue l'iterazione su tutte le chiavi restituite e recupera individualmente gli elementi memorizzati nella cache corrispondenti dalla cache. Il vantaggio di questo approccio è che la query non restituisce automaticamente molti dati e restituisce invece solo chiavi. Quindi, l'applicazione client può determinare quali chiavi desidera recuperare. Lo svantaggio di questo approccio è che se hai comunque intenzione di recuperare la maggior parte degli elementi dalla cache, finirai per fare molti viaggi nella cache. E, se la cache viene distribuita, questo potrebbe diventare costoso. In tal caso, puoi condurre una ricerca che restituisca le chiavi e gli elementi insieme.

Ricerca nella cache Articoli restituiti

Come hai già visto, un semplice Cache.Search(...) restituisce una raccolta di chiavi. Tuttavia, se intendi recuperare tutti o la maggior parte degli elementi memorizzati nella cache associati a queste chiavi, Cache.Search(...) non è un modo molto efficiente per cercare nella cache. Il motivo è che prima effettuerai una chiamata per eseguire la ricerca. Quindi, effettuerai una serie di chiamate per recuperare gli elementi associati a ciascuna chiave. Questa può diventare un'operazione molto costosa. In queste situazioni, è meglio recuperare tutte le chiavi e gli elementi in una chiamata. Di seguito è riportato l'esempio che fa proprio questo.

public class Program
{
    public static void Main(string[] args)
    {
        NCache.InitializeCache("myReplicatedCache");
        String query = "SELECT NCacheQuerySample.Business.Product WHERE this.ProductID > 100";
        // Fetch the keys matching this search criteria
        IDictionary dict = NCache.Cache.SearchEntries(query);
        if (dict.Count > 0)
        {
            IDictionaryEnumerator ide = dict.GetEnumerator();
            while (ide.MoveNext())
            {
                String key = (String)ide.Key;
                Product prod = (Product)ide.Value;
                HandleProduct(prod);
                Console.WriteLine("Key = {0}, ProductID: {1}",
                key, prod.ProductID);
            }
        }
        NCache.Cache.Dispose();
    }
}

Il codice precedente cerca nella cache e restituisce un dizionario contenente sia le chiavi che i valori. In questo modo, tutti i dati basati sui criteri di ricerca vengono recuperati in una chiamata a NCache. Questo è un modo molto più efficiente per recuperare tutti i dati dalla cache rispetto a farlo Cache.Search().

Indicizzazione degli attributi ricercabili

Tieni presente che NCache richiede che tutti gli attributi ricercabili siano indicizzati. Questo perché senza indicizzazione, NCache dovrebbe attraversare l'intera cache per trovare gli elementi che l'utente sta cercando. E questa è un'operazione molto costosa con il potenziale di rallentare l'intera cache e annullare il motivo principale per cui le persone lo farebbero NCache, in particolare per aumentare le prestazioni e la scalabilità delle applicazioni.

NCache fornisce il proprio meccanismo di indicizzazione. È possibile identificare gli oggetti negli assembly .NET che si desidera indicizzare. Quindi, quando aggiungi dati a NCache, controlla se stai aggiungendo questi oggetti. E, se lo sei, usa .NET Reflection per estrarre i dati dagli attributi indicizzati e crea il suo indice interno. Quindi, quando esegui query sulla cache con Cache.Search() o Cache.SearchEntries(), NCache utilizza questi indici per trovare rapidamente gli oggetti desiderati e te li restituisce.

Indicizzazione degli attributi degli oggetti prima di avviare la cache
Figura 1: indicizzazione degli attributi degli oggetti prima di avviare la cache

Si noti che ogni volta che si specificano gli indici sugli attributi degli oggetti, si aggiunge un po' al tempo di elaborazione per le operazioni di aggiunta, inserimento e rimozione. Tuttavia, l'operazione Get non è influenzata.

Interrogazione di diverse topologie di memorizzazione nella cache

Sebbene il comportamento di ricerca dal punto di vista dell'applicazione client sia lo stesso indipendentemente dalle topologie di memorizzazione nella cache in uso, gli interni della ricerca variano da topologia a topologia. Ad esempio, se stai eseguendo una ricerca su una cache replicata, la ricerca viene eseguita interamente sul server cache da cui hai avviato la ricerca. Questo perché l'intera cache è disponibile lì. Ecco come viene eseguita una query in una cache replicata.

La query viene eseguita in locale su un server
Figura 2: la query viene eseguita in locale su un server

Tuttavia, se si dispone di una topologia di memorizzazione nella cache della replica partizionata o partizionata, non tutti i dati risiedono su un singolo nodo della cache nel cluster. In questa situazione, il server cache in cui viene avviata la query invia la query a tutti gli altri server del cluster e la esegue anche localmente. La query viene quindi eseguita in tutti i server in parallelo ei suoi risultati vengono restituiti da tutti i nodi a questo nodo del server di origine. Questo nodo del server combina quindi tutti i risultati (esegue una "unione") e li restituisce al client. Di seguito è riportato un diagramma che mostra tutto questo.

La query viene eseguita in parallelo su tutti i nodi del server
Figura 3: la query viene eseguita in parallelo su tutti i nodi del server

I tuoi assembly .NET

In NCache, l'indicizzazione viene eseguita sui nodi del server. Tuttavia, NCache utilizza .NET Reflection sul lato client per estrarre i valori degli attributi degli oggetti e li invia al server. Pertanto, gli assembly .NET che contengono la definizione dell'oggetto sono necessari solo sul lato client in cui risiede l'applicazione. Indipendentemente dal fatto che tu stia eseguendo in modalità InProc o OutProc, gli assembly devono trovarsi in una directory a cui l'applicazione client possa accedervi.

Inoltre, NCache supporta anche il linguaggio di query degli oggetti per i client Java.

Sintassi del linguaggio di query

NCache supporta un linguaggio simile a SQL chiamato Object Query Language (OQL). Questa lingua ha la seguente sintassi in NCache.

SELECT NCacheQuerySample.Business.Product WHERE this.ProductID > 100;

SELECT NCacheQuerySample.Business.Product WHERE (this.ProductID != 100 AND this.ProductID <= 200);

SELECT NCacheQuerySample.Business.Product WHERE (this.ProductID == 150 OR this.ProductID == 160);

SELECT NCacheQuerySample.Business.Product WHERE this.ProductID IN (1, 4, 7, 10);

SELECT NCacheQuerySample.Business.Product WHERE this.ProductID NOT IN (1, 4, 7, 10);

SELECT NCacheQuerySample.Business.Employee WHERE this.HiringDate > DateTime.now;

SELECT NCacheQuerySample.Business.Employee WHERE this.HiringDate > DateTime ('01/01/2007');

SELECT NCacheQuerySample.Business.Employee WHERE this.Hired = true;

È possibile combinare più espressioni insieme con AND e OR utilizzando le parentesi nidificate. La grammatica completa della sintassi del linguaggio di query è specificata di seguito.

<Query>                 ::= SELECT <ObjectType> 
                          | SELECT <ObjectType> WHERE <Expression>

<Expression>            ::= <OrExpr>

<OrExpr>                ::= <OrExpr> 'OR' <AndExpr>
                          | <AndExpr>

<AndExpr>               ::= <AndExpr> 'AND' <UnaryExpr>
                          | <UnaryExpr>

<UnaryExpr>             ::= 'NOT'  <CompareExpr>
                          | <CompareExpr>

<CompareExpr>           ::= <Atrrib> '='  <Value>
                          | <Atrrib> '!=' <Value>
                          | <Atrrib> '==' <Value>
                          | <Atrrib> '<>' <Value>
                          | <Atrrib> '<'  <Value>
                          | <Atrrib> '>'  <Value>
                          | <Atrrib> '<=' <Value>
                          | <Atrrib> '>=' <Value>
                          | <Atrrib> 'IN' <InList>
                          | <Atrrib> 'NOT' 'IN' <InList>
                          | '(' <Expression> ')'

<Atrrib>		::= <ObjectValue>                                                    

<Value>                 ::= '-' <NumLiteral> 
                          | <NumLiteral> 
                          | <StrLiteral>
                          | 'true'
                          | 'false'
                          | <Date>

<Date>                  ::= 'DateTime' '.' 'now'
                          | 'DateTime' '(' StringLiteral ')'

<StrLiteral>		::= StringLiteral
			  | 'null'  

<NumLiteral>            ::= IntegerLiteral 
                          | RealLiteral

<ObjectType>            ::= '*' 
                          | <Property>

<Property>		::= <Property> '.' Identifier
			  | Identifier

<ObjectValue>           ::= Keyword '.' Identifier              

<InList>                ::= '(' <ListType> ')'

<ListType>              ::= <NumLiteralList>
                          | <StrLiteralList>
                          | <DateList>

<NumLiteralList>        ::=  <NumLiteral> ',' <NumLiteralList>
                          | <NumLiteral>

<StrLiteralList>        ::= <StrLiteral> ',' <StrLiteralList>
                          | <StrLiteral>

<DateList>              ::= <Date> ',' <DateList>
                          | <Date> 

Conclusione

Come hai visto NCache rende molto semplice interrogare la cache distribuita. Questa è una potente funzionalità che ti consente di utilizzare la tua cache in un modo più significativo e trovare le cose al suo interno più facilmente. Controlla.


Autore: Iqbal Khan lavora per Alachisoft , azienda di software leader nella fornitura di soluzioni di caching distribuito .NET e Java, mappatura O/R e ottimizzazione dello storage di SharePoint. Puoi raggiungerlo a iqbal@alachisoft.com.

© Copyright Alachisoft 2002 - . Tutti i diritti riservati. NCache è un marchio registrato di Diyatech Corp.