sql >> Database teknologi >  >> NoSQL >> Memcached

Python + Memcached:Effektiv cachelagring i distribuerede applikationer

Når du skriver Python-applikationer, er caching vigtigt. Brug af en cache til at undgå genberegning af data eller adgang til en langsom database kan give dig et fantastisk ydelsesboost.

Python tilbyder indbyggede muligheder for caching, fra en simpel ordbog til en mere komplet datastruktur såsom functools.lru_cache . Sidstnævnte kan cache ethvert element ved hjælp af en algoritme, der er brugt mindst for nylig for at begrænse cachestørrelsen.

Disse datastrukturer er dog pr. definition lokale til din Python-proces. Når flere kopier af din applikation kører på tværs af en stor platform, tillader brug af en datastruktur i hukommelsen ikke deling af det cachelagrede indhold. Dette kan være et problem for store og distribuerede applikationer.

Derfor, når et system er distribueret på tværs af et netværk, har det også brug for en cache, der er distribueret på tværs af et netværk. I dag er der masser af netværksservere, der tilbyder caching-kapacitet - vi har allerede dækket, hvordan man bruger Redis til caching med Django.

Som du vil se i denne tutorial, er memcached en anden god mulighed for distribueret caching. Efter en hurtig introduktion til grundlæggende memcached-brug lærer du om avancerede mønstre såsom "cache and set" og brug af reservecaches for at undgå problemer med ydelse af kold cache.


Installerer memcached

Memcached er tilgængelig for mange platforme:

  • Hvis du kører Linux , kan du installere det ved at bruge apt-get install memcached eller yum install memcached . Dette vil installere memcached fra en forudbygget pakke, men du kan også bygge memcached fra kilden, som forklaret her.
  • Til macOS , at bruge Homebrew er den enkleste mulighed. Bare kør brew install memcached efter du har installeret Homebrew-pakkehåndteringen.
  • Windows , skal du selv kompilere memcached eller finde præ-kompilerede binære filer.

Når den er installeret, memcached kan simpelthen startes ved at kalde memcached kommando:

$ memcached

Før du kan interagere med memcached fra Python-land, skal du installere en memcached klient bibliotek. Du vil se, hvordan du gør dette i næste afsnit, sammen med nogle grundlæggende cache-adgangsoperationer.



Lagring og hentning af cachelagrede værdier ved hjælp af Python

Hvis du aldrig har brugt memcached , det er ret nemt at forstå. Det giver dybest set en gigantisk netværkstilgængelig ordbog. Denne ordbog har nogle få egenskaber, der adskiller sig fra en klassisk Python-ordbog, hovedsagelig:

  • Nøgler og værdier skal være bytes
  • Nøgler og værdier slettes automatisk efter en udløbstid

Derfor er de to grundlæggende handlinger til interaktion med memcached er set og get . Som du måske har gættet, bruges de til henholdsvis at tildele en værdi til en nøgle eller til at få en værdi fra en nøgle.

Mit foretrukne Python-bibliotek til at interagere med memcached er pymemcache -Jeg anbefaler at bruge det. Du kan blot installere det ved hjælp af pip:

$ pip install pymemcache

Følgende kode viser, hvordan du kan oprette forbindelse til memcached og brug den som en netværksdistribueret cache i dine Python-applikationer:

>>> from pymemcache.client import base

# Don't forget to run `memcached' before running this next line:
>>> client = base.Client(('localhost', 11211))

# Once the client is instantiated, you can access the cache:
>>> client.set('some_key', 'some value')

# Retrieve previously set data again:
>>> client.get('some_key')
'some value'

memcached netværksprotokol er virkelig enkel og dens implementering ekstremt hurtig, hvilket gør det nyttigt at gemme data, som ellers ville være langsomme at hente fra den kanoniske datakilde eller at beregne igen:

Selvom det er ligetil nok, tillader dette eksempel lagring af nøgle/værdi-tupler på tværs af netværket og adgang til dem gennem flere, distribuerede, kørende kopier af din applikation. Dette er forenklet, men alligevel kraftfuldt. Og det er et godt første skridt mod at optimere din applikation.



Udløber automatisk cachelagrede data

