Prestanda och optimering¶
Det här dokumentet ger en översikt över tekniker och verktyg som kan hjälpa dig att få din Django-kod att köras mer effektivt - snabbare och med mindre systemresurser.
Introduktion¶
I allmänhet är det första man gör att skriva kod som fungerar, vars logik fungerar som den ska för att ge den förväntade effekten. Ibland räcker dock inte detta för att få koden att fungera så effektivt som man skulle önska.
I det här fallet behövs det något - och i praktiken ofta en samling saker - för att förbättra kodens prestanda utan att, eller endast minimalt, påverka dess beteende.
Allmänna tillvägagångssätt¶
Vad optimerar du för?¶
Det är viktigt att ha en klar uppfattning om vad man menar med ”prestanda”. Det finns inte bara ett sätt att mäta det.
Förbättrad hastighet kan vara det mest uppenbara målet för ett program, men ibland kan andra prestandaförbättringar eftersträvas, till exempel lägre minnesförbrukning eller färre krav på databasen eller nätverket.
Förbättringar inom ett område leder ofta till förbättrad prestanda inom ett annat, men inte alltid; ibland kan det ena till och med ske på bekostnad av det andra. Till exempel kan en förbättring av ett programs hastighet leda till att det använder mer minne. Ännu värre är att det kan vara självdestruktivt - om hastighetsförbättringen är så minneskrävande att systemet börjar få slut på minne, har du gjort mer skada än nytta.
Det finns andra avvägningar att ta hänsyn till. Din egen tid är en värdefull resurs, mer värdefull än CPU-tid. Vissa förbättringar kan vara för svåra för att vara värda att implementera, eller kan påverka kodens portabilitet eller underhållbarhet. Alla prestandaförbättringar är inte värda ansträngningen.
Du måste alltså veta vilka prestandaförbättringar du siktar på, och du måste också veta att du har en god anledning att sikta i den riktningen - och för det behöver du:
Benchmarking av prestationer¶
Det duger inte att bara gissa eller anta var ineffektiviteten ligger i din kod.
Django verktyg¶
django-debug-toolbar är ett mycket praktiskt verktyg som ger insikter i vad din kod gör och hur mycket tid den lägger på att göra det. I synnerhet kan det visa dig alla SQL-frågor som din sida genererar och hur lång tid var och en av dem har tagit.
Det finns även tredjepartspaneler för verktygsfältet som t.ex. kan rapportera om cacheprestanda och renderingstider för mallar.
Tjänster från tredje part¶
Det finns ett antal kostnadsfria tjänster som analyserar och rapporterar om prestandan för sidorna på din webbplats utifrån en HTTP-fjärrklients perspektiv, vilket i praktiken simulerar en faktisk användares upplevelse.
Dessa kan inte rapportera om det interna i din kod, men kan ge en användbar inblick i webbplatsens övergripande prestanda, inklusive aspekter som inte kan mätas på ett adekvat sätt från Django-miljön.
Det finns också flera betaltjänster som utför en liknande analys, inklusive några som är Django-medvetna och kan integreras med din kodbas för att profilera dess prestanda mycket mer omfattande.
Gör rätt från början¶
En del av optimeringsarbetet handlar om att ta itu med prestandabrister, men en del av arbetet kan vara inbyggt i vad du skulle göra ändå, som en del av de goda rutiner du bör använda redan innan du börjar tänka på att förbättra prestandan.
I det avseendet är Python ett utmärkt språk att arbeta med, eftersom lösningar som ser eleganta ut och känns rätt oftast är de som ger bäst resultat. Som med de flesta färdigheter krävs det övning för att lära sig vad som ”ser rätt ut”, men en av de mest användbara riktlinjerna är:
Arbeta på lämplig nivå¶
Django erbjuder många olika sätt att närma sig saker, men bara för att det är möjligt att göra något på ett visst sätt betyder det inte att det är det mest lämpliga sättet att göra det på. Till exempel kanske du upptäcker att du kan beräkna samma sak - antalet objekt i en samling, kanske - i en QuerySet
, i Python eller i en mall.
Det kommer dock nästan alltid att gå snabbare att göra detta arbete på lägre nivåer än på högre. På högre nivåer måste systemet hantera objekt genom flera abstraktionsnivåer och lager av maskiner.
Det vill säga, databasen kan vanligtvis göra saker snabbare än Python kan, som kan göra dem snabbare än mallspråket kan:
# QuerySet operation on the database
# fast, because that's what databases are good at
my_bicycles.count()
# counting Python objects
# slower, because it requires a database query anyway, and processing
# of the Python objects
len(my_bicycles)
<!--
Django template filter
slower still, because it will have to count them in Python anyway,
and because of template language overheads
-->
{{ my_bicycles|length }}
Generellt sett är den mest lämpliga nivån för jobbet den lägsta nivå som det är bekvämt att koda för.
Observera
Exemplet ovan är endast illustrativt.
För det första, i ett verkligt fall måste du överväga vad som händer före och efter din räkning för att ta reda på vad som är ett optimalt sätt att göra det * i det specifika sammanhanget *. Dokumentet om databasoptimering beskriver ett fall där det skulle vara bättre att räkna i mallen.
För det andra finns det andra alternativ att överväga: i ett verkligt fall kan {{ my_bicycles.count }}
, som anropar QuerySet
count()
-metoden direkt från mallen, vara det lämpligaste valet.
Cachning¶
Ofta är det dyrt (det vill säga resurskrävande och långsamt) att beräkna ett värde, så det kan finnas stora fördelar med att spara värdet i en snabbt tillgänglig cache, redo för nästa gång det behövs.
Det är en tillräckligt viktig och kraftfull teknik för att Django innehåller ett omfattande ramverk för cachelagring samt andra mindre delar av cachelagringsfunktionalitet.
Ramverket för cachelagring¶
Djangos caching-ramverk erbjuder mycket betydande möjligheter till prestandavinster genom att spara dynamiskt innehåll så att det inte behöver beräknas för varje begäran.
För enkelhetens skull erbjuder Django olika nivåer av cachegranularitet: du kan cacha utdata från specifika vyer, eller bara de delar som är svåra att producera, eller till och med en hel webbplats.
Implementering av cachelagring ska inte ses som ett alternativ till att förbättra kod som fungerar dåligt på grund av att den är dåligt skriven. Det är ett av de sista stegen mot att producera välfungerande kod, inte en genväg.
cached_property
¶
Det är vanligt att man måste anropa en klassinstans metod mer än en gång. Om den funktionen är dyr kan det vara slöseri att göra det.
Genom att använda cached_property
-dekoratorn sparas det värde som returneras av en egenskap; nästa gång funktionen anropas på den instansen kommer den att returnera det sparade värdet istället för att beräkna det på nytt. Observera att detta endast fungerar på metoder som tar self
som sitt enda argument och att det ändrar metoden till en egenskap.
Vissa Django-komponenter har också sin egen cachningsfunktionalitet; dessa diskuteras nedan i de avsnitt som rör dessa komponenter.
Att förstå lättja¶
Lathet är en strategi som kompletterar cachelagring. Cachelagring undviker omräkning genom att spara resultat; lathet fördröjer beräkningen tills den faktiskt krävs.
Lathet gör att vi kan hänvisa till saker innan de är instansierade, eller till och med innan det är möjligt att instansiera dem. Detta har många användningsområden.
Till exempel kan :ref:``lazy translation <lazy-translations>` användas innan målspråket ens är känt, eftersom det inte sker förrän den översatta strängen faktiskt behövs, till exempel i en renderad mall.
Lathet är också ett sätt att spara kraft genom att försöka undvika arbete från första början. Det vill säga, en aspekt av lathet är att inte göra något förrän det måste göras, eftersom det kanske inte visar sig vara nödvändigt trots allt. Lättja kan därför ha konsekvenser för prestationen, och ju dyrare arbete det handlar om, desto mer finns det att vinna på lättja.
Python tillhandahåller ett antal verktyg för latent utvärdering, särskilt genom konstruktionerna generator och generator expression. Det är värt att läsa på om lathet i Python för att upptäcka möjligheter att använda lata mönster i din kod.
Lättja i Django¶
Django är i sig själv ganska lat. Ett bra exempel på detta kan hittas i utvärderingen av en QuerySet
. QuerySets are lazy. Således kan en QuerySet
skapas, skickas runt och kombineras med andra QuerySet
-instanser, utan att faktiskt göra några resor till databasen för att hämta de objekt som den beskriver. Det som skickas runt är QuerySet
-objektet, inte samlingen av objekt som - så småningom - kommer att krävas från databasen.
Å andra sidan kommer vissa operationer att tvinga fram en utvärdering av en QuerySet. Om du undviker att utvärdera en QuerySet
i förtid kan du spara en dyr och onödig resa till databasen.
Django erbjuder också en keep_lazy()
dekorator. Detta gör att en funktion som har anropats med ett latent argument kan bete sig latent själv och bara utvärderas när det behövs. Således kommer det lata argumentet - som kan vara ett dyrt argument - inte att anropas för utvärdering förrän det är absolut nödvändigt.
Databaser¶
Databasoptimering¶
Djangos databaslager tillhandahåller olika sätt att hjälpa utvecklare att få bästa möjliga prestanda från sina databaser. Dokumentet Dokumentation om databasoptimering samlar länkar till relevant dokumentation och lägger till olika tips som beskriver de steg som ska tas när du försöker optimera din databasanvändning.
HTTP-prestanda¶
Middleware¶
Django levereras med några användbara delar av middleware som kan hjälpa till att optimera din webbplats prestanda. De inkluderar:
:klass:`~django.middleware.http.ConditionalGetMiddleware`¶
Lägger till stöd för moderna webbläsare för att villkorligt GET-svar baserat på rubrikerna ETag
och Last-Modified
. Det beräknar och ställer också in en ETag om det behövs.
:klass:`~django.middleware.gzip.GZipMiddleware`¶
Komprimerar svar för alla moderna webbläsare, vilket sparar bandbredd och överföringstid. Observera att GZipMiddleware för närvarande anses vara en säkerhetsrisk och är sårbar för attacker som upphäver det skydd som TLS/SSL ger. Se varningen i GZipMiddleware
för mer information.
Sessioner¶
Använda cachade sessioner¶
:ref:``Using cached sessions <cached-sessions-backend>` kan vara ett sätt att öka prestandan genom att eliminera behovet av att ladda sessionsdata från en långsammare lagringskälla som databasen och istället lagra ofta använda sessionsdata i minnet.
Statiska filer¶
Statiska filer, som per definition inte är dynamiska, är ett utmärkt mål för optimeringsvinster.
ManifestStaticFilesStorage
¶
Genom att utnyttja webbläsarnas cachelagringsfunktioner kan du helt eliminera nätverksträffar för en viss fil efter den första nedladdningen.
ManifestStaticFilesStorage
lägger till en innehållsberoende tagg till filnamnen på statiska filer för att göra det säkert för webbläsare att cachelagra dem på lång sikt utan att missa framtida ändringar - när en fil ändras, så kommer taggen också att göra det, så webbläsare laddar om tillgången automatiskt.
”Minifiering”¶
Flera Django-verktyg och -paket från tredje part ger möjlighet att ”minifiera” HTML, CSS och JavaScript. De tar bort onödiga blanksteg, nya linjer och kommentarer samt förkortar variabelnamn och minskar därmed storleken på de dokument som din webbplats publicerar.
Mall för prestanda¶
Observera detta:
att använda
{% block %}
är snabbare än att använda{% include %}
kraftigt fragmenterade mallar, sammansatta av många små bitar, kan påverka prestandan
Den cachade mallladdaren¶
Att aktivera cached template loader
förbättrar ofta prestandan drastiskt, eftersom det undviker att kompilera varje mall varje gång den behöver återges.
Använda olika versioner av tillgänglig programvara¶
Ibland kan det vara värt att kontrollera om det finns andra och bättre versioner av den programvara som du använder.
Dessa tekniker riktar sig till mer avancerade användare som vill tänja på gränserna för prestanda för en redan väloptimerad Django-webbplats.
De är dock inga magiska lösningar på prestandaproblem, och det är osannolikt att de kommer att ge mer än marginella vinster för webbplatser som inte redan gör de mer grundläggande sakerna på rätt sätt.
Observera
Det är värt att upprepa: att leta efter alternativ till programvara som du redan använder är aldrig det första svaret på prestandaproblem. När du når den här optimeringsnivån behöver du en formell benchmarkinglösning.
Nyare är ofta - men inte alltid - bättre¶
Det är ganska ovanligt att en ny version av en väl underhållen programvara är mindre effektiv, men underhållarna kan inte förutse alla möjliga användningsfall - så även om du är medveten om att nyare versioner sannolikt kommer att fungera bättre, ska du inte anta att de alltid kommer att göra det.
Detta gäller för Django självt. Successiva utgåvor har erbjudit ett antal förbättringar i hela systemet, men du bör fortfarande kontrollera den verkliga prestandan för din applikation, eftersom du i vissa fall kan upptäcka att förändringar innebär att den fungerar sämre snarare än bättre.
Nyare versioner av Python, och även av Python-paket, kommer ofta att fungera bättre också - men mät, snarare än anta.
Observera
Såvida du inte har stött på ett ovanligt prestandaproblem i en viss version, kommer du i allmänhet att hitta bättre funktioner, tillförlitlighet och säkerhet i en ny version och att dessa fördelar är mycket viktigare än någon prestanda du kan vinna eller förlora.
Alternativ till Djangos mallspråk¶
I nästan alla fall är Djangos inbyggda mallspråk helt adekvat. Men om flaskhalsarna i ditt Django-projekt verkar ligga i mallsystemet och du har uttömt andra möjligheter att åtgärda detta, kan ett tredjepartsalternativ vara svaret.
Jinja2 kan erbjuda prestandaförbättringar, särskilt när det gäller hastighet.
Alternativa mallsystem varierar i vilken utsträckning de delar Djangos mallspråk.
Observera
Om du upplever prestandaproblem i templates är det första du bör göra att förstå exakt varför. Det kan visa sig att det går snabbare att använda ett alternativt mallsystem, men samma vinster kan också uppnås utan att man behöver göra sig besväret - till exempel kan dyr bearbetning och logik i dina mallar göras mer effektivt i dina vyer.
Alternativa implementeringar av programvara¶
Det kan vara värt att kontrollera om Python-programvaran du använder har tillhandahållits i en annan implementering som kan köra samma kod snabbare.
Men: de flesta prestandaproblem i välskrivna Django-webbplatser ligger inte på Python-körningsnivån, utan snarare i ineffektiva databasfrågor, cachning och mallar. Om du förlitar dig på dåligt skriven Python-kod är det osannolikt att dina prestandaproblem kommer att lösas genom att den körs snabbare.
Att använda en alternativ implementering kan medföra problem med kompatibilitet, distribution, portabilitet eller underhåll. Det säger sig självt att innan du använder en icke-standardiserad implementering bör du säkerställa att den ger tillräckliga prestandavinster för din applikation för att uppväga de potentiella riskerna.
Med dessa försiktighetsåtgärder i åtanke bör du vara medveten om:
PyPy¶
PyPy är en implementering av Python i Python själv (den ”vanliga” Python-implementeringen är i C). PyPy kan erbjuda betydande prestandavinster, vanligtvis för tunga applikationer.
Ett viktigt mål för PyPy-projektet är kompatibilitet med befintliga Python API:er och bibliotek. Django är kompatibelt, men du måste kontrollera kompatibiliteten för andra bibliotek som du förlitar dig på.
C-implementeringar av Python-bibliotek¶
Vissa Python-bibliotek är också implementerade i C och kan vara mycket snabbare. De strävar efter att erbjuda samma API:er. Observera att kompatibilitetsproblem och beteendeskillnader inte är okända (och inte alltid omedelbart uppenbara).