Richtlijnen voor caching

Caching (in de cache opslaan van gegevens) is een veelvoorkomende techniek die als doel heeft de prestaties en schaalbaarheid van een systeem te verbeteren. In de cache worden regelmatig gebruikte gegevens tijdelijk gekopieerd naar opslag die zich dichter bij de toepassing bevindt dan de oorspronkelijke bron. Deze aanpak kan de reactietijden voor clienttoepassingen aanzienlijk verbeteren door gegevens sneller te leveren.

Caching is het meest effectief wanneer een clientexemplaren herhaaldelijk dezelfde gegevens lezen, met name als alle volgende voorwaarden van toepassing zijn op het oorspronkelijke gegevensarchief:

  • Het gegevensarchief blijft relatief statisch.
  • Het is traag vergeleken met de snelheid van de cache.
  • Het is onderhevig aan een hoge mate van conflicten.
  • De afstand tot de klanten is groot genoeg om een aanzienlijke netwerklatentie te veroorzaken.

Caching in gedistribueerde toepassingen

Gedistribueerde toepassingen implementeren doorgaans een of beide van de volgende strategieën bij het opslaan van gegevens in de cache:

  • Gebruik een privécache, waar u gegevens lokaal op de computer bewaart waarop een toepassing of service wordt uitgevoerd.

  • Gebruik een gedeelde cache, die fungeert als een gemeenschappelijke bron waartoe meerdere processen en machines toegang hebben.

In beide gevallen kunt u caching uitvoeren aan de clientzijde en aan de serverzijde. Het proces dat de gebruikersinterface biedt voor een systeem, zoals een webbrowser of bureaubladtoepassing, verwerkt caching aan de clientzijde. Caching aan de serverzijde wordt op afstand uitgevoerd door het proces dat de zakelijke services levert.

Privécachen

Het meest eenvoudige type cache is een in-memory opslag. Deze wordt bewaard in de adresruimte van één proces en de code die in dat proces wordt uitgevoerd, heeft rechtstreeks toegang tot de cache. Dit type cache is snel toegankelijk. Het kan ook een effectieve manier bieden voor het opslaan van bescheiden hoeveelheden statische gegevens. De hoeveelheid geheugen die beschikbaar is op de computer beperkt doorgaans de grootte van een cache.

Als u meer informatie in de cache wilt opslaan dan fysiek mogelijk is in het geheugen, kunt u gegevens in de cache naar het lokale bestandssysteem schrijven. Het openen van gegevens vanuit het bestandssysteem duurt langer dan als de gegevens in het geheugen worden opgeslagen, maar het moet nog steeds sneller en betrouwbaarder zijn dan het ophalen van gegevens in een netwerk.

Als u meerdere exemplaren van een toepassing hebt die dit model gelijktijdig gebruikt, heeft elk toepassingsexemplaar een eigen onafhankelijke cache die een eigen kopie van de gegevens bevat.

U kunt een cache beschouwen als een momentopname van de oorspronkelijke gegevens op een bepaald moment in het verleden. Als deze gegevens niet statisch zijn, is het waarschijnlijk dat verschillende toepassingsexemplaren verschillende versies van de gegevens in hun cache bevatten. Daarom kan dezelfde query die door deze exemplaren wordt uitgevoerd, verschillende resultaten retourneren, zoals wordt weergegeven in het volgende diagram.

Diagram met de resultaten van het gebruik van een cache in het geheugen in verschillende exemplaren van een toepassing.

Diagram met inconsistentie in de cache voor meerdere toepassingsexemplaren die zijn verbonden met een gedeelde SQL-database. Rechts ziet u een SQL-database die wordt weergegeven als een cilinder. Linksboven staat een grote cirkel met het label 'toepassingsinstantie A' en linksonder staat een vergelijkbare cirkel met het label 'toepassingsinstantie B'. In toepassingsinstantie A staat een tandwielpictogram dat het toepassingsproces voorstelt. Onder het tandwielpictogram bevindt zich een rastertabel die een cache in het geheugen vertegenwoordigt. Buiten de begrenzing staat een label met de tekst: cache is een momentopname van de gegevens op tijdstip X. Binnen toepassingsinstantie B staat nog een tandwielpictogram. Onder het tandwielpictogram bevindt zich een rastertabel die een cache in het geheugen vertegenwoordigt. Buiten de grens staat een label met de tekst: de cache is een momentopname van de gegevens op tijdstip Y. Vanuit de SQL-database loopt een lijn naar applicatie-instantie A, met het label: applicatie-instantie A haalt op tijdstip X gegevens op en cachet deze in het geheugen. Een tweede regel is een uitbreiding van de SQL-database naar het toepassingsexemplaren B, gelabeld toepassingsexemplaren B haalt gegevens op het moment Y op en slaat deze in het geheugen op. Tussen deze twee annotatieregels staat een notitie waarin staat dat de informatie in de database verandert tussen tijdstip X en tijdstip Y.

Gedeelde caching

Met behulp van een gedeelde cache kunt u ervoor zorgen dat alle toepassingsexemplaren dezelfde weergave van gegevens in de cache zien. De cache bevindt zich op een afzonderlijke locatie, die doorgaans wordt gehost als onderdeel van een afzonderlijke service, zoals wordt weergegeven in het volgende diagram.

Diagram met de resultaten van toepassingen met behulp van een gedeelde cache.

Diagram waarin wordt getoond hoe een gedeelde cacheservice cache-inconsistentie oplost voor meerdere toepassingsexemplaren die zijn verbonden met een SQL-database. Aan de rechterkant bevindt zich een SQL-database die wordt weergegeven als een cilinder. In het midden staat een kader met het label 'shared cache service' met daarin een raster dat de gedeelde cache vertegenwoordigt. Aan de linkerkant bevindt zich een tandwielpictogram met het label Toepassingsexemplaren A. Daaronder bevindt zich een tweede tandwielpictogram met het label Toepassingsexemplaren B. Een zwarte pijl wijst van de SQL-database naar het serviceraster voor gedeelde cache, waarmee wordt aangegeven dat de database de gedeelde cache vult. Vanuit de gedeelde cacheservice breiden twee blauwe pijlen zich naar buiten uit, één naar toepassingsexemplaren A en één naar toepassingsexemplaren B. Aan de linkerkant loopt een gekromde haak verticaal tussen de twee toepassingsexemplaren, met een aangrenzend label met de tekst 'Beide toepassingsexemplaren zien dezelfde gegevens in de cache'. Dit label en haakje benadrukken het belangrijkste voordeel van de architectuur voor gedeelde cache: in tegenstelling tot een cache per exemplaar in het geheugen, halen zowel toepassingsexemplaren A als B gegevens op uit dezelfde gecentraliseerde cache.

Een belangrijk voordeel van de benadering voor gedeelde caching is schaalbaarheid. Veel gedeelde cacheservices worden geïmplementeerd met behulp van een cluster servers en software gebruiken om de gegevens transparant over het cluster te verdelen. Een toepassingsexemplaar stuurt een verzoek naar de cache-service. De onderliggende infrastructuur bepaalt de locatie van de gegevens in de cache in het cluster. U kunt de cache eenvoudig schalen door meer servers toe te voegen.

Er zijn twee belangrijkste nadelen van de benadering voor gedeelde caching:

  • De cache is langzamer toegankelijk omdat deze niet langer lokaal wordt bewaard bij elk toepassingsexemplaren.
  • Het implementeren van een afzonderlijke cacheservice kan complexiteit aan de oplossing toevoegen.

Overwegingen voor het gebruik van caching

In de volgende secties worden overwegingen beschreven voor het ontwerpen en gebruiken van een cache.

Bepalen wanneer gegevens in de cache moeten worden opgeslagen

