# Django 6.0.5 documentation

# Optimering av databasåtkomst

Djangos databaslager tillhandahåller olika sätt att hjälpa utvecklare att få ut mesta möjliga av sina databaser. Detta dokument samlar länkar till relevant dokumentation och lägger till olika tips, organiserade under ett antal rubriker som beskriver de steg som ska vidtas när du försöker optimera din databasanvändning.

## Profil först

Som allmän programmeringspraxis säger detta sig självt. Ta reda på vilka frågor du gör och vad de kostar dig. Använd `QuerySet.explain()` för att förstå hur specifika `QuerySet` exekveras av din databas. Du kanske också vill använda ett externt projekt som django-debug-toolbar, eller ett verktyg som övervakar din databas direkt.

Kom ihåg att du kan optimera för hastighet eller minne eller båda, beroende på dina krav. Ibland kan optimering för det ena vara till nackdel för det andra, men ibland kan de hjälpa varandra. Det arbete som utförs av databasprocessen kanske inte heller har samma kostnad (för dig) som samma mängd arbete som utförs i din Python-process. Det är upp till dig att bestämma vilka dina prioriteringar är, var balansen måste ligga och profilera alla dessa efter behov eftersom detta beror på din applikation och server.

Med allt som följer, kom ihåg att profilera efter varje ändring för att säkerställa att ändringen är en fördel, och en tillräckligt stor fördel med tanke på minskningen av läsbarheten i din kod. Alla förslagen nedan kommer med förbehållet att den allmänna principen kanske inte gäller under dina omständigheter, eller kanske till och med är omvänd.

## Använda standardtekniker för DB-optimering

…inklusive..:

- Indexes. This is a number one priority, after you have determined from profiling what indexes should be added. Use `Meta.indexes` or `Field.db_index` to add these from Django. Consider adding indexes to fields that you frequently query using `filter()`, `exclude()`, `order_by()`, etc. as indexes may help to speed up lookups. Note that determining the best indexes is a complex database-dependent topic that will depend on your particular application. The overhead of maintaining an index may outweigh any gains in query speed.

- Lämplig användning av fälttyper.

Vi kommer att anta att du har gjort de saker som anges ovan. Resten av detta dokument fokuserar på hur man använder Django på ett sådant sätt att man inte gör onödigt arbete. Detta dokument tar inte heller upp andra optimeringstekniker som gäller för alla dyra operationer, till exempel general purpose caching.

## Förstå `QuerySet`

Att förstå QuerySets är avgörande för att få bra prestanda med enkel kod. I synnerhet:

### Förstå utvärdering av `QuerySet`

För att undvika prestandaproblem är det viktigt att förstå:

- att QuerySets är lata.

- när de utvärderas.

- hur data lagras i minnet.

### Förstå cachelagrade attribut

Förutom cachelagring av hela `QuerySet` finns det cachelagring av resultatet av attribut på ORM-objekt. I allmänhet kommer attribut som inte är anropsbara att cachelagras. Om vi till exempel antar att example blog models:

    >>> entry = Entry.objects.get(id=1)
    >>> entry.blog  # Blog object is retrieved at this point
    >>> entry.blog  # cached version, no DB access

Men i allmänhet orsakar anropsbara attribut DB-uppslagningar varje gång:

    >>> entry = Entry.objects.get(id=1)
    >>> entry.authors.all()  # query performed
    >>> entry.authors.all()  # query performed again

Var försiktig när du läser mallkod - mallsystemet tillåter inte användning av parenteser, men kommer att kalla callables automatiskt, vilket döljer ovanstående distinktion.

Var försiktig med dina egna anpassade egenskaper - det är upp till dig att implementera caching när det behövs, till exempel med hjälp av `cached_property`-dekoratorn.

### Använd malltaggen `with`

För att utnyttja cachelagringsbeteendet hos `QuerySet` kan du behöva använda malltaggen `with`.

### Använd `iterator()`

When you have a lot of objects, the caching behavior of the `QuerySet` can cause a large amount of memory to be used. In this case, `iterator()` may help.

### Använd `explain()`

`QuerySet.explain()` ger dig detaljerad information om hur databasen exekverar en fråga, inklusive index och sammanfogningar som används. Dessa detaljer kan hjälpa dig att hitta frågor som kan skrivas om på ett effektivare sätt eller identifiera index som kan läggas till för att förbättra prestandan.

## Gör databasarbete i databasen snarare än i Python

Till exempel:

- På den mest grundläggande nivån använder du filter och exkludera för att göra filtrering i databasen.

- Använd `F expressions` för att filtrera baserat på andra fält inom samma modell.

- Använd annotate för att göra aggregering i databasen.

Om dessa inte räcker för att generera den SQL du behöver:

### Använd `RawSQL`

En mindre portabel men mer kraftfull metod är `RawSQL`-uttrycket, som gör att viss SQL uttryckligen kan läggas till i frågan. Om det fortfarande inte är tillräckligt kraftfullt:

### Använd rå SQL

Skriv din egen custom SQL för att hämta data eller fylla i modeller. Använd `django.db.connection.queries` för att ta reda på vad Django skriver åt dig och börja därifrån.

