Optimizējiet Django datubāzes vaicājumus ar select_related un prefetch_related, lai uzlabotu veiktspēju. Apgūstiet praktiskus piemērus un labākās prakses.
Django ORM vaicājumu optimizācija: select_related vs. prefetch_related
Jūsu Django lietojumprogrammai augot, efektīvi datubāzes vaicājumi kļūst izšķiroši svarīgi, lai uzturētu optimālu veiktspēju. Django ORM nodrošina jaudīgus rīkus, lai samazinātu datubāzes pieprasījumu skaitu un uzlabotu vaicājumu ātrumu. Divas galvenās metodes, kā to panākt, ir select_related un prefetch_related. Šī visaptverošā rokasgrāmata izskaidros šos jēdzienus, demonstrēs to lietošanu ar praktiskiem piemēriem un palīdzēs jums izvēlēties pareizo rīku jūsu konkrētajām vajadzībām.
Izpratne par N+1 problēmu
Pirms iedziļināties select_related un prefetch_related, ir būtiski saprast problēmu, ko tās risina: N+1 vaicājumu problēmu. Tā rodas, kad jūsu lietojumprogramma izpilda vienu sākotnējo vaicājumu, lai iegūtu objektu kopu, un pēc tam veic papildu vaicājumus (N vaicājumus, kur N ir objektu skaits), lai iegūtu saistītos datus katram objektam.
Apskatīsim vienkāršu piemēru ar modeļiem, kas attēlo autorus un grāmatas:
class Author(models.Model):
name = models.CharField(max_length=255)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
Tagad iedomājieties, ka vēlaties attēlot grāmatu sarakstu ar to attiecīgajiem autoriem. Naivs risinājums varētu izskatīties šādi:
books = Book.objects.all()
for book in books:
print(f"{book.title} by {book.author.name}")
Šis kods ģenerēs vienu vaicājumu, lai iegūtu visas grāmatas, un pēc tam vienu vaicājumu katrai grāmatai, lai iegūtu tās autoru. Ja jums ir 100 grāmatas, jūs izpildīsiet 101 vaicājumu, kas rada ievērojamu veiktspējas slogu. Tā ir N+1 problēma.
Iepazīstinām ar select_related
select_related tiek izmantots, lai optimizētu vaicājumus, kas ietver viens pret vienu (one-to-one) un ārējās atslēgas (foreign key) attiecības. Tas darbojas, pievienojot saistītās tabulas sākotnējam vaicājumam ar JOIN, tādējādi efektīvi iegūstot saistītos datus vienā datubāzes pieprasījumā.
Atgriezīsimies pie mūsu autoru un grāmatu piemēra. Lai novērstu N+1 problēmu, mēs varam izmantot select_related šādi:
books = Book.objects.all().select_related('author')
for book in books:
print(f"{book.title} by {book.author.name}")
Tagad Django izpildīs vienu, sarežģītāku vaicājumu, kas apvieno Book un Author tabulas. Kad jūs ciklā piekļūstat book.author.name, dati jau ir pieejami, un netiek veikti papildu datubāzes vaicājumi.
select_related izmantošana ar vairākām attiecībām
select_related var šķērsot vairākas attiecības. Piemēram, ja jums ir modelis ar ārējo atslēgu uz citu modeli, kuram savukārt ir ārējā atslēga uz vēl citu modeli, jūs varat izmantot select_related, lai iegūtu visus saistītos datus vienā piegājienā.
class Country(models.Model):
name = models.CharField(max_length=255)
class AuthorProfile(models.Model):
author = models.OneToOneField(Author, on_delete=models.CASCADE)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
# Add country to Author
Author.profile = models.OneToOneField(AuthorProfile, on_delete=models.CASCADE, null=True, blank=True)
authors = Author.objects.all().select_related('profile__country')
for author in authors:
print(f"{author.name} is from {author.profile.country.name if author.profile else 'Unknown'}")
Šajā gadījumā select_related('profile__country') iegūst AuthorProfile un saistīto Country vienā vaicājumā. Ievērojiet dubultās pasvītras (__) notāciju, kas ļauj jums šķērsot attiecību koku.
select_related ierobežojumi
select_related ir visefektīvākais ar "viens pret vienu" un ārējās atslēgas attiecībām. Tas nav piemērots "daudzi pret daudziem" (many-to-many) attiecībām vai apgrieztām ārējās atslēgas attiecībām, jo tas var novest pie lieliem un neefektīviem vaicājumiem, strādājot ar lielām saistīto datu kopām. Šādos scenārijos prefetch_related ir labāka izvēle.
Iepazīstinām ar prefetch_related
prefetch_related ir paredzēts, lai optimizētu vaicājumus, kas ietver daudzi pret daudziem (many-to-many) un apgrieztās ārējās atslēgas (reverse foreign key) attiecības. Tā vietā, lai izmantotu JOIN, prefetch_related veic atsevišķus vaicājumus katrai attiecībai un pēc tam izmanto Python, lai "savienotu" rezultātus. Lai gan tas ietver vairākus vaicājumus, tas var būt efektīvāk nekā izmantot JOIN, strādājot ar lielām saistīto datu kopām.
Apskatīsim scenāriju, kur katrai grāmatai var būt vairāki žanri:
class Genre(models.Model):
name = models.CharField(max_length=255)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
genres = models.ManyToManyField(Genre)
Lai iegūtu grāmatu sarakstu ar to žanriem, select_related izmantošana nebūtu piemērota. Tā vietā mēs izmantojam prefetch_related:
books = Book.objects.all().prefetch_related('genres')
for book in books:
genre_names = [genre.name for genre in book.genres.all()]
print(f"{book.title} ({', '.join(genre_names)}) by {book.author.name}")
Šajā gadījumā Django izpildīs divus vaicājumus: vienu, lai iegūtu visas grāmatas, un otru, lai iegūtu visus žanrus, kas saistīti ar šīm grāmatām. Pēc tam tas izmanto Python, lai efektīvi saistītu žanrus ar to attiecīgajām grāmatām.
prefetch_related ar apgrieztām ārējām atslēgām
prefetch_related ir noderīgs arī apgriezto ārējo atslēgu attiecību optimizēšanai. Apskatīsim šādu piemēru:
class Author(models.Model):
name = models.CharField(max_length=255)
country = models.CharField(max_length=255, blank=True, null=True) # Added for clarity
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)
Lai iegūtu autoru sarakstu un viņu grāmatas:
authors = Author.objects.all().prefetch_related('books')
for author in authors:
book_titles = [book.title for book in author.books.all()]
print(f"{author.name} has written: {', '.join(book_titles)}")
Šeit prefetch_related('books') iegūst visas grāmatas, kas saistītas ar katru autoru, atsevišķā vaicājumā, izvairoties no N+1 problēmas, piekļūstot author.books.all().
prefetch_related izmantošana ar vaicājumu kopu (queryset)
Jūs varat vēl vairāk pielāgot prefetch_related darbību, nodrošinot pielāgotu vaicājumu kopu (queryset), lai iegūtu saistītos objektus. Tas ir īpaši noderīgi, ja nepieciešams filtrēt vai kārtot saistītos datus.
from django.db.models import Prefetch
authors = Author.objects.prefetch_related(Prefetch('books', queryset=Book.objects.filter(title__icontains='django')))
for author in authors:
django_books = author.books.all()
print(f"{author.name} has written {len(django_books)} books about Django.")
Šajā piemērā Prefetch objekts ļauj mums norādīt pielāgotu vaicājumu kopu, kas iegūst tikai tās grāmatas, kuru nosaukumos ir vārds "django".
prefetch_related ķēdēšana
Līdzīgi kā ar select_related, jūs varat savirknēt prefetch_related izsaukumus, lai optimizētu vairākas attiecības:
authors = Author.objects.all().prefetch_related('books__genres')
for author in authors:
for book in author.books.all():
genres = book.genres.all()
print(f"{author.name} wrote {book.title} which is of genre(s) {[genre.name for genre in genres]}")
Šis piemērs iepriekš ielādē grāmatas, kas saistītas ar autoru, un pēc tam žanrus, kas saistīti ar šīm grāmatām. Izmantojot ķēdētu prefetch_related, jūs varat optimizēt dziļi ligzdotas attiecības.
select_related pret prefetch_related: Pareizā rīka izvēle
Tātad, kad jums vajadzētu izmantot select_related un kad prefetch_related? Šeit ir vienkārša vadlīnija:
select_related: Izmantojiet "viens pret vienu" un ārējās atslēgas attiecībām, kur bieži nepieciešams piekļūt saistītajiem datiem. Tas veic JOIN datubāzē, tāpēc tas parasti ir ātrāk, lai iegūtu nelielu daudzumu saistīto datu.prefetch_related: Izmantojiet "daudzi pret daudziem" un apgrieztām ārējās atslēgas attiecībām, vai strādājot ar lielām saistīto datu kopām. Tas veic atsevišķus vaicājumus un izmanto Python, lai savienotu rezultātus, kas var būt efektīvāk nekā lieli JOIN vaicājumi. Izmantojiet arī tad, ja nepieciešams pielietot pielāgotu vaicājumu kopas filtrēšanu saistītajiem objektiem.
Kopsavilkumā:
- Attiecību veids:
select_related(ForeignKey, OneToOne),prefetch_related(ManyToManyField, apgrieztā ForeignKey) - Vaicājuma veids:
select_related(JOIN),prefetch_related(Atsevišķi vaicājumi + Python savienošana) - Datu apjoms:
select_related(Mazi saistītie dati),prefetch_related(Lieli saistītie dati)
Praktiski piemēri un labākā prakse
Šeit ir daži praktiski piemēri un labākās prakses, kā izmantot select_related un prefetch_related reālās dzīves scenārijos:
- E-komercija: Rādod produkta detaļas, izmantojiet
select_related, lai iegūtu produkta kategoriju un ražotāju. Izmantojietprefetch_related, lai iegūtu produktu attēlus vai saistītos produktus. - Sociālie mediji: Rādod lietotāja profilu, izmantojiet
prefetch_related, lai iegūtu lietotāja ierakstus un sekotājus. Izmantojietselect_related, lai iegūtu lietotāja profila informāciju. - Satura pārvaldības sistēma (CMS): Rādod rakstu, izmantojiet
select_related, lai iegūtu autoru un kategoriju. Izmantojietprefetch_related, lai iegūtu raksta birkas un komentārus.
Vispārējā labākā prakse:
- Profilējiet savus vaicājumus: Izmantojiet Django debug toolbar vai citus profilēšanas rīkus, lai identificētu lēnus vaicājumus un potenciālās N+1 problēmas.
- Sāciet vienkārši: Sāciet ar naivu implementāciju un pēc tam optimizējiet, pamatojoties uz profilēšanas rezultātiem.
- Testējiet rūpīgi: Pārliecinieties, ka jūsu optimizācijas neievieš jaunas kļūdas vai veiktspējas regresijas.
- Apsveriet kešatmiņas izmantošanu: Bieži piekļūstamiem datiem apsveriet kešatmiņas mehānismu (piemēram, Django kešatmiņas ietvaru vai Redis) izmantošanu, lai vēl vairāk uzlabotu veiktspēju.
- Izmantojiet indeksus datubāzē: Tas ir obligāti optimālai vaicājumu veiktspējai, īpaši produkcijā.
Padziļinātas optimizācijas metodes
Papildus select_related un prefetch_related ir arī citas padziļinātas metodes, ko varat izmantot, lai optimizētu savus Django ORM vaicājumus:
only()undefer(): Šīs metodes ļauj norādīt, kurus laukus iegūt no datubāzes. Izmantojietonly(), lai iegūtu tikai nepieciešamos laukus, undefer(), lai izslēgtu laukus, kas nav nepieciešami nekavējoties.values()unvalues_list(): Šīs metodes ļauj iegūt datus kā vārdnīcas vai kortežus, nevis Django modeļa instances. Tas var būt efektīvāk, ja jums nepieciešama tikai daļa no modeļa laukiem.- Neapstrādāti SQL vaicājumi: Dažos gadījumos Django ORM var nebūt visefektīvākais veids, kā iegūt datus. Varat izmantot neapstrādātus SQL vaicājumus sarežģītiem vai ļoti optimizētiem vaicājumiem.
- Datubāzei specifiskas optimizācijas: Dažādām datubāzēm (piem., PostgreSQL, MySQL) ir dažādas optimizācijas metodes. Izpētiet un izmantojiet datubāzei specifiskas funkcijas, lai vēl vairāk uzlabotu veiktspēju.
Internacionalizācijas apsvērumi
Izstrādājot Django lietojumprogrammas globālai auditorijai, ir svarīgi ņemt vērā internacionalizāciju (i18n) un lokalizāciju (l10n). Tas var ietekmēt jūsu datubāzes vaicājumus vairākos veidos:
- Valodai specifiski dati: Jums var būt nepieciešams glabāt satura tulkojumus savā datubāzē. Izmantojiet Django i18n ietvaru, lai pārvaldītu tulkojumus un nodrošinātu, ka jūsu vaicājumi iegūst pareizo datu valodas versiju.
- Rakstzīmju kopas un salīdzināšanas kārtulas (collations): Izvēlieties atbilstošas rakstzīmju kopas un salīdzināšanas kārtulas savai datubāzei, lai atbalstītu plašu valodu un rakstzīmju klāstu.
- Laika joslas: Strādājot ar datumiem un laikiem, esiet uzmanīgi ar laika joslām. Glabājiet datumus un laikus UTC formātā un konvertējiet tos uz lietotāja vietējo laika joslu, tos attēlojot.
- Valūtas formatēšana: Rādod cenas, izmantojiet atbilstošus valūtas simbolus un formatējumu, pamatojoties uz lietotāja lokalizāciju.
Noslēgums
Django ORM vaicājumu optimizācija ir būtiska, lai veidotu mērogojamas un veiktspējīgas tīmekļa lietojumprogrammas. Izprotot un efektīvi izmantojot select_related un prefetch_related, jūs varat ievērojami samazināt datubāzes vaicājumu skaitu un uzlabot savas lietojumprogrammas kopējo atsaucību. Atcerieties profilēt savus vaicājumus, rūpīgi pārbaudīt optimizācijas un apsvērt citas padziļinātas metodes, lai vēl vairāk uzlabotu veiktspēju. Ievērojot šīs labākās prakses, jūs varat nodrošināt, ka jūsu Django lietojumprogramma sniedz vienmērīgu un efektīvu lietotāja pieredzi neatkarīgi no tās lieluma vai sarežģītības. Tāpat ņemiet vērā, ka labs datubāzes dizains un pareizi konfigurēti indeksi ir obligāti optimālai veiktspējai.