Caching kan de prestaties, schaalbaarheid en beschikbaarheid aanzienlijk verbeteren. Hoe meer gegevens u hebt en hoe groter het aantal gebruikers dat toegang nodig heeft tot deze gegevens, hoe groter de voordelen van caching. Caching vermindert de latentie en conflicten die zijn gekoppeld aan het verwerken van grote hoeveelheden gelijktijdige aanvragen in het oorspronkelijke gegevensarchief.

Een database kan bijvoorbeeld een beperkt aantal gelijktijdige verbindingen ondersteunen. Door gegevens op te halen uit een gedeelde cache, in plaats van de onderliggende database, kan een clienttoepassing toegang krijgen tot deze gegevens, zelfs als het aantal beschikbare verbindingen momenteel uitgeput is. Als de database niet beschikbaar is, kunnen clienttoepassingen bovendien mogelijk doorgaan met behulp van de gegevens in de cache.

Overweeg om gegevens die regelmatig worden gelezen, maar die zelden worden gewijzigd, in de cache op te nemen. Cachegegevens met een hoger deel van leesbewerkingen dan schrijfbewerkingen. Gebruik de cache echter niet als gezaghebbende opslag van kritieke informatie. Sla in plaats daarvan alle belangrijke wijzigingen op in een permanent gegevensarchief. Als de cache niet beschikbaar is, kan uw toepassing nog steeds blijven werken met behulp van het gegevensarchief en verliest u geen belangrijke informatie.

Bepalen hoe gegevens effectief worden opgeslagen in de cache

Als u een cache effectief wilt gebruiken, bepaalt u de meest geschikte gegevens om op te cachen en slaat u deze op het juiste moment in de cache op. U kunt gegevens toevoegen aan de cache wanneer een toepassing deze voor het eerst ophaalt. De toepassing moet de gegevens slechts eenmaal ophalen uit het gegevensarchief en vervolgens kan aan de volgende toegang worden voldaan met behulp van de cache.

U kunt ook een cache gedeeltelijk of volledig vullen met gegevens van tevoren, meestal wanneer de toepassing wordt gestart. Deze benadering wordt seeding genoemd. Het is echter mogelijk niet raadzaam om seeding voor een grote cache te implementeren, omdat deze benadering plotselinge, hoge belasting kan opleggen aan het oorspronkelijke gegevensarchief wanneer de toepassing wordt uitgevoerd.

Analyseer gebruikspatronen om te bepalen of een cache volledig of gedeeltelijk moet worden ingevuld en om te kiezen welke gegevens in de cache moeten worden opgeslagen. U kunt bijvoorbeeld de cache seeden met de statische gebruikersprofielgegevens voor klanten die de toepassing elke dag gebruiken, maar niet voor klanten die de toepassing slechts één keer per week gebruiken.

Caching werkt doorgaans goed met gegevens die onveranderbaar zijn of die niet vaak worden gewijzigd. Voorbeelden hiervan zijn referentie-informatie, zoals product- en prijsinformatie in een e-commercetoepassing, of gedeelde statische resources die kostbaar zijn om te bouwen. Laad sommige of alle gegevens in de cache bij het opstarten van de toepassing om de vraag op resources te minimaliseren en de prestaties te verbeteren. Mogelijk wilt u ook een achtergrondproces hebben waarmee de referentiegegevens in de cache periodiek worden bijgewerkt om ervoor te zorgen dat deze up-to-datum is. Of het achtergrondproces kan de cache vernieuwen wanneer de verwijzingsgegevens worden gewijzigd.

Caching is minder handig voor dynamische gegevens, hoewel er enkele uitzonderingen zijn. Zie de sectie Cache voor zeer dynamische gegevens verderop in dit artikel voor meer informatie. Wanneer de oorspronkelijke gegevens regelmatig worden gewijzigd, wordt de informatie in de cache snel verlopen of vermindert de overhead van het synchroniseren van de cache met het oorspronkelijke gegevensarchief de effectiviteit van caching.

Een cache hoeft de volledige gegevens voor een entiteit niet op te nemen. Als een gegevensitem bijvoorbeeld een object met meerdere waarden vertegenwoordigt, zoals een bankklant met een naam, adres en rekeningsaldo, blijven sommige van deze elementen mogelijk statisch, zoals de naam en het adres. Andere elementen, zoals het rekeningsaldo, zijn mogelijk dynamischer. In deze situaties kan het handig zijn om de statische delen van de gegevens op te cachen en alleen de resterende gegevens op te halen (of te berekenen) wanneer dat nodig is.

Voer prestatietests en gebruiksanalyses uit om te bepalen of het vooraf invullen of laden op aanvraag van de cache, of een combinatie van beide, geschikt is. Baseer de beslissing over de volatiliteit en het gebruikspatroon van de gegevens. Cachegebruik en prestatieanalyse zijn belangrijk in toepassingen die zware belastingen tegenkomen en zeer schaalbaar moeten zijn. In zeer schaalbare scenario's kunt u bijvoorbeeld de cache seeden om de belasting van het gegevensarchief op piekmomenten te verminderen.

Caching kan ook worden gebruikt om herhalende berekeningen te voorkomen terwijl de toepassing wordt uitgevoerd. Als een bewerking gegevens transformeert of een ingewikkelde berekening uitvoert, kunnen de resultaten van de bewerking in de cache worden opgeslagen. Als dezelfde berekening later vereist is, kan de toepassing de resultaten ophalen uit de cache.

Een toepassing kan gegevens wijzigen die zich in een cache bevinden. Denk echter aan de cache als een tijdelijk gegevensarchief dat op elk gewenst moment kan verdwijnen. Sla geen waardevolle gegevens alleen op in de cache; zorg ervoor dat u ook de informatie in het oorspronkelijke gegevensarchief onderhoudt. Deze methode minimaliseert de kans op verlies van gegevens als de cache niet beschikbaar is.

De cache consistent houden voor schrijfbewerkingen

Wanneer uw toepassing gegevens wijzigt die ook in de cache worden opgeslagen, bepaalt u hoe de cache consistent blijft met het recordsysteem. Er zijn twee benaderingen gebruikelijk:

  • Ongeldig maken bij schrijven: Schrijf de wijziging naar de gegevensopslag en verwijder vervolgens het betreffende cache-item. Met de volgende leesbewerking wordt de cache opnieuw ingevuld vanuit het gegevensarchief. Deze methode houdt het schrijfpad eenvoudig, maar een lezer kan direct na een schrijfactie te maken krijgen met een cache-misser of kort verouderde gegevens zien. In het Cache-Aside patroon wordt deze benadering beschreven.

  • Write-through: Werk het gegevensarchief en de cache bij als onderdeel van dezelfde schrijfbewerking en retourneer het schrijfantwoord pas nadat beide updates zijn voltooid. Deze methode biedt lezers de bijgewerkte waarde direct na een geslaagde schrijfbewerking, ten koste van een hogere schrijflatentie en meer coördinatielogica.

    Zie write-through caching met Azure Managed Redis en Azure SQL Database voor een end-to-end voorbeeld waarin gebruik wordt gemaakt van Azure Functions om write-through-updates te coördineren voor Azure SQL Database en Azure Managed Redis.

Gebruik alleen schrijven voor leesintensieve toegangspaden waarvoor lees-na-schrijven nieuwheid nodig is. Voor gegevens die zelden worden gelezen nadat deze zijn geschreven of die voortdurend worden gewijzigd, ongeldig maken bij schrijven of overslaan van de cache.

Zeer dynamische gegevens in cache opslaan

Het opslaan van snel veranderende informatie in een permanent gegevensarchief kan een overhead voor het systeem betekenen. Denk bijvoorbeeld aan een apparaat dat voortdurend de status of een andere meting rapporteert. Als een toepassing ervoor kiest deze gegevens niet in de cache op te slaan op basis van dat de gegevens in de cache meestal verouderd zijn, kan dezelfde overweging gelden bij het opslaan en ophalen van deze gegevens uit het gegevensarchief. In de tijd die nodig is om deze gegevens op te slaan en op te halen, kan dit veranderen.