Når du gemmer data i memcached , kan du indstille en udløbstid – et maksimalt antal sekunder for memcached at holde nøglen og værdien omkring. Efter denne forsinkelse memcached fjerner automatisk nøglen fra sin cache.

Hvad skal du indstille denne cachetid til? Der er ikke noget magisk tal for denne forsinkelse, og det vil helt afhænge af typen af ​​data og applikation, du arbejder med. Det kan være et par sekunder, eller det kan være et par timer.

Cache-invalidering , som definerer hvornår cachen skal fjernes, fordi den er ude af synkronisering med de aktuelle data, er også noget, din applikation skal håndtere. Især hvis du præsenterer data, der er for gamle eller forældede skal undgås.

Her er der igen ingen magisk opskrift; det afhænger af den type applikation, du bygger. Der er dog flere udestående sager, der bør håndteres - som vi endnu ikke har dækket i ovenstående eksempel.

En caching-server kan ikke vokse uendeligt - hukommelse er en begrænset ressource. Derfor vil nøgler blive skyllet ud af caching-serveren, så snart den har brug for mere plads til at gemme andre ting.

Nogle nøgler kan også være udløbet, fordi de nåede deres udløbstid (også nogle gange kaldet "time-to-live" eller TTL). I disse tilfælde går dataene tabt, og den kanoniske datakilde skal forespørges igen.

Det her lyder mere kompliceret, end det i virkeligheden er. Du kan generelt arbejde med følgende mønster, når du arbejder med memcached i Python:

from pymemcache.client import base


def do_some_query():
    # Replace with actual querying code to a database,
    # a remote REST API, etc.
    return 42


# Don't forget to run `memcached' before running this code
client = base.Client(('localhost', 11211))
result = client.get('some_key')

if result is None:
    # The cache is empty, need to get the value
    # from the canonical source:
    result = do_some_query()

    # Cache the result for next time:
    client.set('some_key', result)

# Whether we needed to update the cache or not,
# at this point you can work with the data
# stored in the `result` variable:
print(result)

Bemærk: Håndtering af manglende nøgler er obligatorisk på grund af normale udskylningsoperationer. Det er også obligatorisk at håndtere det kolde cache-scenarie, dvs. når memcached er lige startet. I så fald vil cachen være helt tom, og cachen skal genudfyldes, én anmodning ad gangen.

Dette betyder, at du skal se alle cachelagrede data som flygtige. Og du skal aldrig forvente, at cachen indeholder en værdi, du tidligere har skrevet til den.



Opvarmning af en kold cache

Nogle af de kolde cache-scenarier kan ikke forhindres, for eksempel en memcached krak. Men nogle kan, for eksempel migrere til en ny memcached server.

Når det er muligt at forudsige, at et koldt cache-scenarie vil ske, er det bedre at undgå det. En cache, der skal genopfyldes, betyder, at den kanoniske lagring af de cachelagrede data pludselig vil blive massivt ramt af alle cachebrugere, der mangler cachedata (også kendt som det tordnende flokproblem).

pymemcache giver en klasse ved navn FallbackClient der hjælper med at implementere dette scenarie som vist her:

from pymemcache.client import base
from pymemcache import fallback


def do_some_query():
    # Replace with actual querying code to a database,
    # a remote REST API, etc.
    return 42


# Set `ignore_exc=True` so it is possible to shut down
# the old cache before removing its usage from 
# the program, if ever necessary.
old_cache = base.Client(('localhost', 11211), ignore_exc=True)
new_cache = base.Client(('localhost', 11212))

client = fallback.FallbackClient((new_cache, old_cache))

result = client.get('some_key')

if result is None:
    # The cache is empty, need to get the value 
    # from the canonical source:
    result = do_some_query()

    # Cache the result for next time:
    client.set('some_key', result)

print(result)

FallbackClient forespørgsler, som den gamle cache overførte til dens konstruktør under respekt for rækkefølgen. I dette tilfælde vil den nye cache-server altid blive forespurgt først, og i tilfælde af en cache-miss, vil den gamle blive forespurgt - hvilket undgår en mulig returrejse til den primære datakilde.