## Hämta enskilda objekt med hjälp av en unik, indexerad kolumn

Det finns två skäl att använda en kolumn med `unique` eller `db_index` när man använder `get()` för att hämta enskilda objekt. För det första kommer frågan att vara snabbare på grund av det underliggande databasindexet. Dessutom kan frågan köras mycket långsammare om flera objekt matchar uppslagningen; att ha en unik begränsning på kolumnen garanterar att detta aldrig kommer att hända.

Så med hjälp av exempel på bloggmodeller:

    >>> entry = Entry.objects.get(id=10)

kommer att vara snabbare än:

    >>> entry = Entry.objects.get(headline="News Item Title")

eftersom `id` indexeras av databasen och garanterat är unik.

Att göra följande är potentiellt ganska långsamt:

    >>> entry = Entry.objects.get(headline__startswith="News")

Först och främst är `headline` inte indexerad, vilket gör att den underliggande databashämtningen blir långsammare.

För det andra garanterar inte uppslagningen att endast ett objekt returneras. Om frågan matchar mer än ett objekt kommer alla objekt att hämtas och överföras från databasen. Denna straffavgift kan bli betydande om hundratals eller tusentals poster returneras. Straffet blir ännu större om databasen finns på en separat server, där nätverksoverhead och latens också spelar en roll.

## Hämta allt på en gång om du vet att du kommer att behöva det

Att göra flera sökningar i databasen efter olika delar av en enda ”uppsättning” data som du behöver alla delar av är i allmänhet mindre effektivt än att hämta allt i en enda fråga. Detta är särskilt viktigt om du har en fråga som körs i en slinga och därför kan komma att göra många databasfrågor när det bara behövs en. Så här är det:

### Använd `QuerySet.select_related()` och `prefetch_related()`

Förstå `select_related()` och `prefetch_related()` noggrant och använd dem:

- i hanterare och standardhanterare där så är lämpligt. Var medveten om när din manager används och inte används; ibland är detta knepigt så gör inga antaganden.

- i visningskod eller andra lager, eventuellt med användning av `prefetch_related_objects()` där det behövs.

## Hämta inte saker som du inte behöver

### Använd `QuerySet.values()` och `values_list()`

When you only want a `dict` or `list` of values, and don’t need ORM model objects, make appropriate usage of `values()`. These can be useful for replacing model objects in template code - as long as the dicts you supply have the same attributes as those used in the template, you are fine.

### Använd `QuerySet.defer()` och `only()`

Use `defer()` and `only()` if there are database columns you know that you won’t need (or won’t need in most cases) to avoid loading them. Note that if you do use them, the ORM will have to go and get them in a separate query, making this a pessimization if you use it inappropriately.

Var inte för aggressiv när det gäller att skjuta upp fält utan profilering eftersom databasen måste läsa det mesta av icke-text, icke-`VARCHAR`-data från disken för en enda rad i resultaten, även om det slutar med att bara använda några få kolumner. Metoderna `defer()` och `only()` är mest användbara när du kan undvika att ladda en massa textdata eller för fält som kan kräva mycket bearbetning för att konvertera tillbaka till Python. Som alltid, profilera först, optimera sedan.

### Använd `QuerySet.contains(obj)`

…om du bara vill ta reda på om `obj` finns i frågeuppsättningen, snarare än `om obj i frågeuppsättningen`.

### Använd `QuerySet.count()`

…om du bara vill ha antalet, istället för att göra `len(queryset)`.

### Använd `QuerySet.exists()`

…om du bara vill ta reda på om minst ett resultat finns, snarare än `if queryset`.

Men..:

### Överanvänd inte `contains()`, `count()` och `exists()`

Om du kommer att behöva andra data från QuerySet ska du utvärdera dem omedelbart.

Om vi till exempel antar att en `Group`-modell har en många-till-många-relation till `User`, är följande kod optimal:

    members = group.members.all()

    if display_group_members:
        if members:
            if current_user in members:
                print("You and", len(members) - 1, "other users are members of this group.")
            else:
                print("There are", len(members), "members in this group.")

            for member in members:
                print(member.username)
        else:
            print("There are no members in this group.")

Det är optimalt eftersom:

1. Eftersom QuerySets är lata görs inga databasfrågor om `display_group_members` är `False`.

2. Genom att lagra `group.members.all()` i variabeln `members` kan dess resultatcache återanvändas.

3. Raden `if members:` gör att `QuerySet.__bool__()` anropas, vilket gör att `group.members.all()`-frågan körs på databasen. Om det inte finns några resultat returneras `False`, annars `True`.

4. Raden `if current_user in members:` kontrollerar om användaren finns i resultatcachen, så att inga ytterligare databasfrågor ställs.

5. Användningen av `len(members)` anropar `QuerySet.__len__()` och återanvänder resultatcachen, så inte heller här ställs några databasfrågor.

6. Slingan `for member` itererar över resultatcachen.