In een dergelijke situatie kunt u rekening houden met de voordelen van het rechtstreeks opslaan van de dynamische informatie in de cache in plaats van in het permanente gegevensarchief. Als de gegevens niet kritiek zijn en geen controle vereist, maakt het niet uit of de incidentele wijziging verloren gaat.

Gegevensverval in een cache beheren

In de meeste gevallen bevat een cache gegevens die een kopie van gegevens uit het oorspronkelijke gegevensarchief zijn. De gegevens in het oorspronkelijke gegevensarchief kunnen worden gewijzigd nadat u deze in de cache hebt opgeslagen, waardoor de gegevens in de cache verlopen. Met veel cachesystemen kunt u de cache zo configureren dat gegevens verlopen, waardoor de periode wordt verminderd waarin gegevens mogelijk verouderd zijn.

Nadat de gegevens in de cache zijn verlopen, worden deze door de cache verwijderd. De toepassing haalt vervolgens nieuwe gegevens op uit het oorspronkelijke gegevensarchief en kan de verlopen gegevens in de cache vervangen. U kunt een standaardverloopbeleid instellen wanneer u de cache configureert. In veel cacheservices kunt u ook de verloopperiode voor afzonderlijke objecten opgeven wanneer u ze programmatisch opslaat in de cache. In sommige caches kunt u de verloopperiode opgeven als een absolute waarde of als een schuifwaarde als het item niet binnen een opgegeven tijd wordt geopend. Met deze instelling wordt een algemeen verloopbeleid voor de cache genegeerd, maar alleen voor de specifieke objecten.

Note

Houd rekening met de verloopperiode voor de cache en de objecten die deze zorgvuldig bevat. Als u deze te kort maakt, verlopen objecten te snel en vermindert u de voordelen van het gebruik van de cache. Als u de periode te lang maakt, loopt u het risico dat de gegevens verlopen.

Als u gegevens gedurende lange tijd in de cache wilt laten staan, kan de cache vol raken. In dit geval kunnen aanvragen voor het toevoegen van nieuwe items aan de cache ertoe leiden dat de cache bepaalde items in een proces dat ook wel verwijdering wordt genoemd, geforceerd verwijdert. Cacheservices verwijderen doorgaans gegevens op basis van minst recent gebruikte (LRU), maar u kunt dit beleid meestal overschrijven en voorkomen dat items worden verwijderd. Als u deze benadering echter gebruikt, loopt u het risico dat u het geheugen overschrijdt dat beschikbaar is in de cache. Een toepassing die probeert een item toe te voegen aan een volledige cache mislukt met een uitzondering.

Sommige caching-implementaties kunnen andere verwijderingsbeleidsregels bieden. Typen verwijderingsbeleid zijn:

  • Een meest recent gebruikt beleid: hiermee verwijdert u de laatst gebruikte items uit de cache in de verwachting dat de gegevens niet opnieuw vereist zijn.
  • Een first-in-first-out beleid: hiermee verwijdert u eerst de oudste gegevens uit de cache.
  • Een expliciet verwijderingsbeleid: verwijdert items uit de cache op basis van een triggergebeurtenis, zoals de gegevens die worden gewijzigd.

Gegevens in een cache aan de clientzijde ongeldig maken

Gegevens die zijn opgeslagen in een cache aan de clientzijde, worden beschouwd als buiten het beheer van de service die de gegevens aan de client levert. Een service kan een client niet rechtstreeks dwingen om gegevens toe te voegen aan of te verwijderen uit een cache aan de clientzijde.

Deze beperking betekent dat een client die gebruikmaakt van een slecht geconfigureerde cache verouderde informatie kan blijven gebruiken. Als het verloopbeleid van de cache bijvoorbeeld niet correct wordt geïmplementeerd, kan een client verouderde gegevens gebruiken die lokaal in de cache worden opgeslagen wanneer de informatie in de oorspronkelijke gegevensbron wordt gewijzigd.

Als u een webtoepassing bouwt die gegevens via een HTTP-verbinding bedient, kunt u impliciet afdwingen dat een webclient, zoals een browser of webproxy, de meest recente informatie ophaalt. U kunt deze vernieuwing activeren door de URI te wijzigen wanneer u de resource bijwerkt. Webclients gebruiken doorgaans de URI van een resource als de sleutel in de cache aan de clientzijde, dus als de URI wordt gewijzigd, negeert de webclient eventuele eerder in de cache opgeslagen versies van een resource en haalt de nieuwe versie op.

Gelijktijdigheid in een cache beheren

Vaak ontwerpt u caches die moeten worden gedeeld door meerdere exemplaren van een toepassing. Elk toepassingsexemplaren kunnen gegevens in de cache lezen en wijzigen, dus dezelfde gelijktijdigheidsproblemen die zich voordoen met een gedeeld gegevensarchief, zijn ook van toepassing op een cache. In een situatie waarin een toepassing gegevens in de cache moet wijzigen, moet u er mogelijk voor zorgen dat updates van één exemplaar van de toepassing de wijzigingen die door een ander exemplaar zijn aangebracht, niet overschrijven.

Afhankelijk van de aard van de gegevens en de kans op botsingen, moet u een van de twee benaderingen voor gelijktijdigheid aannemen:

  • Optimistisch: Voordat de toepassing de gegevens bijwerken, wordt gecontroleerd of de gegevens in de cache zijn gewijzigd sinds deze zijn opgehaald. Als de gegevens nog steeds hetzelfde zijn, brengt de toepassing de wijziging aan. Anders bepaalt de toepassing of deze moet worden bijgewerkt. De bedrijfslogica die deze beslissing aanstuurt, is toepassingsspecifiek. Deze methode is geschikt voor situaties waarin updates niet vaak voorkomen of wanneer conflicten waarschijnlijk niet voorkomen.

  • Pessimistisch: Wanneer de toepassing de gegevens ophaalt, worden de gegevens in de cache vergrendeld om te voorkomen dat een ander exemplaar deze wijzigt. Dit proces zorgt ervoor dat conflicten niet kunnen optreden, maar het kan ook andere instanties blokkeren die dezelfde gegevens moeten verwerken. Pessimistische gelijktijdigheid kan van invloed zijn op de schaalbaarheid van een oplossing en wordt alleen aanbevolen voor bewerkingen met korte levensduur. Deze aanpak kan geschikt zijn voor situaties waarin conflicten waarschijnlijker zijn, met name als een toepassing meerdere items in de cache bijwerken en ervoor moet zorgen dat deze wijzigingen consistent worden toegepast.

Hoge beschikbaarheid en schaalbaarheid implementeren en prestaties verbeteren

Vermijd het gebruik van een cache als de primaire opslagplaats met gegevens. Het oorspronkelijke gegevensarchief, waaruit de cache is gevuld, dient deze rol. Het oorspronkelijke gegevensarchief zorgt voor persistentie van gegevens.

Wees voorzichtig met het niet introduceren van kritieke afhankelijkheden van de beschikbaarheid van een gedeelde cacheservice in uw oplossingen. Een toepassing moet kunnen blijven functioneren als de gedeelde cache niet beschikbaar is. De toepassing mag niet onresponsief worden of falen terwijl wordt gewacht totdat de cacheservice hervat wordt.

Daarom moet de toepassing worden voorbereid om de beschikbaarheid van de cacheservice te detecteren en terug te vallen naar het oorspronkelijke gegevensarchief als de cache niet toegankelijk is. Het Circuit-Breaker-patroon is handig voor het afhandelen van dit scenario. De service die de cache levert, kan worden hersteld en zodra die weer beschikbaar is, kan de cache opnieuw worden gevuld terwijl gegevens uit de oorspronkelijke gegevensopslag worden gelezen, volgens een strategie zoals het Cache-Aside-patroon.

