sql >> Database teknologi >  >> NoSQL >> Redis

Caching i Django med Redis

Applikationsydelse er afgørende for dit produkts succes. I et miljø, hvor brugerne forventer en hjemmesides svartider på mindre end et sekund, kan konsekvenserne af en langsom applikation måles i kroner og øre. Selvom du ikke sælger noget, forbedrer hurtig sideindlæsning oplevelsen af ​​at besøge dit websted.

Alt, hvad der sker på serveren fra det øjeblik, den modtager en anmodning, til det øjeblik, den returnerer et svar, øger den tid, det tager at indlæse en side. Som en generel tommelfingerregel, jo mere behandling du kan eliminere på serveren, jo hurtigere vil din applikation udføre. Caching af data, efter at de er blevet behandlet og derefter servering fra cachen, næste gang der anmodes om dem, er en måde at aflaste serveren på. I dette selvstudium vil vi udforske nogle af de faktorer, der binder din applikation til, og vi vil demonstrere, hvordan man implementerer caching med Redis for at modvirke deres virkninger.

Gratis bonus: Klik her for at få adgang til en gratis Django Learning Resources Guide (PDF), der viser dig tips og tricks samt almindelige faldgruber, du skal undgå, når du bygger Python + Django-webapplikationer.


Hvad er Redis?

Redis er et datastrukturlager i hukommelsen, der kan bruges som en caching-motor. Da det holder data i RAM, kan Redis levere det meget hurtigt. Redis er ikke det eneste produkt, vi kan bruge til caching. Memcached er et andet populært cachesystem i hukommelsen, men mange mennesker er enige om, at Redis er Memcached overlegen under de fleste omstændigheder. Personligt kan vi godt lide, hvor nemt det er at konfigurere og bruge Redis til andre formål, såsom Redis Queue.



Kom godt i gang

Vi har lavet en eksempelapplikation for at introducere dig til begrebet caching. Vores applikation bruger:

  • Django (v1.9.8)
  • Django Debug Toolbar (v1.4)
  • django-redis (v4.4.3)
  • Redis (v3.2.0)

Installer appen

Inden du kloner depotet, skal du installere virtualenvwrapper, hvis du ikke allerede har det. Dette er et værktøj, der lader dig installere de specifikke Python-afhængigheder, som dit projekt har brug for, så du isoleret kan målrette de versioner og biblioteker, der kræves af din app.

Skift derefter mapper til det sted, hvor du opbevarer projekter, og kloner eksempel-app-lageret. Når det er gjort, skal du ændre mapper til det klonede lager og derefter lave et nyt virtuelt miljø til eksempelappen ved hjælp af mkvirtualenv kommando:

$ mkvirtualenv django-redis
(django-redis)$

BEMÆRK: Oprettelse af et virtuelt miljø med mkvirtualenv aktiverer den også.

Installer alle de nødvendige Python-afhængigheder med pip , og tjek derefter følgende tag ud:

(django-redis)$ git checkout tags/1

Afslut opsætningen af ​​eksempelappen ved at bygge databasen og udfylde den med eksempeldata. Sørg også for at oprette en superbruger, så du kan logge ind på administratorsiden. Følg kodeeksemplerne nedenfor, og prøv derefter at køre appen for at sikre dig, at den fungerer korrekt. Besøg administratorsiden i browseren for at bekræfte, at dataene er blevet korrekt indlæst.

(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver

Når du har kørt Django-appen, skal du gå videre til Redis-installationen.



Installer Redis

Download og installer Redis ved at bruge instruktionerne i dokumentationen. Alternativt kan du installere Redis ved hjælp af en pakkehåndtering såsom apt-get eller hjemmebrygget afhængigt af dit OS.

Kør Redis-serveren fra et nyt terminalvindue.

$ redis-server

Start derefter Redis kommandolinjegrænseflade (CLI) i et andet terminalvindue og test, at den forbinder til Redis-serveren. Vi vil bruge Redis CLI til at inspicere de nøgler, som vi føjer til cachen.

$ redis-cli ping
PONG

Redis leverer en API med forskellige kommandoer, som en udvikler kan bruge til at handle på datalageret. Django bruger django-redis for at udføre kommandoer i Redis.

Når vi ser på vores eksempelapp i en teksteditor, kan vi se Redis-konfigurationen i settings.py fil. Vi definerer en standard cache med CACHES indstilling ved hjælp af en indbygget django-redis cache som vores backend. Redis kører på port 6379 som standard, og vi peger på den placering i vores indstilling. En sidste ting at nævne er, at django-redis tilføjer nøglenavne med et præfiks og en version for at hjælpe med at skelne lignende nøgler. I dette tilfælde har vi defineret præfikset til at være "eksempel".

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient"
        },
        "KEY_PREFIX": "example"
    }
}