Hvis en tast er indstillet, vil den kun blive sat til den nye cache. Efter nogen tid kan den gamle cache tages ud af drift og FallbackClient kan erstattes instrueret med new_cache klient.



Tjek og indstil

Når du kommunikerer med en fjerncache, kommer det sædvanlige samtidighedsproblem tilbage:Der kan være flere klienter, der forsøger at få adgang til den samme nøgle på samme tid. memcached giver et tjek og sæt operation, forkortet til CAS , som hjælper med at løse dette problem.

Det enkleste eksempel er en applikation, der ønsker at tælle antallet af brugere, den har. Hver gang en besøgende opretter forbindelse, øges en tæller med 1. Ved hjælp af memcached , ville en simpel implementering være:

def on_visit(client):
    result = client.get('visitors')
    if result is None:
        result = 1
    else:
        result += 1
    client.set('visitors', result)

Men hvad sker der, hvis to forekomster af applikationen forsøger at opdatere denne tæller på samme tid?

Det første kald client.get('visitors') vil returnere det samme antal besøgende for dem begge, lad os sige, at det er 42. Så vil begge tilføje 1, beregne 43 og sætte antallet af besøgende til 43. Det tal er forkert, og resultatet skulle være 44, dvs. 42 + 1 + 1.

For at løse dette samtidighedsproblem, CAS-driften af ​​memcached er praktisk. Følgende uddrag implementerer en korrekt løsning:

def on_visit(client):
    while True:
        result, cas = client.gets('visitors')
        if result is None:
            result = 1
        else:
            result += 1
        if client.cas('visitors', result, cas):
            break

gets metoden returnerer værdien ligesom get metode, men den returnerer også en CAS-værdi .

Hvad der er i denne værdi er ikke relevant, men det bruges til næste metode cas opkald. Denne metode svarer til set operation, bortset fra at den mislykkes, hvis værdien er ændret siden gets operation. I tilfælde af succes brydes løkken. Ellers genstartes handlingen fra begyndelsen.

I scenariet, hvor to forekomster af applikationen forsøger at opdatere tælleren på samme tid, er det kun én, der lykkes med at flytte tælleren fra 42 til 43. Den anden forekomst får en False værdi returneret af client.cas opkald, og skal prøve løkken igen. Den vil hente 43 som værdi denne gang, vil øge den til 44 og dens cas opkald vil lykkes, og dermed løse vores problem.

At øge en tæller er interessant som et eksempel til at forklare, hvordan CAS fungerer, fordi det er forsimplet. Dog memcached giver også incr og decr metoder til at øge eller formindske et heltal i en enkelt anmodning i stedet for at udføre flere gets /cas opkald. I virkelige applikationer gets og cas bruges til mere komplekse datatyper eller operationer

De fleste eksterne cacheservere og datalager giver en sådan mekanisme til at forhindre samtidighedsproblemer. Det er afgørende at være opmærksom på disse tilfælde for at gøre korrekt brug af deres funktioner.



Ud over cachelagring

De enkle teknikker, der er illustreret i denne artikel, viste dig, hvor nemt det er at udnytte memcached for at fremskynde ydeevnen af ​​din Python-applikation.

Bare ved at bruge de to grundlæggende "sæt" og "hent" operationer kan du ofte fremskynde datahentning eller undgå at genberegne resultater igen og igen. Med memcached kan du dele cachen på tværs af et stort antal distribuerede noder.

Andre, mere avancerede mønstre, du så i denne øvelse, såsom Check And Set (CAS) operation giver dig mulighed for at opdatere data, der er gemt i cachen samtidigt på tværs af flere Python-tråde eller processer, mens du undgår datakorruption.

Hvis du er interesseret i at lære mere om avancerede teknikker til at skrive hurtigere og mere skalerbare Python-applikationer, så tjek Scaling Python. Det dækker mange avancerede emner såsom netværksdistribution, køsystemer, distribueret hashing og kodeprofilering.



  1. VersionError:Ingen matchende dokument fundet fejl på Node.js/Mongoose

  2. hvordan man bruger aggregatfunktion i meteor

  3. HDFS Tutorial – En komplet introduktion til HDFS for begyndere

  4. 9 ClusterControl-funktioner, du ikke finder i andre databasestyringsværktøjer