De schaalbaarheid van het systeem kan echter worden beïnvloed als de toepassing terugvalt op het oorspronkelijke gegevensarchief wanneer de cache tijdelijk niet beschikbaar is. Terwijl de cache wordt hersteld, kan het oorspronkelijke gegevensarchief worden overspoeld met aanvragen voor gegevens, wat resulteert in time-outs en mislukte verbindingen.

Overweeg om een lokale, persoonlijke cache te implementeren in elk exemplaar van een toepassing, samen met de gedeelde cache waartoe alle toepassingsexemplaren toegang hebben. Wanneer de toepassing een item ophaalt, kan het eerst in de lokale cache worden gecontroleerd, vervolgens in de gedeelde cache en ten slotte in het oorspronkelijke gegevensarchief. De lokale cache kan worden gevuld met behulp van de gegevens in de gedeelde cache of in de database als de gedeelde cache niet beschikbaar is.

Deze aanpak vereist een zorgvuldige configuratie om te voorkomen dat de lokale cache te verouderd wordt ten opzichte van de gedeelde cache. De lokale cache fungeert echter als buffer als de gedeelde cache onbereikbaar is, zoals wordt weergegeven in het volgende diagram.

Diagram met de structuur van een gedeelde cache die een lokale privécache als buffer kan gebruiken.

Diagram met een hybride cachearchitectuur. Aan de rechterkant bevindt zich een SQL-database die wordt weergegeven als een cilinder. Rechtsboven bevindt zich een grote cirkel met het label Gedeelde cacheservice met een rastertabel die de gedeelde cache vertegenwoordigt. Een pijl wijst links van de SQL-database naar het gedeelde cacheserviceraster, waarmee wordt aangegeven dat de database de gedeelde cache vult. Linksboven staat een grote cirkel met het label 'toepassingsinstantie A'. In de cirkel staat bovenaan een tandwielpictogram en daaronder een tabel in rastervorm die een lokale privécache voorstelt. Een dubbele zwarte pijl verbindt het tandwielpictogram met het lokale cacheraster, waarmee een lees-/schrijfrelatie tussen het toepassingsproces en de lokale cache wordt aangegeven. Linksonder bevindt zich een grote cirkel met het label toepassingsexemplaren B, met dezelfde interne structuur. Vanuit de gedeelde cacheservice breiden twee blauwe pijlen zich naar buiten uit, één naar boven naar het tandwielpictogram in toepassingsexemplaren A en één naar beneden naar het tandwielpictogram in toepassingsexemplaren B. Een rechthoekige zwarte lijn vormt een terugvalpad dat wordt uitgevoerd vanuit de SQL-database langs de boven- en rechterranden, en vervolgens omlaag en langs de onderkant verbinding maken met de lokale cacherasters binnen beide toepassingsexemplaren. Een label onder in het diagram leest, als de gedeelde cacheservice niet beschikbaar is, vult toepassingslogica de lokale cache uit de database. Helemaal links loopt een gebogen accolade tussen applicatie-instanties A en B, met een aangrenzend label met de tekst: als de gedeelde cacheservice niet beschikbaar is, kunnen applicatie-instanties blijven functioneren met lokale, privé-caches.

Ter ondersteuning van grote caches die relatief langlopende gegevens bevatten, bieden sommige cacheservices een optie voor hoge beschikbaarheid waarmee automatische failover wordt geïmplementeerd als de cache niet beschikbaar is. Deze benadering omvat doorgaans het repliceren van de gegevens in de cache die zijn opgeslagen op een primaire cacheserver naar een secundaire cacheserver en overschakelen naar de secundaire server als de primaire server mislukt of de verbinding is verbroken.

Om de latentie te verminderen die afkomstig is van schrijven naar meerdere bestemmingen, kan de replicatie naar de secundaire server asynchroon optreden wanneer gegevens naar de cache op de primaire server worden geschreven. Deze benadering leidt tot de mogelijkheid dat bepaalde gegevens in de cache verloren kunnen gaan als er een fout opgetreden is, maar het aandeel van deze gegevens moet klein zijn, vergeleken met de totale grootte van de cache.

Als een gedeelde cache groot is, kan het nuttig zijn om de gegevens in de cache te partitioneren over knooppunten om de kans op conflicten te verminderen en de schaalbaarheid te verbeteren. Veel gedeelde caches bieden ondersteuning voor de mogelijkheid om knooppunten dynamisch toe te voegen en te verwijderen en de gegevens opnieuw te verdelen over partities. Deze benadering kan betrekking hebben op clustering, waarbij de verzameling knooppunten wordt gepresenteerd aan clienttoepassingen als één cache. Intern worden de gegevens echter verspreid over knooppunten na een vooraf gedefinieerde distributiestrategie waarmee de belasting gelijkmatig wordt verdeeld. Zie het Sharding-patroon voor meer informatie.

Clustering kan ook de beschikbaarheid van de cache verhogen. Als een knooppunt mislukt, is de rest van de cache nog steeds toegankelijk. Clustering wordt vaak gebruikt met replicatie en failover. Elk knooppunt kan worden gerepliceerd en de replica kan snel online worden gebracht als het knooppunt uitvalt.

Veel lees- en schrijfbewerkingen hebben waarschijnlijk betrekking op enkele gegevenswaarden of -objecten. Soms kan het echter nodig zijn om grote hoeveelheden gegevens snel op te slaan of op te halen. Het seeden van een cache kan bijvoorbeeld betrekking hebben op het schrijven van honderden of duizenden items naar de cache. Een toepassing moet mogelijk ook een groot aantal gerelateerde items ophalen uit de cache als onderdeel van dezelfde aanvraag.

Veel grootschalige caches bieden batchbewerkingen voor deze doeleinden. Met deze functie kan een clienttoepassing een groot aantal items in één aanvraag verpakken, waardoor de overhead voor het uitvoeren van een groot aantal kleine aanvragen wordt verminderd.

Cache en uiteindelijke consistentie

Het Cache-Aside patroon werkt alleen als het exemplaar van de toepassing dat de cache vult, toegang moet hebben tot de meest recente en consistente versie van de gegevens. In een systeem dat uiteindelijke consistentie implementeert (zoals een gerepliceerd gegevensarchief), is deze voorwaarde mogelijk niet waar.

Eén exemplaar van een toepassing kan een gegevensitem wijzigen en de in de cache opgeslagen versie van dat item ongeldig maken. Een ander exemplaar van de toepassing kan proberen dit item uit de cache te lezen, wat een cache-misser veroorzaakt. Vervolgens worden de gegevens uit het gegevensarchief gelezen en toegevoegd aan de cache. Als het gegevensarchief echter niet volledig is gesynchroniseerd met de andere replica's, kan het toepassingsexemplaren de cache lezen en vullen met de oude waarde.

Een gedistribueerde cache introduceert een andere laag voor dit probleem. De CAP-theorema geeft aan dat een gedistribueerd systeem maximaal twee van de drie garanties kan bieden: consistentie, beschikbaarheid en partitietolerantie. Omdat netwerkpartities onvermijdelijk zijn in cloudomgevingen, moet u kiezen tussen consistentie en beschikbaarheid. De meeste gedistribueerde caches, waaronder Redis, geven prioriteit aan beschikbaarheid en partitietolerantie boven sterke consistentie. Deze prioriteit betekent dat leesbewerkingen van een cachereplica verouderde gegevens kunnen retourneren tijdens een netwerkpartitie of direct na een schrijfbewerking naar een ander knooppunt. Wanneer u uw cachestrategie ontwerpt, bepaalt u hoeveel veroudering uw toepassing kan tolereren en time-to-live -waarden (TTL) dienovereenkomstig kan instellen. Voor gegevens die actueel moeten zijn, gebruikt u kortere TTLs of slaat u de cache volledig over en leest u deze uit het brongegevensarchief.

