Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Cachelagring är en vanlig teknik för att förbättra prestanda och skalbarhet i system. Den cachelagrar data genom att tillfälligt kopiera data som används ofta till snabb lagring som finns nära programmet. Om den snabba datalagringen ligger närmare än originalkällan kan cachelagring förbättra svarstiden för klientprogram betydligt. Det går helt enkelt att komma åt data snabbare.
Cachelagring är mest effektivt när en klientinstans upprepade gånger läser samma data, särskilt om alla följande villkor gäller för det ursprungliga datalagret:
- Det är fortfarande relativt statiskt.
- det är långsamt jämfört med cacheminnet
- det har hög konkurrensnivå
- Det är långt borta när nätverksfördröjningen kan göra att åtkomsten blir långsam.
Cachelagring i distribuerade program
Distribuerade program implementerar vanligtvis någon eller båda av följande strategier vid cachelagring av data:
- De använder en privat cache, där data lagras lokalt på datorn som kör en instans av ett program eller en tjänst.
- De använder en delad cache som fungerar som en gemensam källa som kan nås av flera processer och datorer.
I båda fallen kan cachelagring utföras på klientsidan och på serversidan. Cachelagring på klientsidan utförs av den process som tillhandahåller användargränssnittet för ett system, till exempel en webbläsare eller ett skrivbordsprogram. Cachelagring på serversidan utförs av den process som tillhandahåller de affärstjänster som körs via fjärranslutning.
Private caching
Den mest grundläggande typen av cacheminne är ett minnesinternt arkiv. Den lagras i adressutrymmet för en enda process och nås direkt av koden som körs i den processen. Den här typen av cache är snabb att komma åt. Det kan också vara ett effektivt sätt att lagra blygsamma mängder statiska data. Storleken på en cache begränsas vanligtvis av mängden minne som är tillgängligt på den dator som är värd för processen.
Om du behöver cachelagras mer information än vad som är fysiskt möjligt i minnet kan du skriva cachelagrade data till det lokala filsystemet. Den här processen går långsammare att komma åt än data som lagras i minnet, men den bör fortfarande vara snabbare och mer tillförlitlig än att hämta data i ett nätverk.
Om du har flera instanser av ett program som använder den här modellen samtidigt, har varje programinstans en egen oberoende cache med en egen kopia av data.
Tänk på en cache som en ögonblicksbild av ursprungliga data någon gång tidigare. Om dessa data inte är statiska är det troligt att olika programinstanser har olika versioner av data i sina cacheminnen. Därför kan samma fråga som utförs av dessa instanser returnera olika resultat, som visas i bild 1.
Bild 1: Använda en minnesintern cache i olika instanser av ett program.
Shared caching
Om du använder en delad cache kan det bidra till att minska oron för att data kan skilja sig åt i varje cache, vilket kan inträffa vid minnesintern cachelagring. Delad cachelagring säkerställer att olika programinstanser ser samma vy över cachelagrade data. Cacheminnet hittas på en separat plats, som vanligtvis finns som en del av en separat tjänst, enligt bild 2.
Bild 2: Använda en delad cache.
En viktig fördel med metoden för delad cachelagring är den skalbarhet som den ger. Många delade cachetjänster implementeras med hjälp av ett kluster med servrar och använder programvara för att distribuera data över klustret transparent. En programinstans skickar helt enkelt en begäran till cachetjänsten. Den underliggande infrastrukturen avgör platsen för cachelagrade data i klustret. Du kan enkelt skala cachen genom att lägga till fler servrar.
Det finns två huvudsakliga nackdelar med metoden för delad cachelagring:
- Cachen är långsammare att komma åt eftersom den inte längre lagras lokalt för varje programinstans.
- Kravet på att implementera en separat cachetjänst kan öka komplexiteten i lösningen.
Överväganden för att använda cachelagring
I följande avsnitt beskrivs mer detaljerat vad du bör tänka på när du utformar och använder en cache.
Bestämma när data ska cachelagrats
Cachelagring kan avsevärt förbättra prestanda, skalbarhet och tillgänglighet. Ju mer data du har och ju större antal användare som behöver komma åt dessa data, desto större blir fördelarna med cachelagring. Cachelagring minskar svarstiden och konkurrensen som är associerad med hantering av stora mängder samtidiga begäranden i det ursprungliga datalagret.
En databas kan till exempel ha stöd för ett begränsat antal samtidiga anslutningar. Om du hämtar data från en delad cache i stället för den underliggande databasen kan ett klientprogram komma åt dessa data även om antalet tillgängliga anslutningar för närvarande är förbrukat. Om databasen blir otillgänglig kan klientprogram dessutom fortsätta med hjälp av de data som lagras i cacheminnet.
Överväg att cachelagra data som läses ofta men ändras sällan (till exempel data som har en högre andel läsåtgärder än skrivåtgärder). Vi rekommenderar dock inte att du använder cacheminnet som auktoritativt arkiv för kritisk information. Se i stället till att alla ändringar som programmet inte har råd att förlora alltid sparas i ett beständigt datalager. Om cacheminnet inte är tillgängligt kan programmet fortfarande fortsätta att fungera med hjälp av datalagret och du förlorar inte viktig information.
Fastställa hur data cachelagrades effektivt
Nyckeln till att använda en cache ligger i att fastställa lämpligaste data att cachelagra och cachelagra dem vid rätt tidpunkt. Data kan läggas till i cacheminnet på begäran första gången de hämtas av ett program. Programmet behöver bara hämta data en gång från datalagret och att efterföljande åtkomst kan uppfyllas med hjälp av cachen.
Alternativt kan en cache helt eller delvis fyllas med data i förväg, vanligtvis när programmet startar (en metod som kallas seeding). Det kanske dock inte är lämpligt att implementera seeding för en stor cache eftersom den här metoden kan medföra en plötslig, hög belastning på det ursprungliga datalagret när programmet börjar köras.
Ofta kan en analys av användningsmönster hjälpa dig att avgöra om du helt eller delvis ska fylla i en cache och välja vilka data som ska cachelageras. Du kan till exempel använda cacheminnet med statiska användarprofildata för kunder som använder programmet regelbundet (kanske varje dag), men inte för kunder som bara använder programmet en gång i veckan.
Cachelagring fungerar vanligtvis bra med data som inte kan ändras eller som ändras sällan. Exempel är referensinformation som produkt- och prisinformation i ett e-handelsprogram eller delade statiska resurser som är kostsamma att konstruera. Vissa eller alla dessa data kan läsas in i cacheminnet vid programstart för att minimera efterfrågan på resurser och förbättra prestandan. Du kanske också vill ha en bakgrundsprocess som regelbundet uppdaterar referensdata i cacheminnet för att säkerställa att de är up-to-date. Eller så kan bakgrundsprocessen uppdatera cacheminnet när referensdata ändras.
Cachelagring är mindre användbart för dynamiska data, även om det finns vissa undantag till detta (mer information finns i avsnittet Cachelagra mycket dynamiska data senare i den här artikeln). När de ursprungliga data ändras regelbundet blir antingen den cachelagrade informationen inaktuell snabbt eller omkostnaderna för att synkronisera cacheminnet med det ursprungliga datalagret minskar cachelagringens effektivitet.
En cache behöver inte innehålla fullständiga data för en entitet. Om ett dataobjekt till exempel representerar ett flervärdesobjekt, till exempel en bankkund med namn, adress och kontosaldo, kan vissa av dessa element förbli statiska, till exempel namn och adress. Andra element, till exempel kontosaldot, kan vara mer dynamiska. I dessa situationer kan det vara användbart att cachelagrar statiska delar av data och hämtar (eller beräknar) endast återstående information när den behövs.
Vi rekommenderar att du utför prestandatestning och användningsanalys för att avgöra om prepopulering eller inläsning på begäran av cachen, eller en kombination av båda, är lämplig. Beslutet bör baseras på datans volatilitet och användningsmönster. Cacheanvändning och prestandaanalys är viktiga i program som drabbas av stora belastningar och måste vara mycket skalbara. I mycket skalbara scenarier kan du till exempel använda cacheminnet för att minska belastningen på datalagret vid tider med hög belastning.
Cachelagring kan också användas för att undvika upprepade beräkningar medan programmet körs. Om en åtgärd transformerar data eller utför en komplicerad beräkning kan den spara resultatet av åtgärden i cacheminnet. Om samma beräkning krävs efteråt kan programmet helt enkelt hämta resultaten från cacheminnet.
Ett program kan ändra data som lagras i en cache. Vi rekommenderar dock att du tänker på cachen som ett tillfälligt datalager som kan försvinna när som helst. Lagra inte endast värdefulla data i cacheminnet. se till att du även behåller informationen i det ursprungliga datalagret. Det innebär att om cacheminnet blir otillgängligt minimerar du risken för att förlora data.
Cachelagrade mycket dynamiska data
När du lagrar snabbt föränderlig information i ett beständigt datalager kan det medföra ett omkostnader för systemet. Tänk dig till exempel en enhet som kontinuerligt rapporterar status eller någon annan mätning. Om ett program väljer att inte cachelagrar dessa data på grund av att den cachelagrade informationen nästan alltid kommer att vara inaktuell, kan samma överväganden vara sant när du lagrar och hämtar den här informationen från datalagret. Under den tid det tar att spara och hämta dessa data kan de ha ändrats.
I en sådan situation bör du överväga fördelarna med att lagra dynamisk information direkt i cacheminnet i stället för i det beständiga datalagret. Om data är icke-kritiska och inte kräver granskning spelar det ingen roll om enstaka ändring går förlorad.
Hantera förfallodatum för data i en cache
I de flesta fall är data som lagras i en cache en kopia av data som lagras i det ursprungliga datalagret. Data i det ursprungliga datalagret kan ändras när de har cachelagrats, vilket gör att cachelagrade data blir inaktuella. Med många cachelagringssystem kan du konfigurera cacheminnet så att data upphör att gälla och minska den period för vilken data kan vara inaktuella.
När cachelagrade data upphör att gälla tas de bort från cacheminnet och programmet måste hämta data från det ursprungliga datalagret (det kan placera den nyligen hämtade informationen i cacheminnet igen). Du kan ange en standardprincip för förfallodatum när du konfigurerar cacheminnet. I många cachetjänster kan du också ange förfalloperioden för enskilda objekt när du lagrar dem programmatiskt i cacheminnet. Med vissa cacheminnen kan du ange förfalloperioden som ett absolut värde, eller som ett glidande värde som gör att objektet tas bort från cacheminnet om det inte nås inom den angivna tiden. Den här inställningen åsidosätter alla förfalloprinciper för hela cachen, men bara för de angivna objekten.
Note
Överväg förfalloperioden för cacheminnet och de objekt som den innehåller noggrant. Om du gör det för kort går objekten ut för snabbt och du minskar fördelarna med att använda cacheminnet. Om du gör perioden för lång riskerar du att data blir inaktuella.
Det är också möjligt att cachen kan fyllas i om data tillåts vara kvar under en längre tid. I det här fallet kan alla begäranden om att lägga till nya objekt i cacheminnet leda till att vissa objekt med två skäl tas bort i en process som kallas borttagning. Cachetjänster avlägsnar vanligtvis data på LRU-basis (minst nyligen använda), men du kan vanligtvis åsidosätta den här principen och förhindra att objekt avlägsnas. Men om du använder den här metoden riskerar du att överskrida det minne som är tillgängligt i cacheminnet. Ett program som försöker lägga till ett objekt i cacheminnet misslyckas med ett undantag.
Vissa cachelagringsimplementeringar kan ge ytterligare borttagningsprinciper. Det finns flera typer av borttagningsprinciper. These include:
- En princip som används senast (i förväntningen att data inte kommer att krävas igen).
- En först-in-först-ut-princip (äldsta data tas bort först).
- En explicit borttagningsprincip baserat på en utlöst händelse (till exempel de data som ändras).
Ogiltigförklara data i en cache på klientsidan
Data som lagras i en cache på klientsidan anses vanligtvis ligga utanför den tjänst som tillhandahåller data till klienten. En tjänst kan inte direkt tvinga en klient att lägga till eller ta bort information från en cache på klientsidan.
Det innebär att det är möjligt för en klient som använder en dåligt konfigurerad cache att fortsätta använda inaktuell information. Om till exempel förfalloprinciperna för cachen inte implementeras korrekt kan en klient använda inaktuell information som cachelagras lokalt när informationen i den ursprungliga datakällan har ändrats.
Om du skapar ett webbprogram som hanterar data via en HTTP-anslutning kan du implicit tvinga en webbklient (till exempel en webbläsare eller webbproxy) att hämta den senaste informationen. Du kan göra detta om en resurs uppdateras genom en ändring i resursens URI. Webbklienter använder vanligtvis URI:n för en resurs som nyckel i cacheminnet på klientsidan, så om URI:n ändras ignorerar webbklienten alla tidigare cachelagrade versioner av en resurs och hämtar den nya versionen i stället.
Hantera samtidighet i en cache
Cacheminnen är ofta utformade för att delas av flera instanser av ett program. Varje programinstans kan läsa och ändra data i cacheminnet. Samma samtidighetsproblem som uppstår med ett delat datalager gäller därför även för ett cacheminne. I en situation där ett program behöver ändra data som lagras i cacheminnet kan du behöva se till att uppdateringar som görs av en instans av programmet inte skriver över de ändringar som görs av en annan instans.
Beroende på typen av data och sannolikheten för kollisioner kan du använda någon av två metoder för samtidighet:
- Optimistic. Omedelbart innan data uppdateras kontrollerar programmet om data i cacheminnet har ändrats sedan de hämtades. Om data fortfarande är desamma kan ändringen göras. Annars måste programmet bestämma om det ska uppdateras. (Affärslogik som styr det här beslutet är programspecifik.) Den här metoden är lämplig för situationer där uppdateringar är ovanliga eller där kollisioner sannolikt inte inträffar.
- Pessimistic. När det hämtar data låser programmet dem i cacheminnet för att förhindra att en annan instans ändrar dem. Den här processen säkerställer att kollisioner inte kan inträffa, men de kan också blockera andra instanser som behöver bearbeta samma data. Pessimistisk samtidighet kan påverka skalbarheten för en lösning och rekommenderas endast för kortvariga åtgärder. Den här metoden kan vara lämplig för situationer där kollisioner är mer sannolika, särskilt om ett program uppdaterar flera objekt i cacheminnet och måste se till att dessa ändringar tillämpas konsekvent.
Implementera hög tillgänglighet och skalbarhet och förbättra prestanda
Undvik att använda en cache som den primära lagringsplatsen för data. det här är rollen för det ursprungliga datalagret som cacheminnet fylls i från. Det ursprungliga datalagret ansvarar för att säkerställa datans beständighet.
Var noga med att inte introducera kritiska beroenden om tillgängligheten för en delad cachetjänst i dina lösningar. Ett program bör kunna fortsätta fungera om tjänsten som tillhandahåller den delade cachen inte är tillgänglig. Programmet får inte svara eller misslyckas i väntan på att cachetjänsten ska återupptas.
Därför måste programmet vara förberett för att identifiera tillgängligheten för cachetjänsten och återgå till det ursprungliga datalagret om cachen är otillgänglig. The Circuit-Breaker pattern is useful for handling this scenario. The service that provides the cache can be recovered, and once it becomes available, the cache can be repopulated as data is read from the original data store, following a strategy such as the Cache-aside pattern.
Systemets skalbarhet kan dock påverkas om programmet återgår till det ursprungliga datalagret när cacheminnet är tillfälligt otillgängligt. Medan datalagret återställs kan det ursprungliga datalagret översvämmas av begäranden om data, vilket resulterar i tidsgränser och misslyckade anslutningar.
Överväg att implementera en lokal, privat cache i varje instans av ett program, tillsammans med den delade cache som alla programinstanser har åtkomst till. När programmet hämtar ett objekt kan det först checka in sin lokala cache, sedan i den delade cachen och slutligen i det ursprungliga datalagret. Den lokala cachen kan fyllas i med hjälp av data i antingen den delade cachen eller i databasen om den delade cachen inte är tillgänglig.
Den här metoden kräver noggrann konfiguration för att förhindra att den lokala cachen blir för inaktuell när det gäller den delade cachen. Den lokala cachen fungerar dock som en buffert om den delade cachen inte kan nås. Bild 3 visar den här strukturen.
Bild 3: Använda en lokal privat cache med en delad cache.
För att stödja stora cacheminnen som innehåller relativt långlivade data tillhandahåller vissa cachetjänster ett alternativ med hög tillgänglighet som implementerar automatisk redundans om cachen blir otillgänglig. Den här metoden innebär vanligtvis att replikera cachelagrade data som lagras på en primär cacheserver till en sekundär cacheserver och växla till den sekundära servern om den primära servern misslyckas eller anslutningen går förlorad.
För att minska svarstiden som är associerad med att skriva till flera mål kan replikeringen till den sekundära servern ske asynkront när data skrivs till cachen på den primära servern. Den här metoden leder till att viss cachelagrad information kan gå förlorad om det uppstår ett fel, men andelen av dessa data bör vara liten jämfört med cachens totala storlek.
Om en delad cache är stor kan det vara fördelaktigt att partitioneras cachelagrade data mellan noder för att minska risken för konkurrens och förbättra skalbarheten. Många delade cacheminnen stöder möjligheten att dynamiskt lägga till (och ta bort) noder och balansera om data mellan partitioner. Den här metoden kan omfatta klustring, där samlingen av noder visas för klientprogram som en sömlös och enkel cache. Internt sprids dock data mellan noder efter en fördefinierad distributionsstrategi som balanserar belastningen jämnt. Mer information om möjliga partitioneringsstrategier finns i Vägledning för datapartitionering.
Klustring kan också öka cachens tillgänglighet. Om en nod misslyckas är resten av cacheminnet fortfarande tillgängligt. Klustring används ofta tillsammans med replikering och redundans. Varje nod kan replikeras och repliken kan snabbt anslutas om noden misslyckas.
Många läs- och skrivåtgärder kommer sannolikt att omfatta enskilda datavärden eller objekt. Ibland kan det dock vara nödvändigt att lagra eller hämta stora mängder data snabbt. Till exempel kan seeding av en cache innebära att du skriver hundratals eller tusentals objekt till cacheminnet. Ett program kan också behöva hämta ett stort antal relaterade objekt från cacheminnet som en del av samma begäran.
Många storskaliga cacheminnen tillhandahåller batchåtgärder för dessa ändamål. Detta gör det möjligt för ett klientprogram att paketera en stor mängd objekt i en enda begäran och minskar de kostnader som är associerade med att utföra ett stort antal små begäranden.
Cachelagring och slutlig konsekvens
För att cache-aside-mönstret ska fungera måste instansen av programmet som fyller i cacheminnet ha åtkomst till den senaste och konsekventa versionen av data. I ett system som implementerar eventuell konsekvens (till exempel ett replikerat datalager) kanske detta inte är fallet.
En instans av ett program kan ändra ett dataobjekt och ogiltigförklara den cachelagrade versionen av objektet. En annan instans av programmet kan försöka läsa det här objektet från en cache, vilket orsakar en cachemiss, så att den läser data från datalagret och lägger till det i cacheminnet. Men om datalagret inte har synkroniserats helt med de andra replikerna kan programinstansen läsa och fylla i cacheminnet med det gamla värdet.
Mer information om hur du hanterar datakonsekvens finns i Primer för datakonsekvens.
Skydda cachelagrade data
Oavsett vilken cachetjänst du använder bör du överväga hur du skyddar data som lagras i cacheminnet från obehörig åtkomst. Det finns två huvudsakliga problem:
- Sekretessen för data i cacheminnet.
- Datasekretessen när den flödar mellan cacheminnet och det program som använder cacheminnet.
För att skydda data i cacheminnet kan cachetjänsten implementera en autentiseringsmekanism som kräver att program anger följande:
- Vilka identiteter som kan komma åt data i cacheminnet.
- Vilka åtgärder (läsning och skrivning) som dessa identiteter tillåts utföra.
När en identitet har beviljats skriv- eller läsåtkomst till cacheminnet kan identiteten använda alla data i cacheminnet för att minska kostnaderna för läsning och skrivning av data.
Om du behöver begränsa åtkomsten till delmängder av cachelagrade data kan du göra något av följande:
- Dela upp cachen i partitioner (med hjälp av olika cacheservrar) och bevilja endast åtkomst till identiteter för de partitioner som de ska tillåtas att använda.
- Kryptera data i varje delmängd med hjälp av olika nycklar och ange endast krypteringsnycklarna till identiteter som ska ha åtkomst till varje delmängd. Ett klientprogram kanske fortfarande kan hämta alla data i cacheminnet, men det kan bara dekryptera de data som det har nycklar för.
Du måste också skydda data när de flödar in och ut ur cacheminnet. För att göra detta är du beroende av de säkerhetsfunktioner som tillhandahålls av nätverksinfrastrukturen som klientprogram använder för att ansluta till cachen. Om cachen implementeras med hjälp av en på-plats-server i samma organisation som är värd för klientprogrammen, kanske isoleringen av själva nätverket inte kräver att du vidtar ytterligare åtgärder. Om cachen finns på distans och kräver en TCP- eller HTTP-anslutning via ett offentligt nätverk (till exempel Internet) bör du överväga att implementera SSL.
Överväganden för att implementera cachelagring i Azure
Azure Cache for Redis är en implementering av Redis-cachen med öppen källkod som körs som en tjänst i ett Azure-datacenter. Den tillhandahåller en cachelagringstjänst som kan nås från alla Azure-program, oavsett om programmet implementeras som en molntjänst, en webbplats eller på en virtuell Azure-dator. Cacheminnen kan delas av klientprogram som har rätt åtkomstnyckel.
Azure Cache for Redis är en högpresterande cachelagringslösning som ger tillgänglighet, skalbarhet och säkerhet. Den körs vanligtvis som en tjänst spridd över en eller flera dedikerade datorer. Den försöker lagra så mycket information som möjligt i minnet för att säkerställa snabb åtkomst. Den här arkitekturen är avsedd att ge låg svarstid och högt dataflöde genom att minska behovet av att utföra långsamma I/O-åtgärder.
Azure Cache for Redis är kompatibelt med många av de olika API:er som används av klientprogram. Om du har befintliga program som redan använder Azure Cache for Redis som körs lokalt tillhandahåller Azure Cache for Redis en snabb migreringsväg till cachelagring i molnet.
Funktioner i Redis
Redis är mer än en enkel cacheserver. Den tillhandahåller en distribuerad minnesintern databas med en omfattande kommandouppsättning som stöder många vanliga scenarier. Dessa beskrivs senare i det här dokumentet i avsnittet Använda Redis-cachelagring. I det här avsnittet sammanfattas några av de viktigaste funktionerna som Redis tillhandahåller.
Redis som en minnesintern databas
Redis stöder både läs- och skrivåtgärder. I Redis kan skrivningar skyddas från systemfel antingen genom att lagras regelbundet i en lokal ögonblicksbildfil eller i en loggfil med endast tillägg. Den här situationen är inte fallet i många cacheminnen, vilket bör betraktas som övergående datalager.
Alla skrivningar är asynkrona och blockerar inte klienter från att läsa och skriva data. När Redis börjar köras läser den data från ögonblicksbilden eller loggfilen och använder dem för att konstruera minnesintern cache. For more information, see Redis persistence on the Redis website.
Note
Redis garanterar inte att alla skrivningar sparas om det uppstår ett oåterkalleligt fel, men i värsta fall kan du förlora bara några sekunders data. Kom ihåg att en cache inte är avsedd att fungera som en auktoritativ datakälla, och det är program som använder cachen för att säkerställa att kritiska data sparas korrekt i ett lämpligt datalager. For more information, see the Cache-aside pattern.
Redis-datatyper
Redis är ett nyckelvärdeslager där värden kan innehålla enkla typer eller komplexa datastrukturer som hashvärden, listor och uppsättningar. Den stöder en uppsättning atomiska åtgärder för dessa datatyper. Nycklar kan vara permanenta eller taggade med en begränsad time-to-live, då nyckeln och dess motsvarande värde tas bort automatiskt från cacheminnet. Mer information om Redis-nycklar och -värden finns på sidan En introduktion till Redis-datatyper och abstraktioner på Redis webbplats.
Redis-replikering och klustring
Redis stöder primär/underordnad replikering för att säkerställa tillgänglighet och upprätthålla dataflödet. Skrivåtgärder till en primär Redis-nod replikeras till en eller flera underordnade noder. Läsåtgärder kan hanteras av den primära eller någon av de underordnade.
Om du har en nätverkspartition kan underordnade fortsätta att hantera data och sedan transparent synkronisera om med den primära när anslutningen återupprättas. For further details, visit the Replication page on the Redis website.
Redis tillhandahåller också klustring, vilket gör att du transparent kan partitionera data i shards mellan servrar och sprida belastningen. Den här funktionen förbättrar skalbarheten eftersom nya Redis-servrar kan läggas till och data partitioneras om när cachens storlek ökar.
Dessutom kan varje server i klustret replikeras med hjälp av primär/underordnad replikering. Detta säkerställer tillgängligheten för varje nod i klustret. Mer information om klustring och horisontell partitionering finns på självstudiesidan för Redis-kluster på Redis webbplats.
Användning av Redis-minne
En Redis-cache har en begränsad storlek som är beroende av de resurser som är tillgängliga på värddatorn. När du konfigurerar en Redis-server kan du ange den maximala mängd minne som den kan använda. Du kan också konfigurera en nyckel i en Redis-cache så att den har en förfallotid, varefter den tas bort automatiskt från cachen. Den här funktionen kan förhindra att minnesintern cache fylls med gamla eller inaktuella data.
När minnet fylls kan Redis automatiskt ta bort nycklar och deras värden genom att följa ett antal principer. Standardvärdet är LRU (används minst nyligen), men du kan också välja andra principer, till exempel att ta bort nycklar slumpmässigt eller stänga av borttagning helt och hållet (i vilket fall försök att lägga till objekt i cacheminnet misslyckas om det är fullt). Sidan Använda Redis som en LRU-cache innehåller mer information.
Redis-transaktioner och batchar
Redis gör det möjligt för ett klientprogram att skicka en serie åtgärder som läser och skriver data i cacheminnet som en atomisk transaktion. Alla kommandon i transaktionen kommer garanterat att köras sekventiellt och inga kommandon som utfärdas av andra samtidiga klienter kommer att sammanvävas mellan dem.
Det här är dock inte sanna transaktioner eftersom en relationsdatabas skulle utföra dem. Transaktionsbearbetning består av två steg – den första är när kommandona placeras i kö och den andra är när kommandona körs. Under kommandokösteget skickas kommandona som utgör transaktionen av klienten. Om något slags fel inträffar nu (till exempel ett syntaxfel eller fel antal parametrar) vägrar Redis att bearbeta hela transaktionen och tar bort den.
Under körningsfasen utför Redis varje köat kommando i följd. Om ett kommando misslyckas under den här fasen fortsätter Redis med nästa köade kommando och återställer inte effekterna av kommandon som redan har körts. Den här förenklade transaktionsformen bidrar till att upprätthålla prestanda och undvika prestandaproblem som orsakas av konkurrens.
Redis implementerar en form av optimistisk låsning som hjälper till att upprätthålla konsekvens. For detailed information about transactions and locking with Redis, visit the Transactions page on the Redis website.
Redis stöder även icke-transaktionell batchbearbetning av begäranden. Redis-protokollet som klienter använder för att skicka kommandon till en Redis-server gör det möjligt för en klient att skicka en serie åtgärder som en del av samma begäran. Detta kan bidra till att minska paketfragmenteringen i nätverket. När batchen bearbetas utförs varje kommando. Om något av dessa kommandon är felaktigt konfigurerade avvisas de (vilket inte sker med en transaktion), men de återstående kommandona utförs. Det finns inte heller någon garanti för i vilken ordning kommandona i batchen ska bearbetas.
Redis security
Redis fokuserar enbart på att ge snabb åtkomst till data och är utformat för att köras i en betrodd miljö som endast kan nås av betrodda klienter. Redis stöder en begränsad säkerhetsmodell baserat på lösenordsautentisering. (Det går att ta bort autentiseringen helt, även om vi inte rekommenderar detta.)
Alla autentiserade klienter delar samma globala lösenord och har åtkomst till samma resurser. Om du behöver mer omfattande inloggningssäkerhet måste du implementera ditt eget säkerhetslager framför Redis-servern och alla klientbegäranden ska passera genom det här ytterligare lagret. Redis ska inte exponeras direkt för ej betrodda eller oautentiserade klienter.
Du kan begränsa åtkomsten till kommandon genom att inaktivera dem eller byta namn på dem (och genom att endast ge privilegierade klienter de nya namnen).
Redis har inte direkt stöd för någon form av datakryptering, så all kodning måste utföras av klientprogram. Dessutom tillhandahåller Redis ingen form av transportsäkerhet. Om du behöver skydda data när de flödar över nätverket rekommenderar vi att du implementerar en SSL-proxy.
For more information, visit the Redis security page on the Redis website.
Note
Azure Cache for Redis tillhandahåller ett eget säkerhetslager genom vilket klienter ansluter. De underliggande Redis-servrarna exponeras inte för det offentliga nätverket.
Azure Redis-cache
Azure Cache for Redis ger åtkomst till Redis-servrar som finns i ett Azure-datacenter. Den fungerar som en fasad som ger åtkomstkontroll och säkerhet. Du kan etablera en cache med hjälp av Azure-portalen.
Portalen innehåller ett antal fördefinierade konfigurationer. Dessa sträcker sig från en 53 GB cache som körs som en dedikerad tjänst som stöder SSL-kommunikation (för sekretess) och huvud-/underordnad replikering med ett serviceavtal (SLA) på 99,9% tillgänglighet, ned till en cache på 250 MB utan replikering (inga tillgänglighetsgarantier) som körs på delad maskinvara.
Med hjälp av Azure-portalen kan du också konfigurera borttagningsprincipen för cacheminnet och styra åtkomsten till cachen genom att lägga till användare i de roller som tillhandahålls. Dessa roller, som definierar de åtgärder som medlemmar kan utföra, inkluderar Ägare, Deltagare och Läsare. Medlemmar i rollen Ägare har till exempel fullständig kontroll över cachen (inklusive säkerhet) och dess innehåll, medlemmar i rollen Deltagare kan läsa och skriva information i cachen, och medlemmar i rollen Läsare kan bara hämta data från cachen.
De flesta administrativa uppgifter utförs via Azure-portalen. Därför är många av de administrativa kommandon som är tillgängliga i standardversionen av Redis inte tillgängliga, inklusive möjligheten att ändra konfigurationen programmatiskt, stänga av Redis-servern, konfigurera ytterligare underordnade eller med två medel spara data på disk.
Azure-portalen innehåller en praktisk grafisk visning som gör att du kan övervaka cachens prestanda. Du kan till exempel visa antalet anslutningar som görs, antalet begäranden som utförs, mängden läsningar och skrivningar samt antalet cacheträffar jämfört med cachemissar. Med hjälp av den här informationen kan du fastställa cachens effektivitet och vid behov växla till en annan konfiguration eller ändra borttagningsprincipen.
Dessutom kan du skapa aviseringar som skickar e-postmeddelanden till en administratör om ett eller flera kritiska mått ligger utanför ett förväntat intervall. Du kanske till exempel vill varna en administratör om antalet cachemissar överskrider ett angivet värde under den senaste timmen, eftersom det innebär att cachen kan vara för liten eller att data tas bort för snabbt.
Du kan också övervaka cpu-, minnes- och nätverksanvändningen för cacheminnet.
Mer information och exempel som visar hur du skapar och konfigurerar en Azure Cache for Redis finns på sidan Varv runt Azure Cache for Redis på Azure-bloggen.
Cachelagring av sessionstillstånd och HTML-utdata
Om du skapar ASP.NET webbprogram som körs med hjälp av Azure-webbroller kan du spara information om sessionstillstånd och HTML-utdata i en Azure Cache for Redis. Med sessionstillståndsprovidern för Azure Cache for Redis kan du dela sessionsinformation mellan olika instanser av en ASP.NET webbapp och är mycket användbar i webbgruppssituationer där klient-server-tillhörighet inte är tillgänglig och cachelagring av sessionsdata i minnet inte skulle vara lämpligt.
Att använda sessionstillståndsprovidern med Azure Cache for Redis ger flera fördelar, bland annat:
- Dela sessionstillstånd med ett stort antal instanser av ASP.NET webbprogram.
- Ger förbättrad skalbarhet.
- Stöd för kontrollerad, samtidig åtkomst till samma sessionstillståndsdata för flera läsare och en enskild skrivare.
- Använd komprimering för att spara minne och förbättra nätverksprestanda.
Mer information finns i ASP.NET sessionstillståndsprovider för Azure Cache for Redis.
Note
Använd inte sessionstillståndsprovidern för Azure Cache for Redis med ASP.NET program som körs utanför Azure-miljön. Svarstiden för att komma åt cacheminnet utanför Azure kan eliminera prestandafördelarna med cachelagring av data.
På samma sätt kan du med utdatacacheprovidern för Azure Cache for Redis spara DE HTTP-svar som genereras av en ASP.NET webbapp. Om du använder utdatacacheprovidern med Azure Cache for Redis kan du förbättra svarstiderna för program som renderar komplexa HTML-utdata. Programinstanser som genererar liknande svar kan använda delade utdatafragment i cacheminnet i stället för att generera dessa HTML-utdata på nytt. Mer information finns i ASP.NET cacheprovider för utdata för Azure Cache for Redis.
Skapa en anpassad Redis-cache
Azure Cache for Redis fungerar som en fasad för de underliggande Redis-servrarna. Om du behöver en avancerad konfiguration som inte omfattas av Azure Redis-cachen (till exempel en cache som är större än 53 GB) kan du skapa och vara värd för dina egna Redis-servrar med hjälp av Azure Virtual Machines.
Det här är en potentiellt komplex process eftersom du kan behöva skapa flera virtuella datorer för att fungera som primära och underordnade noder om du vill implementera replikering. Om du vill skapa ett kluster behöver du dessutom flera primärservrar och underordnade servrar. En minimal klustrad replikeringstopologi som ger hög tillgänglighet och skalbarhet består av minst sex virtuella datorer ordnade som tre par primära/underordnade servrar (ett kluster måste innehålla minst tre primära noder).
Varje primärt/underordnat par ska finnas nära varandra för att minimera svarstiden. Varje uppsättning par kan dock köras i olika Azure-datacenter i olika regioner, om du vill hitta cachelagrade data nära de program som mest sannolikt använder dem. Ett exempel på hur du skapar och konfigurerar en Redis-nod som körs som en virtuell Azure-dator finns i Köra Redis på en virtuell CentOS Linux-dator i Azure.
Note
Om du implementerar din egen Redis-cache på det här sättet ansvarar du för att övervaka, hantera och skydda tjänsten.
Partitionera en Redis-cache
Partitionering av cachen innebär att cacheminnet delas över flera datorer. Den här strukturen ger dig flera fördelar jämfört med att använda en enda cacheserver, inklusive:
- Skapa en cache som är mycket större än vad som kan lagras på en enskild server.
- Distribuera data mellan servrar, förbättra tillgängligheten. Om en server misslyckas eller blir otillgänglig är de data som den innehåller inte tillgängliga, men data på de återstående servrarna kan fortfarande nås. För en cache är detta inte avgörande eftersom cachelagrade data bara är en tillfällig kopia av data som lagras i en databas. Cachelagrade data på en server som blir otillgänglig kan cachelagras på en annan server i stället.
- Sprida belastningen mellan servrarna, vilket förbättrar prestanda och skalbarhet.
- Geolokalisera data nära de användare som har åtkomst till dem, vilket minskar svarstiden.
För en cache är den vanligaste formen av partitionering horisontell partitionering. I den här strategin är varje partition (eller shard) en Redis-cache i sig. Data dirigeras till en specifik partition med hjälp av horisontell partitioneringslogik, som kan använda en mängd olika metoder för att distribuera data. The Sharding pattern provides more information about implementing sharding.
Om du vill implementera partitionering i en Redis-cache kan du använda någon av följande metoder:
- Frågeroutning på serversidan. I den här tekniken skickar ett klientprogram en begäran till någon av Redis-servrarna som utgör cachen (förmodligen den närmaste servern). Varje Redis-server lagrar metadata som beskriver den partition som den innehåller och innehåller även information om vilka partitioner som finns på andra servrar. Redis-servern undersöker klientbegäran. Om den kan lösas lokalt utför den begärda åtgärden. Annars vidarebefordras begäran till rätt server. Den här modellen implementeras av Redis-klustring och beskrivs mer detaljerat på redis-klusters självstudiesidan på Redis-webbplatsen. Redis-klustring är transparent för klientprogram och ytterligare Redis-servrar kan läggas till i klustret (och data partitioneras om) utan att du behöver konfigurera om klienterna.
- Client-side partitioning. I den här modellen innehåller klientprogrammet logik (eventuellt i form av ett bibliotek) som dirigerar begäranden till lämplig Redis-server. Den här metoden kan användas med Azure Cache for Redis. Skapa flera Azure Cache for Redis (en för varje datapartition) och implementera logiken på klientsidan som dirigerar begäranden till rätt cache. Om partitioneringsschemat ändras (om till exempel ytterligare Azure Cache for Redis skapas) kan klientprogram behöva konfigureras om.
- Proxy-assisted partitioning. I det här schemat skickar klientprogram begäranden till en mellanliggande proxytjänst som förstår hur data partitioneras och sedan dirigerar begäran till rätt Redis-server. Den här metoden kan också användas med Azure Cache for Redis. proxytjänsten kan implementeras som en Azure-molntjänst. Den här metoden kräver ytterligare en nivå av komplexitet för att implementera tjänsten, och begäranden kan ta längre tid att utföra än att använda partitionering på klientsidan.
Sidan Partitionering: hur du delar upp data mellan flera Redis-instanser på Redis webbplats innehåller ytterligare information om hur du implementerar partitionering med Redis.
Implementera Redis-cacheklientprogram
Redis stöder klientprogram som skrivits på flera programmeringsspråk. Om du skapar nya program med hjälp av .NET Framework rekommenderar vi att du använder StackExchange.Redis-klientbiblioteket. Det här biblioteket innehåller en .NET Framework-objektmodell som sammanfattar informationen för att ansluta till en Redis-server, skicka kommandon och ta emot svar. Den är tillgänglig i Visual Studio som ett NuGet-paket. Du kan använda samma bibliotek för att ansluta till en Azure Cache for Redis eller en anpassad Redis-cache som finns på en virtuell dator.
Om du vill ansluta till en Redis-server använder du klassens Connect statiska ConnectionMultiplexer metod. Anslutningen som den här metoden skapar är utformad för att användas under klientprogrammets livslängd och samma anslutning kan användas av flera samtidiga trådar. Återanslut och koppla inte från varje gång du utför en Redis-åtgärd eftersom detta kan försämra prestandan.
Du kan ange anslutningsparametrarna, till exempel adressen till Redis-värden och lösenordet. Om du använder Azure Cache for Redis är lösenordet antingen den primära eller sekundära nyckeln som genereras för Azure Cache for Redis med hjälp av Azure-portalen.
När du har anslutit till Redis-servern kan du hämta ett handtag på Redis-databasen som fungerar som cacheminne. Redis-anslutningen tillhandahåller metoden GetDatabase för att göra detta. Du kan sedan hämta objekt från cacheminnet och lagra data i cacheminnet med hjälp StringGet av metoderna och StringSet . Dessa metoder förväntar sig en nyckel som en parameter och returnerar objektet antingen i cacheminnet som har ett matchande värde (StringGet) eller lägger till objektet i cachen med den här nyckeln (StringSet).
Beroende på platsen för Redis-servern kan många åtgärder medföra viss svarstid medan en begäran skickas till servern och ett svar returneras till klienten. StackExchange-biblioteket innehåller asynkrona versioner av många av de metoder som det exponerar för att hjälpa klientprogram att förbli dynamiska. Dessa metoder stöder det aktivitetsbaserade asynkrona mönstret i .NET Framework.
Följande kodfragment visar en metod med namnet RetrieveItem. Den illustrerar en implementering av cache-aside-mönstret baserat på Redis och StackExchange-biblioteket. Metoden tar ett strängnyckelvärde och försöker hämta motsvarande objekt från Redis-cachen genom att anropa StringGetAsync metoden (den asynkrona versionen av StringGet).
Om objektet inte hittas hämtas det från den underliggande datakällan med hjälp av GetItemFromDataSourceAsync metoden (som är en lokal metod och inte en del av StackExchange-biblioteket). Den läggs sedan till i cachen med hjälp StringSetAsync av metoden så att den kan hämtas snabbare nästa gång.
// 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;
}
Metoderna StringGet och StringSet är inte begränsade till att hämta eller lagra strängvärden. De kan ta alla objekt som serialiseras som en matris med byte. Om du behöver spara ett .NET-objekt kan du serialisera det som en byteström och använda StringSet metoden för att skriva det till cacheminnet.
På samma sätt kan du läsa ett objekt från cachen med hjälp StringGet av metoden och deserialisera det som ett .NET-objekt. Följande kod visar en uppsättning tilläggsmetoder för IDatabase-gränssnittet ( GetDatabase metoden för en Redis-anslutning returnerar ett IDatabase objekt) och en exempelkod som använder dessa metoder för att läsa och skriva ett BlogPost objekt till cachen:
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;
}
}
Följande kod illustrerar en metod med namnet RetrieveBlogPost som använder dessa tilläggsmetoder för att läsa och skriva ett serialiserbart BlogPost objekt till cachen enligt cache-aside-mönstret:
// 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 har stöd för kommandopipelinering om ett klientprogram skickar flera asynkrona begäranden. Redis kan multiplexa begäranden med samma anslutning i stället för att ta emot och svara på kommandon i en strikt sekvens.
Den här metoden hjälper till att minska svarstiden genom att använda nätverket effektivare. Följande kodfragment visar ett exempel som hämtar information om två kunder samtidigt. Koden skickar två begäranden och utför sedan en annan bearbetning (visas inte) innan du väntar på att få resultatet. Metoden Wait för cacheobjektet liknar .NET Framework-metoden 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);
Mer information om hur du skriver klientprogram som kan använda Azure Cache for Redis finns i dokumentationen om Azure Cache for Redis. More information is also available at StackExchange.Redis.
Sidan Pipelines och multiplexers på samma webbplats innehåller mer information om asynkrona åtgärder och pipelining med Redis och StackExchange-biblioteket.
Använda Redis-cachelagring
Den enklaste användningen av Redis för cachelagringsproblem är nyckel/värde-par där värdet är en opretenterad sträng med godtycklig längd som kan innehålla binära data. (Det är i princip en matris med byte som kan behandlas som en sträng). Det här scenariot illustrerades i avsnittet Implementera Redis Cache-klientprogram tidigare i den här artikeln.
Observera att nycklar också innehåller otolkade data, så att du kan använda valfri binär information som nyckel. Ju längre nyckeln är, desto mer utrymme tar det att lagra, och ju längre tid det tar att utföra uppslagsåtgärder. För användbarhet och enkelt underhåll utformar du nyckelområdet noggrant och använder meningsfulla (men inte utförliga) nycklar.
Använd till exempel strukturerade nycklar som "customer:100" för att representera nyckeln för kunden med ID 100 i stället för bara "100". Med det här schemat kan du enkelt skilja mellan värden som lagrar olika datatyper. Du kan till exempel också använda nyckeln "orders:100" för att representera nyckeln för ordern med ID 100.
Förutom endimensionella binära strängar kan ett värde i ett Redis-nyckel/värde-par också innehålla mer strukturerad information, inklusive listor, uppsättningar (sorterade och osorterade) och hashvärden. Redis tillhandahåller en omfattande kommandouppsättning som kan ändra dessa typer, och många av dessa kommandon är tillgängliga för .NET Framework-program via ett klientbibliotek som StackExchange. Sidan En introduktion till Redis-datatyper och abstraktioner på Redis webbplats ger en mer detaljerad översikt över dessa typer och de kommandon som du kan använda för att manipulera dem.
I det här avsnittet sammanfattas några vanliga användningsfall för dessa datatyper och kommandon.
Utföra atomiska åtgärder och batchåtgärder
Redis stöder en serie atomiska get-and-set-åtgärder för strängvärden. Dessa åtgärder tar bort möjliga rasrisker som kan uppstå när du använder separata GET kommandon och SET kommandon. De åtgärder som är tillgängliga är:
INCR,INCRBY,DECR, ochDECRBY, som utför atomiska inkrements- och minskningsåtgärder på heltals numeriska datavärden. StackExchange-biblioteket innehåller överlagrade versioner avIDatabase.StringIncrementAsyncmetoderna ochIDatabase.StringDecrementAsyncför att utföra dessa åtgärder och returnera det resulterande värdet som lagras i cacheminnet. Följande kodfragment visar hur du använder dessa metoder: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, som hämtar värdet som är associerat med en nyckel och ändrar det till ett nytt värde. StackExchange-biblioteket gör den här åtgärden tillgänglig viaIDatabase.StringGetSetAsync-metoden. Kodfragmentet nedan visar ett exempel på den här metoden. Den här koden returnerar det aktuella värdet som är associerat med nyckeln "data:counter" från föregående exempel. Sedan återställs värdet för den här nyckeln till noll, allt som en del av samma åtgärd:ConnectionMultiplexer redisHostConnection = ...; IDatabase cache = redisHostConnection.GetDatabase(); ... string oldValue = await cache.StringGetSetAsync("data:counter", 0);MGETochMSET, som kan returnera eller ändra en uppsättning strängvärden som en enda åtgärd. MetodernaIDatabase.StringGetAsyncochIDatabase.StringSetAsyncär överbelastade för att stödja den här funktionen, som du ser i följande exempel: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);
Du kan också kombinera flera åtgärder i en enda Redis-transaktion enligt beskrivningen i avsnittet Redis-transaktioner och batchar tidigare i den här artikeln. StackExchange-biblioteket har stöd för transaktioner via ITransaction gränssnittet.
Du skapar ett ITransaction objekt med hjälp IDatabase.CreateTransaction av metoden . Du anropar kommandon till transaktionen med hjälp av de metoder som tillhandahålls av ITransaction objektet.
Gränssnittet ITransaction ger åtkomst till en uppsättning metoder som liknar dem som används av IDatabase gränssnittet, förutom att alla metoder är asynkrona. Det innebär att de endast utförs när ITransaction.Execute metoden anropas. Värdet som returneras av ITransaction.Execute metoden anger om transaktionen har skapats (sant) eller om den misslyckades (falskt).
Följande kodfragment visar ett exempel som ökar och minskar två räknare som en del av samma transaktion:
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);
Kom ihåg att Redis-transaktioner skiljer sig från transaktioner i relationsdatabaser. Metoden Execute köar helt enkelt alla kommandon som utgör den transaktion som ska köras, och om någon av dem är felaktigt formaterad stoppas transaktionen. Om alla kommandon har placerats i kö körs varje kommando asynkront.
Om något kommando misslyckas fortsätter de andra bearbetningen. If you need to verify that a command has completed successfully, you must fetch the results of the command by using the Result property of the corresponding task, as shown in the example above. Reading the Result property will block the calling thread until the task has completed.
Mer information finns i Transaktioner i Redis.
När du utför batchåtgärder kan du använda gränssnittet för IBatch StackExchange-biblioteket. Det här gränssnittet ger åtkomst till en uppsättning metoder som liknar dem som används av IDatabase gränssnittet, förutom att alla metoder är asynkrona.
Du skapar ett IBatch objekt med hjälp IDatabase.CreateBatch av metoden och kör sedan batchen IBatch.Execute med hjälp av metoden, som du ser i följande exempel. Den här koden anger helt enkelt ett strängvärde, ökar och minskar samma räknare som användes i föregående exempel och visar resultatet:
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);
Det är viktigt att förstå att om ett kommando i en batch misslyckas på grund av att det är felaktigt kan de andra kommandona fortfarande köras, till skillnad från en transaktion. Metoden IBatch.Execute returnerar inte någon indikation på lyckad eller misslyckad.
Utföra åtgärder för att utlösa och glömma cache
Redis stöder brand- och glöm-åtgärder med hjälp av kommandoflaggor. I det här fallet initierar klienten helt enkelt en åtgärd men har inget intresse av resultatet och väntar inte på att kommandot ska slutföras. Exemplet nedan visar hur du utför INCR-kommandot som en åtgärd för att utlösa och glömma:
ConnectionMultiplexer redisHostConnection = ...;
IDatabase cache = redisHostConnection.GetDatabase();
...
await cache.StringSetAsync("data:key1", 99);
...
cache.StringIncrement("data:key1", flags: CommandFlags.FireAndForget);
Ange nycklar som upphör att gälla automatiskt
När du lagrar ett objekt i en Redis-cache kan du ange en timeout varefter objektet tas bort automatiskt från cacheminnet. Du kan också fråga hur mycket mer tid en nyckel har innan den TTL upphör att gälla med hjälp av kommandot . Det här kommandot är tillgängligt för StackExchange-program med hjälp IDatabase.KeyTimeToLive av metoden .
Följande kodfragment visar hur du anger en förfallotid på 20 sekunder på en nyckel och frågar efter nyckelns återstående livslängd:
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");
Du kan också ange förfallotiden till ett visst datum och en viss tid med hjälp av kommandot EXPIRE, som är tillgängligt i StackExchange-biblioteket som KeyExpireAsync metod:
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
Du kan ta bort ett objekt från cachen manuellt med hjälp av DEL-kommandot, som är tillgängligt via StackExchange-biblioteket som IDatabase.KeyDeleteAsync metod.
Använda taggar för att korskorrelera cachelagrade objekt
En Redis-uppsättning är en samling med flera objekt som delar en enda nyckel. Du kan skapa en uppsättning med hjälp av KOMMANDOT SADD. Du kan hämta objekten i en uppsättning med hjälp av KOMMANDOT SMEMBERS. StackExchange-biblioteket implementerar SADD-kommandot med IDatabase.SetAddAsync -metoden och SMEMBERS-kommandot med IDatabase.SetMembersAsync -metoden.
Du kan också kombinera befintliga uppsättningar för att skapa nya uppsättningar med hjälp av kommandona SDIFF (set difference), SINTER (set intersection) och SUNION (set union). StackExchange-biblioteket förenar dessa åtgärder i IDatabase.SetCombineAsync -metoden. Den första parametern för den här metoden anger den uppsättningsåtgärd som ska utföras.
Följande kodfragment visar hur uppsättningar kan vara användbara för att snabbt lagra och hämta samlingar med relaterade objekt. Den här koden använder den BlogPost typ som beskrevs i avsnittet Implementera Redis Cache-klientprogram tidigare i den här artikeln.
Ett BlogPost objekt innehåller fyra fält – ett ID, en rubrik, en rangordningspoäng och en samling taggar. Det första kodfragmentet nedan visar exempeldata som används för att fylla i en C#-lista med BlogPost objekt:
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
}
Du kan lagra taggarna för varje BlogPost objekt som en uppsättning i en Redis-cache och associera varje uppsättning med ID:t för BlogPost. På så sätt kan ett program snabbt hitta alla taggar som tillhör ett specifikt blogginlägg. Om du vill aktivera sökning i motsatt riktning och hitta alla blogginlägg som delar en specifik tagg kan du skapa en annan uppsättning som innehåller blogginläggen som refererar till tagg-ID:t i nyckeln:
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);
}
}
Med de här strukturerna kan du utföra många vanliga frågor mycket effektivt. Du kan till exempel hitta och visa alla taggar för blogginlägg 1 så här:
// Show the tags for blog post #1
foreach (var value in await cache.SetMembersAsync("blog:posts:1:tags"))
{
Console.WriteLine(value);
}
Du hittar alla taggar som är gemensamma för blogginlägg 1 och blogginlägg 2 genom att utföra en angiven skärningspunktsåtgärd på följande sätt:
// 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);
}
Och du hittar alla blogginlägg som innehåller en specifik tagg:
// 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);
}
Hitta nyligen använda objekt
En vanlig uppgift som krävs för många program är att hitta de senast använda objekten. En bloggwebbplats kanske till exempel vill visa information om de senast lästa blogginläggen.
Du kan implementera den här funktionen med hjälp av en Redis-lista. En Redis-lista innehåller flera objekt som delar samma nyckel. Listan fungerar som en dubbelsluten kö. Du kan skicka objekt till endera änden av listan med hjälp av kommandona LPUSH (vänster push) och RPUSH (höger push). Du kan hämta objekt från endera änden av listan med hjälp av kommandona LPOP och RPOP. Du kan också returnera en uppsättning element med hjälp av kommandona LRANGE och RRANGE.
Kodfragmenten nedan visar hur du kan utföra dessa åtgärder med hjälp av StackExchange-biblioteket. Den här koden använder BlogPost typen från föregående exempel. När ett blogginlägg läses av en användare IDatabase.ListLeftPushAsync skickar metoden rubriken för blogginlägget till en lista som är associerad med nyckeln "blog:recent_posts" i Redis-cachen.
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
När fler blogginlägg läss skickas deras titlar till samma lista. Listan sorteras efter den sekvens där rubrikerna har lagts till. De senast lästa blogginläggen ligger till vänster i listan. (Om samma blogginlägg läss mer än en gång har det flera poster i listan.)
Du kan visa rubrikerna för de senast lästa inläggen IDatabase.ListRange med hjälp av metoden . Den här metoden tar nyckeln som innehåller listan, en startpunkt och en slutpunkt. Följande kod hämtar rubrikerna för de 10 blogginläggen (objekt från 0 till 9) längst till vänster i listan:
// Show latest ten posts
foreach (string postTitle in await cache.ListRangeAsync(redisKey, 0, 9))
{
Console.WriteLine(postTitle);
}
Observera att ListRangeAsync metoden inte tar bort objekt från listan. För att göra detta kan du använda IDatabase.ListLeftPopAsync metoderna och IDatabase.ListRightPopAsync .
Om du vill förhindra att listan växer på obestämd tid kan du regelbundet gallra objekt genom att trimma listan. Kodfragmentet nedan visar hur du tar bort alla utom de fem objekt som är längst till vänster från listan:
await cache.ListTrimAsync(redisKey, 0, 5);
Implementera en ledartavla
Som standard lagras inte objekten i en uppsättning i någon specifik ordning. Du kan skapa en ordnad uppsättning med hjälp av ZADD-kommandot ( IDatabase.SortedSetAdd metoden i StackExchange-biblioteket). Objekten sorteras med hjälp av ett numeriskt värde som kallas poäng, som anges som en parameter till kommandot.
Följande kodfragment lägger till rubriken för ett blogginlägg i en ordnad lista. I det här exemplet har varje blogginlägg också ett poängfält som innehåller rangordningen för blogginlägget.
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);
Du kan hämta blogginläggets rubriker och poäng i stigande poängordning med hjälp IDatabase.SortedSetRangeByRankWithScores av metoden:
foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(redisKey))
{
Console.WriteLine(post);
}
Note
StackExchange-biblioteket innehåller IDatabase.SortedSetRangeByRankAsync också metoden som returnerar data i poängordning, men den returnerar inte poängen.
Du kan också hämta objekt i fallande ordning efter poäng och begränsa antalet objekt som returneras genom att ange ytterligare parametrar för IDatabase.SortedSetRangeByRankWithScoresAsync metoden. I nästa exempel visas rubrikerna och poängen för de 10 främsta rankade blogginläggen:
foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(
redisKey, 0, 9, Order.Descending))
{
Console.WriteLine(post);
}
I nästa exempel används IDatabase.SortedSetRangeByScoreWithScoresAsync metoden som du kan använda för att begränsa de objekt som returneras till dem som ligger inom ett givet poängintervall:
// Blog posts with scores between 5000 and 100000
foreach (var post in await cache.SortedSetRangeByScoreWithScoresAsync(
redisKey, 5000, 100000))
{
Console.WriteLine(post);
}
Meddelande med hjälp av kanaler
Förutom att fungera som en datacache tillhandahåller en Redis-server meddelanden via en mekanism för utgivare/prenumeranter med höga prestanda. Klientprogram kan prenumerera på en kanal och andra program eller tjänster kan publicera meddelanden till kanalen. Prenumererande program tar sedan emot dessa meddelanden och kan bearbeta dem.
Redis tillhandahåller KOMMANDOT PRENUMERERA för klientprogram som ska användas för att prenumerera på kanaler. Det här kommandot förväntar sig namnet på en eller flera kanaler där programmet accepterar meddelanden. StackExchange-biblioteket innehåller ISubscription gränssnittet som gör det möjligt för ett .NET Framework-program att prenumerera på och publicera till kanaler.
Du skapar ett ISubscription objekt med hjälp GetSubscriber av metoden för anslutningen till Redis-servern. Sedan lyssnar du efter meddelanden på en kanal med hjälp SubscribeAsync av metoden för det här objektet. Följande kodexempel visar hur du prenumererar på en kanal med namnet "messages:blogPosts":
ConnectionMultiplexer redisHostConnection = ...;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
...
await subscriber.SubscribeAsync("messages:blogPosts", (channel, message) => Console.WriteLine("Title is: {0}", message));
Den första parametern för Subscribe metoden är namnet på kanalen. Det här namnet följer samma konventioner som används av nycklar i cacheminnet. Namnet kan innehålla binära data, men vi rekommenderar att du använder relativt korta, meningsfulla strängar för att säkerställa bra prestanda och underhåll.
Observera också att namnområdet som används av kanaler är separat från det som används av nycklar. Det innebär att du kan ha kanaler och nycklar som har samma namn, även om det kan göra programkoden svårare att underhålla.
Den andra parametern är ett åtgärdsdelegat. Det här ombudet körs asynkront när ett nytt meddelande visas på kanalen. Det här exemplet visar helt enkelt meddelandet i konsolen (meddelandet innehåller rubriken på ett blogginlägg).
Om du vill publicera till en kanal kan ett program använda kommandot Redis PUBLISH. StackExchange-biblioteket tillhandahåller metoden IServer.PublishAsync för att utföra den här åtgärden. Nästa kodfragment visar hur du publicerar ett meddelande till kanalen "messages:blogPosts":
ConnectionMultiplexer redisHostConnection = ...;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
...
BlogPost blogPost = ...;
subscriber.PublishAsync("messages:blogPosts", blogPost.Title);
Det finns flera punkter som du bör förstå om publicerings-/prenumerationsmekanismen:
- Flera prenumeranter kan prenumerera på samma kanal och de får alla meddelanden som publiceras till kanalen.
- Prenumeranter får endast meddelanden som har publicerats efter att de har prenumererat. Kanaler buffrar inte och när ett meddelande har publicerats skickar Redis-infrastrukturen meddelandet till varje prenumerant och tar sedan bort det.
- Som standard tas meddelanden emot av prenumeranter i den ordning de skickas. I ett mycket aktivt system med ett stort antal meddelanden och många prenumeranter och utgivare kan garanterad sekventiell leverans av meddelanden försämra systemets prestanda. Om varje meddelande är oberoende och ordningen är oviktig kan du aktivera samtidig bearbetning av Redis-systemet, vilket kan bidra till att förbättra svarstiden. Du kan uppnå detta i en StackExchange-klient genom att ange PreserveAsyncOrder för anslutningen som används av prenumeranten till false:
ConnectionMultiplexer redisHostConnection = ...;
redisHostConnection.PreserveAsyncOrder = false;
ISubscriber subscriber = redisHostConnection.GetSubscriber();
Serialization considerations
När du väljer ett serialiseringsformat bör du överväga kompromisser mellan prestanda, samverkan, versionshantering, kompatibilitet med befintliga system, datakomprimering och minneskostnader. När du utvärderar prestandan bör du komma ihåg att prestandamåtten är mycket beroende av kontexten. De kanske inte återspeglar din faktiska arbetsbelastning och kanske inte överväger nyare bibliotek eller versioner. Det finns ingen enskild "snabbaste" serialiserare för alla scenarier.
Några alternativ att tänka på:
Protocol Buffers (also called protobuf) is a serialization format developed by Google for serializing structured data efficiently. Den använder starkt inskrivna definitionsfiler för att definiera meddelandestrukturer. Dessa definitionsfiler kompileras sedan till språkspecifik kod för serialisering och deserialisering av meddelanden. Protobuf kan användas via befintliga RPC-mekanismer eller generera en RPC-tjänst.
Apache Thrift uses a similar approach, with strongly typed definition files and a compilation step to generate the serialization code and RPC services.
Apache Avro provides similar functionality to Protocol Buffers and Thrift, but there's no compilation step. I stället innehåller serialiserade data alltid ett schema som beskriver strukturen.
JSON is an open standard that uses human-readable text fields. Det har brett plattformsoberoende stöd. JSON använder inte meddelandescheman. Eftersom det är ett textbaserat format är det inte särskilt effektivt över kabeln. I vissa fall kan du dock returnera cachelagrade objekt direkt till en klient via HTTP, i vilket fall lagring av JSON kan spara kostnaden för deserialisering från ett annat format och sedan serialisera till JSON.
binär JSON (BSON) är ett binärt serialiseringsformat som använder en struktur som liknar JSON. BSON har utformats för att vara lätt, lätt att skanna och snabb att serialisera och deserialisera i förhållande till JSON. Nyttolaster är jämförbara i storlek med JSON. Beroende på data kan en BSON-nyttolast vara mindre eller större än en JSON-nyttolast. BSON har några ytterligare datatyper som inte är tillgängliga i JSON, särskilt BinData (för bytematriser) och Datum.
MessagePack is a binary serialization format that is designed to be compact for transmission over the wire. Det finns inga meddelandescheman eller kontroll av meddelandetyp.
Bond is a cross-platform framework for working with schematized data. Den stöder serialisering och deserialisering mellan språk. Anmärkningsvärda skillnader från andra system som anges här är stöd för arv, typalias och generiska objekt.
gRPC is an open-source RPC system developed by Google. Som standard använder den Protokollbuffertar som definitionsspråk och underliggande meddelandeutbytesformat.
Next steps
- Dokumentation om Azure Cache for Redis
- Vanliga frågor och svar om Azure Cache for Redis
- Aktivitetsbaserat asynkront mönster
- Redis documentation
- StackExchange.Redis
- Guide för datapartitionering
Related resources
Följande mönster kan också vara relevanta för ditt scenario när du implementerar cachelagring i dina program:
Cache-aside pattern: This pattern describes how to load data on demand into a cache from a data store. Det här mönstret hjälper också till att upprätthålla konsekvens mellan data som lagras i cacheminnet och data i det ursprungliga datalagret.
The Sharding pattern provides information about implementing horizontal partitioning to help improve scalability when storing and accessing large volumes of data.