BEMÆRK :Selvom vi har konfigureret cache-backend, har ingen af ​​visningsfunktionerne implementeret caching.




Appens ydeevne

Som vi nævnte i begyndelsen af ​​denne vejledning, forsinker alt, hvad serveren gør for at behandle en anmodning, applikationens indlæsningstid. Behandlingsomkostningerne ved drift af forretningslogik og gengivelse af skabeloner kan være betydelige. Netværksforsinkelse påvirker den tid, det tager at forespørge i en database. Disse faktorer spiller ind, hver gang en klient sender en HTTP-anmodning til serveren. Når brugere starter mange anmodninger i sekundet, bliver virkningerne på ydeevnen mærkbare, da serveren arbejder på at behandle dem alle.

Når vi implementerer caching, lader vi serveren behandle en anmodning én gang, og så gemmer vi den i vores cache. Da anmodninger om den samme URL modtages af vores applikation, trækker serveren resultaterne fra cachen i stedet for at behandle dem på ny hver gang. Typisk sætter vi en tid til at leve på de cachelagrede resultater, så dataene kan opdateres med jævne mellemrum, hvilket er et vigtigt skridt at implementere for at undgå at vise forældede data.

Du bør overveje at cache resultatet af en anmodning, når følgende tilfælde er sande:

  • gengivelse af siden involverer en masse databaseforespørgsler og/eller forretningslogik,
  • siden besøges ofte af dine brugere,
  • dataene er de samme for hver bruger,
  • og dataene ændres ikke ofte.

Start med at måle ydeevne

Begynd med at teste hastigheden på hver side i din ansøgning ved at benchmarke, hvor hurtigt din ansøgning returnerer et svar efter at have modtaget en anmodning.

For at opnå dette vil vi sprænge hver side med en serie af anmodninger ved hjælp af loadtest, en HTTP-belastningsgenerator og derefter være meget opmærksom på anmodningshastigheden. Besøg linket ovenfor for at installere. Når det er installeret, test resultaterne mod /cookbook/ URL-sti:

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

Bemærk, at vi behandler omkring 16 anmodninger i sekundet:

Requests per second: 16

Når vi ser på, hvad koden gør, kan vi træffe beslutninger om, hvordan vi foretager ændringer for at forbedre ydeevnen. Applikationen foretager 3 netværksopkald til en database med hver anmodning til /cookbook/ , og det tager tid for hvert opkald at åbne en forbindelse og udføre en forespørgsel. Besøg /cookbook/ URL i din browser, og udvid fanen Django Debug Toolbar for at bekræfte denne adfærd. Find menuen mærket "SQL", og læs antallet af forespørgsler:

kogebog/services.py

from cookbook.models import Recipe


def get_recipes():
    # Queries 3 tables: cookbook_recipe, cookbook_ingredient,
    # and cookbook_food.
    return list(Recipe.objects.prefetch_related('ingredient_set__food'))

kogebog/views.py

from django.shortcuts import render
from cookbook.services import get_recipes


def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

Applikationen gengiver også en skabelon med potentielt dyr logik.

<html>
<head>
  <title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
  <h1>{{ recipe.name }}</h1>
    <p>{{ recipe.desc }}</p>
  <h2>Ingredients</h2>
  <ul>
    {% for ingredient in recipe.ingredient_set.all %}
    <li>{{ ingredient.desc }}</li>
    {% endfor %}
  </ul>
  <h2>Instructions</h2>
    <p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>


Implementer cache