Zie Gegevensoverwegingen voor microservices voor meer informatie over het verwerken van gegevensconsistentie in gedistribueerde systemen.

In de cache opgeslagen gegevens beveiligen

Ongeacht de cacheservice die u gebruikt, kunt u overwegen om de gegevens in de cache te beveiligen tegen onbevoegde toegang. Twee belangrijke aandachtspunten zijn:

  • De privacy van de gegevens in de cache.
  • De privacy van gegevens tijdens het stromen tussen de cache en de toepassing die gebruikmaakt van de cache.

Om gegevens in de cache te beveiligen, kan de cacheservice een verificatiemechanisme implementeren waarvoor toepassingen de volgende gegevens moeten opgeven:

  • Welke identiteiten toegang hebben tot gegevens in de cache.
  • Welke lees- en schrijfbewerkingen deze identiteiten mogen uitvoeren.

Om de overhead te verminderen die is gekoppeld aan het lezen en schrijven van gegevens, kan die identiteit, nadat aan een identiteit schrijf- of leestoegang tot de cache is verleend, alle gegevens in de cache gebruiken.

Als u de toegang tot subsets van de gegevens in de cache wilt beperken, gebruikt u een van de volgende methoden:

  • Splits de cache in partities met behulp van verschillende cacheservers. Alleen toegang verlenen tot identiteiten voor de partities die ze mogen gebruiken.

  • Versleutel de gegevens in elke subset met behulp van verschillende sleutels. Geef de versleutelingssleutels alleen op voor identiteiten die toegang moeten hebben tot elke subset. Een clienttoepassing kan mogelijk nog steeds alle gegevens in de cache ophalen, maar kan alleen de gegevens ontsleutelen waarvoor deze de sleutels heeft.

U moet de gegevens ook beveiligen terwijl deze in en uit de cache stromen. Afhankelijk van de beveiligingsfuncties van de netwerkinfrastructuur die clienttoepassingen gebruiken om verbinding te maken met de cache. Als de cache wordt geïmplementeerd met behulp van een on-site-server binnen dezelfde organisatie die als host fungeert voor de clienttoepassingen, hoeft u mogelijk niet meer stappen uit te voeren voor de isolatie van het netwerk zelf. Als de cache zich op afstand bevindt en een TCP- of HTTP-verbinding via een openbaar netwerk zoals internet vereist, kunt u OVERWEGEN TLS te implementeren.

Caching implementeren met behulp van Azure Beheerde Redis

In de overige secties van dit artikel wordt beschreven hoe u de cachepatronen implementeert met behulp van Azure Managed Redis. Azure Managed Redis is een beheerde Redis-service die u kunt gebruiken als een gedeelde cache in toepassingsexemplaren. Het ondersteunt sleutel-waardecaching, gegevensstructuren zoals sets, gesorteerde sets en lijsten en optionele persistentie voor duurzaamheid bij opnieuw opstarten.

Zie de documentatie van Azure Managed Redis voor informatie over beschikbare lagen, capaciteitsplanning, netwerken en functiedetails.

Clienttoepassingen verbinden en configureren

Redis ondersteunt clienttoepassingen in veel programmeertalen. Voor .NET-toepassingen zijn verschillende clientbibliotheken beschikbaar, die elk geschikt zijn voor verschillende Redis-workloads. Uw keuze voor bibliotheek is afhankelijk van het feit of u Redis strikt als een cache of als een gegevensplatform met meerdere modellen gebruikt.

Gebruik de statische Connect methode van de ConnectionMultiplexer klasse om verbinding te maken met een Redis-server. De verbinding die met deze methode wordt gemaakt, is gebouwd voor gebruik gedurende de hele levensduur van de clienttoepassing. Meerdere gelijktijdige threads kunnen dezelfde verbinding gebruiken. Maak niet opnieuw verbinding en verbreek de verbinding telkens wanneer u een Redis-bewerking uitvoert, omdat dit de prestaties kan verminderen.

Zie Gebruik Azure Managed Redis in .NET Core voor voorbeelden van taalspecifieke verbindingen.

Een .NET-clientbibliotheek kiezen

Wanneer u Azure Managed Redis gebruikt voor caching, gebruikt u de volgende .NET bibliotheken:

  • StackExchange.Redis: Een Redis-client op laag niveau die hoge prestaties biedt. Gebruik dit wanneer u directe toegang nodig hebt tot Redis-opdrachten, atomische bewerkingen, transacties, pipelining of Lua-scripts.

  • Microsoft. Extensions.Caching.StackExchangeRedis: biedt een IDistributedCache-integratie voor ASP.NET Core. Gebruik deze functie voor eenvoudige sleutel-waardecache waar waarden worden opgeslagen als ondoorzichtige bytematrices. Deze abstractie biedt geen geavanceerde Redis-gegevensstructuren.

Deze bibliotheken bieden de primitieven die nodig zijn voor het bouwen van algemene cachepatronen, maar de toepassing moet de cachelogica zelf implementeren.

Cachepatronen implementeren

De eenvoudigste manier om Redis te gebruiken voor caching is door waarden onder sleutels op te slaan met behulp van het sleutelwaardemodel. Waarden kunnen tekenreeksen of binaire gegevens van willekeurige lengte zijn, waardoor Redis geschikt is voor het opslaan van geserialiseerde objecten, configuratiegegevens, sessiestatus of vooraf samengestelde resultaten.

Ontwerp uw keyspace zorgvuldig en gebruik zinvolle (maar niet uitgebreide) sleutels. Gebruik bijvoorbeeld gestructureerde sleutels zoals customer:100 (in plaats van alleen 100) om de sleutel voor de klant met id 100 weer te geven. Met dit schema kunt u onderscheid maken tussen waarden die verschillende gegevenstypen opslaan. U kunt bijvoorbeeld ook de sleutel orders:100 gebruiken om de sleutel voor de bestelling met ID 100 weer te geven.

Hoewel tekenreeksen de meest voorkomende benadering voor caching zijn, ondersteunt Redis een uitgebreide set systeemeigen gegevenstypen, zoals hashes, lijsten, sets, gesorteerde sets en streams, die flexibelere cachepatronen mogelijk maken. Zie de Redis-documentatie over gegevenstypen voor meer informatie over Redis-gegevenstypen.

Het Cache-Aside-patroon implementeren

Zoals beschreven in Bepalen hoe gegevens effectief worden opgeslagen in de cache, is een algemene benadering het laden van gegevens in de cache op aanvraag. In het volgende voorbeeld wordt eerst de cache gecontroleerd. Bij een cache-miss wordt de gegevensbron geraadpleegd, en het resultaat wordt opgeslagen voor toekomstige verzoeken.

var config = new ConfigurationOptions();
// ... configure endpoint, credentials, TLS, etc.
ConnectionMultiplexer redisHostConnection = ConnectionMultiplexer.Connect(config);
IDatabase cache = redisHostConnection.GetDatabase();

async Task<string> RetrieveItemAsync(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 is null)
    {
        itemValue = await GetItemFromDataSourceAsync(itemKey);
        await cache.StringSetAsync(itemKey, itemValue);
    }

    return itemValue;
}

Atomische en batchbewerkingen uitvoeren