Totalt gör denna kod antingen en eller noll databasförfrågningar. Den enda avsiktliga optimering som gjorts är att använda variabeln `members`. Att använda `QuerySet.exists()` för `if`, `QuerySet.contains()` för `in` eller `QuerySet.count()` för count skulle var och en orsaka ytterligare frågor.

### Använd `QuerySet.update()` och `delete()`

I stället för att hämta en mängd objekt, ställa in några värden och spara dem individuellt, använd en bulk SQL UPDATE-sats via QuerySet.update(). Gör på samma sätt bulk deletes när det är möjligt.

Observera dock att dessa metoder för massuppdatering inte kan anropa metoderna `save()` eller `delete()` för enskilda instanser, vilket innebär att eventuella anpassade beteenden som du har lagt till för dessa metoder inte kommer att utföras, inklusive allt som drivs från det normala databasobjektet signals.

### Använd värden för främmande nycklar direkt

Om du bara behöver ett främmande nyckelvärde, använd det främmande nyckelvärde som redan finns på det objekt du har, i stället för att hämta hela det relaterade objektet och ta dess primära nyckel. dvs. gör:

    entry.blog_id

istället för:

    entry.blog.id

### Beställ inte resultat om du inte bryr dig

Ordering is not free; each field to order by is an operation the database must perform. If a model has a default ordering (`Meta.ordering`) and you don’t need it, remove it on a `QuerySet` by calling `order_by()` with no parameters.

Om du lägger till ett index i din databas kan det bidra till att förbättra beställningsresultatet.

## Använd bulkmetoder

Använd bulkmetoder för att minska antalet SQL-satser.

### Skapa i bulk

When creating objects, where possible, use the `bulk_create()` method to reduce the number of SQL queries. For example:

    Entry.objects.bulk_create(
        [
            Entry(headline="This is a test"),
            Entry(headline="This is only a test"),
        ]
    )

…är att föredra framför:

    Entry.objects.create(headline="This is a test")
    Entry.objects.create(headline="This is only a test")

Observera att det finns ett antal `försiktighetsåtgärder för denna metod`, så se till att den är lämplig för ditt användningsfall.

### Uppdatering i bulk

When updating objects, where possible, use the `bulk_update()` method to reduce the number of SQL queries. Given a list or queryset of objects:

    entries = Entry.objects.bulk_create(
        [
            Entry(headline="This is a test"),
            Entry(headline="This is only a test"),
        ]
    )

Följande exempel:

    entries[0].headline = "This is not a test"
    entries[1].headline = "This is no longer a test"
    Entry.objects.bulk_update(entries, ["headline"])

…är att föredra framför:

    entries[0].headline = "This is not a test"
    entries[0].save()
    entries[1].headline = "This is no longer a test"
    entries[1].save()

Observera att det finns ett antal `avrådan för denna metod`, så se till att den är lämplig för ditt användningsfall.

### Insättning i bulk

När du infogar objekt i `ManyToManyFields`, använd `add()` med flera objekt för att minska antalet SQL-frågor. Till exempel:

    my_band.members.add(me, my_friend)

…är att föredra framför:

    my_band.members.add(me)
    my_band.members.add(my_friend)

…där `Band` och `Artist` är modeller med ett många-till-många-förhållande.

When inserting different pairs of objects into `ManyToManyField` or when the custom `through` table is defined, use `bulk_create()` method to reduce the number of SQL queries. For example:

    PizzaToppingRelationship = Pizza.toppings.through
    PizzaToppingRelationship.objects.bulk_create(
        [
            PizzaToppingRelationship(pizza=my_pizza, topping=pepperoni),
            PizzaToppingRelationship(pizza=your_pizza, topping=pepperoni),
            PizzaToppingRelationship(pizza=your_pizza, topping=mushroom),
        ],
        ignore_conflicts=True,
    )

…är att föredra framför:

    my_pizza.toppings.add(pepperoni)
    your_pizza.toppings.add(pepperoni, mushroom)

…där `Pizza` och `Topping` har en många-till-många-relation. Observera att det finns ett antal `avrådan för denna metod`, så se till att den är lämplig för ditt användningsfall.

### Ta bort i bulk

När du tar bort objekt från `ManyToManyFields`, använd `remove()` med flera objekt för att minska antalet SQL-frågor. Till exempel:

    my_band.members.remove(me, my_friend)

…är att föredra framför:

    my_band.members.remove(me)
    my_band.members.remove(my_friend)

…där `Band` och `Artist` är modeller med ett många-till-många-förhållande.

När du tar bort olika par objekt från `ManyToManyFields`, använd `delete()` på ett `Q` uttryck med flera `through` modellinstanser för att minska antalet SQL-frågor. Till exempel:

    from django.db.models import Q

    PizzaToppingRelationship = Pizza.toppings.through
    PizzaToppingRelationship.objects.filter(
        Q(pizza=my_pizza, topping=pepperoni)
        | Q(pizza=your_pizza, topping=pepperoni)
        | Q(pizza=your_pizza, topping=mushroom)
    ).delete()

…är att föredra framför:

    my_pizza.toppings.remove(pepperoni)
    your_pizza.toppings.remove(pepperoni, mushroom)

…där `Pizza` och `Topping` har ett många-till-många-förhållande.