Forestil dig det samlede antal netværksopkald, som vores applikation vil foretage, når brugere begynder at besøge vores websted. Hvis 1.000 brugere rammer API'et, der henter kogebogsopskrifter, så vil vores applikation forespørge databasen 3.000 gange, og en ny skabelon vil blive gengivet med hver anmodning. Det tal vokser kun, efterhånden som vores ansøgning skaleres. Heldigvis er denne visning en god kandidat til caching. Opskrifterne i en kogebog ændrer sig sjældent, hvis nogensinde. Da visning af kogebøger er det centrale tema i appen, vil API'en, der henter opskrifterne, garanteret blive kaldt ofte.

I eksemplet nedenfor ændrer vi visningsfunktionen til at bruge caching. Når funktionen kører, tjekker den om view-tasten er i cachen. Hvis nøglen findes, henter appen dataene fra cachen og returnerer dem. Hvis ikke, forespørger Django databasen og gemmer derefter resultatet i cachen med view-tasten. Første gang denne funktion køres, vil Django forespørge i databasen og gengive skabelonen og derefter foretage et netværksopkald til Redis for at gemme dataene i cachen. Hvert efterfølgende kald til funktionen vil helt omgå databasen og forretningslogikken og vil forespørge Redis-cachen.

example/settings.py

# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15

kogebog/views.py

from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes

CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)


@cache_page(CACHE_TTL)
def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

Bemærk, at vi har tilføjet @cache_page() dekoratør til udsigtsfunktionen, sammen med en tid til at leve. Besøg /cookbook/ URL igen og undersøg Django Debug Toolbar. Vi ser, at der foretages 3 databaseforespørgsler og foretages 3 opkald til cachen for at søge efter nøglen og derefter gemme den. Django gemmer to nøgler (1 nøgle til overskriften og 1 nøgle til det gengivede sideindhold). Genindlæs siden og observer, hvordan sideaktiviteten ændrer sig. Anden gang foretages 0 kald til databasen og 2 kald til cachen. Vores side bliver nu serveret fra cachen!

Når vi kører vores ydeevnetest igen, ser vi, at vores applikation indlæses hurtigere.

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

Caching forbedrede den samlede belastning, og vi løser nu 21 anmodninger i sekundet, hvilket er 5 mere end vores baseline:

Requests per second: 21


Inspicering af Redis med CLI

På dette tidspunkt kan vi bruge Redis CLI til at se på, hvad der bliver gemt på Redis-serveren. Indtast keys * på Redis-kommandolinjen kommando, som returnerer alle taster, der matcher ethvert mønster. Du bør se en nøgle kaldet "eksempel:1:visninger.decorators.cache.cache_page". Husk, "eksempel" er vores nøglepræfiks, "1" er versionen, og "views.decorators.cache.cache_page" er det navn, som Django giver nøglen. Kopier nøglenavnet og indtast det med get kommando. Du bør se den gengivede HTML-streng.

$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"

BEMÆRK: Kør flushall kommando på Redis CLI for at rydde alle nøgler fra datalageret. Derefter kan du køre gennem trinene i denne øvelse igen uden at skulle vente på, at cachen udløber.




Afslutning

Det er dyrt at behandle HTTP-anmodninger, og de omkostninger stiger, efterhånden som din applikation vokser i popularitet. I nogle tilfælde kan du i høj grad reducere mængden af ​​behandling, som din server udfører ved at implementere caching. Denne tutorial berørte det grundlæggende i caching i Django med Redis, men det skimmede kun overfladen af ​​et komplekst emne.

Implementering af caching i en robust applikation har mange faldgruber og gotchas. Det er svært at kontrollere, hvad der gemmes i cachen, og hvor længe. Cache-invalidering er en af ​​de svære ting inden for datalogi. At sikre, at private data kun kan tilgås af de tilsigtede brugere, er et sikkerhedsproblem og skal håndteres meget omhyggeligt, når der cachelagres.

Gratis bonus: Klik her for at få adgang til en gratis Django Learning Resources Guide (PDF), der viser dig tips og tricks samt almindelige faldgruber, du skal undgå, når du bygger Python + Django-webapplikationer.

Leg med kildekoden i eksempelapplikationen, og mens du fortsætter med at udvikle med Django, så husk altid at have ydeevne i tankerne.



  1. Hvordan skal jeg oprette forbindelse til en Redis-instans fra en AWS Lambda-funktion?

  2. Sikkerhedskopier en MongoDB-database ved hjælp af mongodump

  3. Er der en måde at "smuk" udskrive MongoDB shell output til en fil?

  4. Redis autofuldførelse