Wanneer meerdere clients of toepassingsexemplaren een cache delen, moet u voorkomen dat gelijktijdige updates gegevens beschadigen. De algemene gelijktijdigheidsstrategieën worden beschreven in Gelijktijdigheid beheren in een cache eerder in dit artikel. Redis biedt verschillende mechanismen die deze strategieën implementeren.

  • Atomische bewerkingen met één sleutel: Gebruik opdrachten om een waarde in één stap bij te werken, waardoor racevoorwaarden worden geëlimineerd die optreden wanneer GET en SET afzonderlijk worden uitgegeven.

    • INCR, , INCRBY, DECRDECRBYatomisch verhogen of verlagen van een numerieke waarde. Gebruik in StackExchange.Redis IDatabase.StringIncrementAsync en IDatabase.StringDecrementAsync. Deze opdrachten zijn handig voor tellers, frequentielimieten en het bijhouden van quota waarbij meerdere clients dezelfde sleutel gelijktijdig bijwerken.

    • GETSET kent atomisch een nieuwe waarde toe aan een sleutel en retourneert de vorige waarde. Gebruik in StackExchange.Redis IDatabase.StringGetSetAsync:

      string oldValue = await cache.StringGetSetAsync("data:counter", 0);
      
  • Bewerkingen met meerdere sleutels:MGET en MSET meerdere tekenreekswaarden in één retour lezen of schrijven, waardoor de netwerkoverhead wordt verminderd wanneer u met meerdere sleutels tegelijk moet werken. De IDatabase.StringGetAsync en IDatabase.StringSetAsync methoden zijn overbelast om deze functionaliteit te ondersteunen:

    // Create a list of key-value pairs
    var keysAndValues =
        new KeyValuePair<RedisKey, RedisValue>[]
        {
            new("data:key1", "value1"),
            new("data:key99", "value2"),
            new("data:key322", "value3")
        };
    
    // Store the list of key-value pairs in the cache
    await cache.StringSetAsync(keysAndValues);
    ...
    // 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 = await cache.StringGetAsync(keys);
    
  • Transacties (optimistische gelijktijdigheid): U kunt de WATCH opdracht gebruiken om een of meer sleutels te bewaken voordat u een transactie met MULTI/EXECstart. Als er belangrijke wijzigingen worden aangebracht voordat de transactie wordt gestart, negeert Redis de transactie en kan de client het opnieuw proberen. De StackExchange-bibliotheek biedt ondersteuning voor transacties via de ITransaction interface.

    U maakt een ITransaction object met behulp van de IDatabase.CreateTransaction methode. U roept opdrachten aan bij de transactie met behulp van de methoden van het ITransaction object.

    De ITransaction interface biedt toegang tot een set methoden die vergelijkbaar is met de methoden die door de IDatabase interface worden geopend, behalve dat alle methoden asynchroon zijn. Dit betekent dat ze alleen worden uitgevoerd wanneer de ITransaction.Execute methode wordt aangeroepen. De waarde die door de ITransaction.Execute methode wordt geretourneerd, geeft aan of de transactie is gemaakt (waar) of dat deze is mislukt (onwaar).

    In het volgende codefragment wordt een voorbeeld getoond waarin twee tellers worden verhoogd en verlaagd als onderdeel van dezelfde transactie.

    ITransaction transaction = cache.CreateTransaction();
    
    var tx1 = transaction.StringIncrementAsync("data:counter1");
    var tx2 = transaction.StringDecrementAsync("data:counter2");
    
    bool result = await transaction.ExecuteAsync();
    
    Console.WriteLine($"Transaction {(result ? "succeeded" : "failed")}");
    
    if (result)
    {
        long increment = await tx1;
        long decrement = await tx2;
    
        Console.WriteLine($"Result of increment: {increment}");
        Console.WriteLine($"Result of decrement: {decrement}");
    }
    

    Redis-transacties zijn in tegenstelling tot transacties in relationele databases. Met Execute de methode worden alle opdrachten in de wachtrij geplaatst die de transactie vormen die moeten worden uitgevoerd en als een opdracht niet geldig is, stopt de transactie. Als alle opdrachten in de wachtrij worden geplaatst, wordt elke opdracht asynchroon uitgevoerd. Als een opdracht mislukt, worden de andere nog steeds verwerkt. Als u wilt controleren of een opdracht is voltooid, haalt u de resultaten op met behulp van de Result eigenschap van de bijbehorende taak, zoals wordt weergegeven in het vorige voorbeeld.

  • Lua-scripts schrijven. Voor updates die uit meerdere stappen bestaan en atomisch moeten worden uitgevoerd over meerdere sleutels heen, kunt u een Lua-script op de server uitvoeren. Redis voert het hele script uit als één bewerking zonder dat er andere opdrachten hoeven te worden gebruikt.

    Note

    Bij geclusterde implementaties moeten alle sleutels die betrokken zijn bij een transactie of Lua-script zich in dezelfde hash-site bevinden. Gebruik hashtags zoals customer:{123}:name of customer:{123}:email om gerelateerde sleutels te koppelen.

Fire-and-forget-cachebewerkingen uitvoeren

Wanneer een cache-update geen invloed heeft op de juistheid van de toepassing, zoals het verhogen van een weergaveteller of het vernieuwen van een niet-kritieke statistiek, kunt u het wachten op het antwoord van de server overslaan. Bij een fire-and-forget-cachebewerking start uw applicatie een achtergrondtaak en gaat vervolgens verder zonder te wachten tot de taak is voltooid. Redis ondersteunt fire-and-forget-bewerkingen, die de latentie van retouren voor de client verminderen via opdrachtvlaggingen:

await cache.StringSetAsync("data:key1", 99);
...
cache.StringIncrement("data:key1", flags: CommandFlags.FireAndForget);

Sleutels opgeven die automatisch verlopen

De verloopstrategieën die worden beschreven in Verlooptijd van gegevens beheren in een cache , worden geïmplementeerd in Redis via TTLs per sleutel. Wanneer u een item opslaat in een Redis-cache, kunt u een time-out opgeven waarna het item automatisch wordt verwijderd. U kunt ook een query uitvoeren op de tijd die een sleutel heeft voordat deze verloopt met behulp van de TTL opdracht. Deze opdracht is beschikbaar voor StackExchange-toepassingen via de IDatabase.KeyTimeToLive methode.

In het volgende codefragment ziet u hoe u een verlooptijd van 20 seconden instelt op een sleutel en hoe u de resterende levensduur van de sleutel opvraagt:

// 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 null
TimeSpan? expiry = cache.KeyTimeToLive("data:key1");

U kunt de vervaldatum ook instellen op een specifieke datum en tijd met behulp van de EXPIREAT opdracht, die beschikbaar is in de StackExchange-bibliotheek als methode KeyExpireAsync . Er wordt een DateTime parameter gebruikt:

await cache.StringSetAsync("data:key1", 99);
await cache.KeyExpireAsync("data:key1",
    new DateTime(2026, 9, 1, 0, 0, 0, DateTimeKind.Utc));

Tip

U kunt handmatig een item uit de cache verwijderen met behulp van de DEL opdracht, die beschikbaar is via de StackExchange-bibliotheek als methode IDatabase.KeyDeleteAsync .

Wanneer Redis de geheugenlimiet bereikt, worden sleutels verwijderd volgens een geconfigureerd verwijderingsbeleid. Het standaardbeleid is volatile-lru, waarmee de minst recent gebruikte sleutel met een TTL-set wordt verwijderd. Andere beleidsregels zijn allkeys-lru, volatile-randomen noeviction (waardoor schrijfbewerkingen mislukken wanneer het geheugen vol is). Kies een verwijderingsbeleid op basis van of uw toepassing gebruikmaakt van TTLs consistent en of u sleutels zonder verlooptijd wilt beveiligen. Zie Geheugenbeheer voor meer informatie.

Items in cache kruislings correleren

Wanneer u gerelateerde items in de cache opslaat, moet u ze vaak vinden op basis van de relatie in plaats van alleen op de primaire sleutel. U kunt bijvoorbeeld blogberichten in de cache opslaan en query's beantwoorden zoals 'welke berichten delen tag Y?' of 'welke tags horen bij post X?'

In Azure Managed Redis is de aanbevolen methode om RedisJSON en RediSearch te gebruiken. Sla elk item in de cache op als een JSON-document met de bijbehorende metagegevens en maak vervolgens een RediSearch-index over de velden waarop u een query moet uitvoeren. RediSearch verwerkt reverse lookups, taggebaseerd filteren, bereikquery's en zoeken in volledige tekst zonder dat uw toepassing afzonderlijke indexstructuren hoeft te onderhouden.

Voor eenvoudigere scenario's kunt u Redis Sets ook gebruiken om handmatig voorwaartse en omgekeerde indexen te bouwen. Bewaar een set per post (met de bijbehorende tags) en een set per tag (met de post-id's):

foreach (BlogPost post in posts)
{
    string postTagsKey = $"blog:posts:{post.Id}:tags";
    await cache.SetAddAsync(
        postTagsKey, post.Tags.Select(s => (RedisValue)s).ToArray());

    foreach (var tag in post.Tags)
    {
        await cache.SetAddAsync($"tag:{tag}:blog:posts", post.Id);
    }
}

U kunt vervolgens query's uitvoeren op tags voor een bericht met behulp van SetMembersAsync, algemene tags zoeken in berichten met behulp van SetCombineAsync(SetOperation.Intersect, ...), of alle berichten voor een bepaalde tag zoeken. De afweging is dat uw toepassing zowel de voorwaartse als omgekeerde sets moet onderhouden, waardoor de complexiteit toeneemt naarmate het aantal relaties toeneemt.

Onlangs geopende items zoeken

Veel toepassingen moeten de laatst geopende of bekeken items bijhouden. Een blogsite kan bijvoorbeeld de laatst gelezen berichten weergeven aan een terugkerende bezoeker. Redis-lijsten bieden een efficiënte manier om op recency gebaseerde cachepatronen te implementeren. Elementen kunnen met LPUSH of RPUSH aan beide uiteinden van de lijst worden toegevoegd en met LPOP of RPOP worden verwijderd. Gebruik LTRIM dit om de lijstlengte te beperken en niet-gebonden geheugengroei te voorkomen.

Een leaderboard implementeren

Redis Sorted Sets (ZSETs) onderhouden geordende classificaties door elk element te koppelen aan een numerieke score. Redis onderhoudt de volgorde automatisch. ZADD is O(log N), en bereikquery's zoals ZRANGE en ZREVRANGE zijn O(log N + M), waarbij M het aantal geretourneerde elementen is, zodat gesorteerde sets efficiënt blijven, zelfs bij grote itemaantallen.

Items toevoegen aan een leaderboard

In het volgende voorbeeld ziet u hoe u een blogbericht en de bijbehorende score toevoegt aan een leaderboard met behulp van de ZADD opdracht via SortedSetAddAsync:

var db = connection.GetDatabase();
string redisKey = "blog:post_rankings";

BlogPost blogPost = ...; // The blog post being ranked

await db.SortedSetAddAsync(redisKey, blogPost.Title, blogPost.Score);
Gerangschikte items ophalen

U kunt items in oplopende scorevolgorde ophalen met behulp van SortedSetRangeByRankWithScoresAsync:

var entries = await db.SortedSetRangeByRankWithScoresAsync(redisKey);

foreach (var entry in entries)
{
    Console.WriteLine($"{entry.Element}: {entry.Score}");
}

Note

SortedSetRangeByRankAsync retourneert alleen lidwaarden, geen scores.

Top-N-items ophalen

Als u de hoogst scorende items wilt ophalen, zoals de top 10 berichten, gebruikt u de aflopende volgorde:

foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(
                               redisKey, 0, 9, Order.Descending))
{
    Console.WriteLine(post);
}
Items ophalen binnen een scorebereik

U kunt ook query's uitvoeren op items op basis van scoregrenzen in plaats van rangschikken:

foreach (var post in await cache.SortedSetRangeByScoreWithScoresAsync(
                               redisKey, 5000, 100000))
{
    Console.WriteLine(post);
}

Als u wilt voorkomen dat een leaderboard voor onbepaalde tijd groeit, verwijdert u oude items met behulp van SortedSetRemoveRangeByRankAsync of gebruikt u tijdgebonden sleutels (bijvoorbeeld dagelijkse of wekelijkse leaderboards). U kunt scores atomisch bijwerken met behulp van SortedSetIncrementAsync (ZINCRBY).

Sessiestatus en HTML-uitvoer van cache

U kunt Azure Managed Redis gebruiken om sessiestatus- en uitvoercachegegevens op te slaan voor ASP.NET Core en ASP.NET toepassingen. Wanneer u sessiegegevens bewaart en uitvoer weergeeft in een gedeelde redis-cache, worden toepassingen uitgevoerd op meerdere exemplaren, zoals in Azure App Service, Azure Kubernetes Service (AKS), Azure Container Appsof virtuele-machineschaalsets kunnen consistente gebruikerservaringen onderhouden zonder dat hiervoor serveraffiniteit is vereist.

Tip

Implementeer uw toepassing en azure Managed Redis-exemplaar in dezelfde Azure-regio voor de beste prestaties.

ASP.NET Core

ASP.NET Core-toepassingen maken gebruik van de IDistributedCache abstractie en sessie-middleware. Azure Managed Redis kan worden geïntegreerd met IDistributedCache behulp van het Microsoft.Extensions.Caching.StackExchangeRedis pakket.

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "<your-cache-name>.<region>.redis.azure.net:10000";
    options.InstanceName = "app-cache:";
});

builder.Services.AddSession();

De ASP.NET Core-middleware voor outputcache kan ook Redis gebruiken als onderliggende gedistribueerde opslag, waardoor applicaties gerenderde fragmenten of pagina's tussen alle instanties kunnen delen. Zie ASP.NET Core-uitvoercacheprovider voor Redis voor meer informatie.

.NET Aspire-integratie

.NET Aspire-toepassingen kunnen het Aspire.Hosting.Azure.Redis pakket gebruiken om een Door Azure beheerde Redis-resource in de app-host te declareren. Gebruikende projecten ontvangen automatisch de verbindingsconfiguratie via afhankelijkheidsinjectie, waardoor handmatig beheer van verbindingsreeksen tussen services wordt geëlimineerd.

// App host: declare the Azure Managed Redis resource
var cache = builder.AddAzureManagedRedis("cache");

builder.AddProject<Projects.ProductService>()
    .WithReference(cache);

Consumerende services registreren de gedistribueerde cache op dezelfde manier als elke andere IDistributedCache-provider. Zie Aan de slag met Redis-integratie voor meer informatie.

Hoge beschikbaarheid, schaalbaarheid en partitionering

Elk beheerd Redis-exemplaar van Azure maakt gebruik van primaire/replicareplicatie. De service bewaakt de status van het knooppunt en bevordert automatisch een replica als de primaire mislukt. Omdat replicatie asynchroon is, kan een kleine hoeveelheid recent geschreven gegevens verloren gaan tijdens een onverwachte failover. Zie Hoge beschikbaarheid en schaalbaarheid implementeren voor de algemene strategieën voor replicatie, failover en gelaagde caching, en voor betere prestaties eerder in dit artikel.

U kunt een lokale cache in het geheugen combineren met Azure Managed Redis om de latentie te verminderen en een terugval te bieden als de gedeelde cache tijdelijk onbereikbaar is. Het Circuit-Breaker-patroon en Cache-Aside patroon helpen bij het beheren van deze gelaagde benadering.

Voor workloads die de capaciteit van één knooppunt overschrijden, ondersteunt Azure Managed Redis partitioneringsgegevens (sharding) over meerdere Redis-knooppunten. Met beide clusterbeleidsregels worden gegevens automatisch over knooppunten geshard met behulp van sleutel-naar-shard-hashing, automatische failover en hersynchronisatie en online resharding (uitschalen en inschalen). Azure Managed Redis ondersteunt twee clusteringbeleidsregels:

  • OSS-clusteringbeleid (standaard): Clients communiceren rechtstreeks met de juiste shard en volgen oss Redis-clustersemantiek, waaronder MOVED- en ASK-omleidingen. Clusterbewuste clients zoals StackExchange.Redis verwerken deze omleidingen automatisch. Dit beleid biedt de laagste routeringsoverhead.

  • Redis Enterprise Clustering Policy: Een proxy biedt transparante routering via één eindpunt. Clients hoeven geen clusterbewuste logica te implementeren of MOVED/ASK-antwoorden te verwerken. Dit beleid biedt eenvoudigere clientintegratie, maar introduceert een kleine hoeveelheid routeringsoverhead.

Azure Managed Redis ondersteunt ook nonclustered modus, die gebruikmaakt van één primair/replicapaar zonder sharding. Deze modus is geschikt voor kleinere workloads waarvoor geen horizontale uitschaling is vereist.

Note

Aangepaste partitioneringsmodellen, zoals hashing aan de clientzijde of niet-Microsoft proxy's, zijn doorgaans alleen nodig in zelfbeheerde Redis-implementaties op virtuele machines of Kubernetes. Azure Managed Redis-clustering verwerkt automatisch routering, failover en resharding.

Actieve geo-replicatie

Voor beschikbaarheid in meerdere regio's ondersteunt Azure Managed Redis actieve geo-replicatie, waarmee exemplaren in Azure regio's worden gekoppeld aan één replicatiegroep. Elk exemplaar kan lees- en schrijfbewerkingen verwerken en wijzigingen worden automatisch gesynchroniseerd. Uw toepassing is verantwoordelijk voor het omleiden van verkeer naar een goed functionerend exemplaar tijdens een regionale storing. Zie Actieve geo-replicatie configureren voor meer informatie.

Gegevenspersistentie

Gegevens in de cache in Azure Managed Redis worden standaard in het geheugen opgeslagen en kunnen verloren gaan als een knooppunt opnieuw wordt opgestart of een failover-overschakeling uitvoert. Voor workloads waarbij het herbouwen van de cache vanuit het brongegevensarchief traag of duur zou zijn, biedt Azure Managed Redis ondersteuning voor optionele gegevenspersistentie:

  • MET RDB-momentopnamen (Redis-database) worden periodieke momentopnamen van een bepaald tijdstip gemaakt die zijn opgeslagen op een beheerde schijf. RDB heeft een minimaal prestatie-effect tijdens normale bewerkingen, maar gegevens die zijn geschreven sinds de laatste momentopname kunnen verloren gaan.

  • AOF (Append-Only File) registreert elke schrijfbewerking naar schijf. AOF vermindert mogelijk gegevensverlies tot ongeveer één seconde schrijfbewerkingen, maar produceert grotere bestanden en kan de schrijfdoorvoer verminderen.

U kunt zowel RDB als AOF samen gebruiken. Redis laadt de RDB-momentopname bij het opstarten en afspeelt vervolgens het AOF-logboek opnieuw voor bijna volledig herstel.

Important

Persistentie verbetert de duurzaamheid tegen knooppuntfouten, maar het is geen mechanisme voor back-up of herstel na noodgevallen. Behoud voor kritieke gegevens altijd de gezaghebbende kopie in uw brongegevensarchief en gebruik het Cache-Aside patroon om de cache opnieuw te vullen.

Zie Gegevenspersistentie configureren voor configuratiedetails.

Gegevens in cache beveiligen in Azure Managed Redis

In de richtlijnen voor Bescherming van gecachte gegevens beschrijft de problemen met toegangsbeheer en gegevens-in-transit. Azure Managed Redis helpt u deze problemen op de volgende manieren aan te pakken:

  • Gebruik Microsoft Entra ID-verificatie als primair mechanisme voor toegangsbeheer en volg het principe van minimale bevoegdheden bij het verlenen van toegang.

  • Gebruik privé-eindpunten om netwerktoegang te beperken, zodat verkeer niet via het openbare internet gaat.

  • Azure Managed Redis versleutelt gegevens tijdens de overdracht met behulp van TLS en versleutelt gegevens in rust.

Serialisatieoverwegingen

Wanneer u .NET-objecten in Redis opslaat als tekenreekswaarden, moet u ze serialiseren. Wanneer u een serialisatie-indeling kiest, kunt u rekening houden met de prestaties, interoperabiliteit, versiebeheer en nettoladinggrootte. Er is geen snelste serializer voor alle scenario's. Benchmarks zijn sterk afhankelijk van context en weerspiegelen mogelijk niet uw werkelijke workload.

Als uw Beheerde Redis-laag ondersteuning biedt voor RedisJSON, kunt u objecten opslaan als systeemeigen JSON-documenten en afzonderlijke velden opvragen zonder de volledige waarde te deserialiseren:

public static class RedisJsonExtensions
{
    public static async Task<T?> GetAsync<T>(
        this IDatabase cache,
        string key,
        string path = "$")
    {
        var result = await cache.ExecuteAsync("JSON.GET", key, path);

        if (result.IsNull)
            return default;

        return JsonSerializer.Deserialize<T>(result!);
    }

    public static async Task SetAsync<T>(
        this IDatabase cache,
        string key,
        T value,
        TimeSpan? expiry = null,
        string path = "$")
    {
        var json = JsonSerializer.Serialize(value);

        // Store JSON document
        await cache.ExecuteAsync("JSON.SET", key, path, json);

        // Apply TTL if provided
        if (expiry.HasValue)
        {
            await cache.KeyExpireAsync(key, expiry);
        }
    }

    public static async Task<bool> ExpireAsync(
        this IDatabase cache,
        string key,
        TimeSpan expiry)
    {
        return await cache.KeyExpireAsync(key, expiry);
    }
}

Wanneer u in plaats daarvan waarden als Redis-tekenreeksen serialiseert, zijn algemene indelingsopties:

  • JSON : door mensen leesbare indeling met brede platformoverschrijdende ondersteuning. Niet de meest compacte indeling, maar een goede keuze wanneer items in de cache rechtstreeks naar HTTP-clients worden geretourneerd, omdat er geen extra herserialisatie- en reserialisatiestap wordt uitgevoerd.

  • MessagePack : een compacte binaire indeling die geen schemavereiste heeft. Produceert kleinere nettoladingen dan JSON met lagere serialisatieoverhead.

  • Protocol Buffers (protobuf): een op schema gebaseerde binair formaat dat compacte datapakketten produceert. Vereist .proto definitiebestanden en een compilatiestap om taalspecifieke code te genereren.

  • BSON : een binaire indeling die JSON uitbreidt met meer typen, zoals datums en onbewerkte binaire gegevens. Nettoladingen zijn vergelijkbaar met JSON. Een praktische keuze wanneer uw toepassing al BSON elders gebruikt, bijvoorbeeld met MongoDB.

De volgende patronen zijn mogelijk relevant wanneer u caching implementeert in uw toepassingen:

  • Cache-Aside patroon: In dit patroon wordt beschreven hoe u gegevens op aanvraag in een cache laadt vanuit een gegevensarchief. Het helpt ook consistentie te behouden tussen gegevens in de cache en gegevens in het oorspronkelijke gegevensarchief.

  • Sharding-patroon: dit patroon bevat informatie over het implementeren van horizontale partitionering om de schaalbaarheid te verbeteren bij het opslaan en openen van grote hoeveelheden gegevens.