Optimeerige Django andmebaasipäringuid, kasutades select_related ja prefetch_related funktsioone, et parandada jõudlust. Õppige praktilisi näiteid ja parimaid praktikaid.
Django ORM-i päringute optimeerimine: select_related vs. prefetch_related
Django rakenduse kasvades muutuvad tõhusad andmebaasipäringud optimaalse jõudluse säilitamiseks ülioluliseks. Django ORM pakub võimsaid tööriistu andmebaasi poole pöördumiste minimeerimiseks ja päringute kiiruse parandamiseks. Kaks peamist tehnikat selle saavutamiseks on select_related ja prefetch_related. See põhjalik juhend selgitab neid kontseptsioone, demonstreerib nende kasutamist praktiliste näidete abil ja aitab teil valida oma konkreetsete vajaduste jaoks õige tööriista.
N+1 probleemi mõistmine
Enne kui süveneda select_related ja prefetch_related funktsioonidesse, on oluline mõista probleemi, mida nad lahendavad: N+1 päringu probleem. See tekib siis, kui teie rakendus teostab ühe esialgse päringu objektide hulga toomiseks ja seejärel teeb täiendavaid päringuid (N päringut, kus N on objektide arv), et tuua seotud andmed iga objekti jaoks.
Vaatleme lihtsat näidet mudelitega, mis esindavad autoreid ja raamatuid:
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)
Kujutage nüüd ette, et soovite kuvada raamatute nimekirja koos nende vastavate autoritega. Naiivne lähenemine võiks välja näha selline:
books = Book.objects.all()
for book in books:
print(f"{book.title} by {book.author.name}")
See kood genereerib ühe päringu kõigi raamatute toomiseks ja seejärel ühe päringu iga raamatu kohta, et tuua selle autor. Kui teil on 100 raamatut, teostate 101 päringut, mis toob kaasa olulise jõudluse kao. See ongi N+1 probleem.
select_related tutvustus
select_related kasutatakse päringute optimeerimiseks, mis hõlmavad üks-ühele ja võõrvõtme suhteid. See töötab seotud tabeli(te) liitmisel esialgse päringuga, tuues seotud andmed tõhusalt kätte ühe andmebaasi pöördumisega.
Vaatame uuesti meie autorite ja raamatute näidet. N+1 probleemi lahendamiseks saame kasutada select_related funktsiooni niimoodi:
books = Book.objects.all().select_related('author')
for book in books:
print(f"{book.title} by {book.author.name}")
Nüüd teostab Django ühe, keerukama päringu, mis liidab tabelid Book ja Author. Kui teete tsüklis pöördumise book.author.name poole, on andmed juba olemas ja täiendavaid andmebaasipäringuid ei tehta.
select_related kasutamine mitme suhtega
select_related saab läbida mitmeid suhteid. Näiteks, kui teil on mudel, millel on võõrvõti teise mudeli külge, millel omakorda on võõrvõti veel ühe mudeli külge, saate kasutada select_related funktsiooni, et tuua kõik seotud andmed ühe korraga.
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)
# Lisa autorile riik
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'}")
Sel juhul toob select_related('profile__country') ühe päringuga AuthorProfile ja seotud Country. Pange tähele topelt allkriipsu (__) tähistust, mis võimaldab teil suhete puud läbida.
select_related piirangud
select_related on kõige tõhusam üks-ühele ja võõrvõtme suhete puhul. See ei sobi mitu-mitmele suhete või pöördvõõrvõtme suhete jaoks, kuna see võib suurte seotud andmehulkade korral viia suurte ja ebatõhusate päringuteni. Nendel juhtudel on prefetch_related parem valik.
prefetch_related tutvustus
prefetch_related on mõeldud päringute optimeerimiseks, mis hõlmavad mitu-mitmele ja pöördvõõrvõtme suhteid. Liitmise asemel teostab prefetch_related iga suhte jaoks eraldi päringud ja kasutab seejärel Pythonit tulemuste "liitmiseks". Kuigi see hõlmab mitut päringut, võib see olla tõhusam kui liitmiste kasutamine suurte seotud andmehulkade puhul.
Vaatleme stsenaariumi, kus igal raamatul võib olla mitu ž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)
Raamatute ja nende Ĺľanrite nimekirja toomiseks ei oleks select_related kasutamine asjakohane. Selle asemel kasutame 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}")
Sel juhul teostab Django kaks päringut: ühe kõigi raamatute toomiseks ja teise kõigi nende raamatutega seotud žanrite toomiseks. Seejärel kasutab see Pythonit, et žanrid tõhusalt vastavate raamatutega siduda.
prefetch_related kasutamine pöördvõõrvõtmetega
prefetch_related on kasulik ka pöördvõõrvõtme suhete optimeerimiseks. Vaatleme järgmist näidet:
class Author(models.Model):
name = models.CharField(max_length=255)
country = models.CharField(max_length=255, blank=True, null=True) # Lisatud selguse huvides
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)
Autorite ja nende raamatute nimekirja toomiseks:
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)}")
Siin toob prefetch_related('books') eraldi päringuga kõik iga autoriga seotud raamatud, vältides N+1 probleemi, kui pääsete ligi author.books.all().
prefetch_related kasutamine päringukomplektiga (queryset)
Saate prefetch_related käitumist veelgi kohandada, pakkudes seotud objektide toomiseks kohandatud päringukomplekti. See on eriti kasulik, kui peate seotud andmeid filtreerima või sortima.
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.")
Selles näites võimaldab Prefetch objekt meil määrata kohandatud päringukomplekti, mis toob ainult need raamatud, mille pealkiri sisaldab sõna "django".
prefetch_related aheldamine
Sarnaselt select_related funktsioonile saate aheldada prefetch_related kutseid mitme suhte optimeerimiseks:
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]}")
See näide eellaadib autoriga seotud raamatud ja seejärel nende raamatutega seotud žanrid. Aheldatud prefetch_related kasutamine võimaldab teil optimeerida sügavalt pesastatud suhteid.
select_related vs. prefetch_related: Õige tööriista valimine
Niisiis, millal peaksite kasutama select_related ja millal prefetch_related? Siin on lihtne juhis:
select_related: Kasutage üks-ühele ja võõrvõtme suhete jaoks, kui peate seotud andmetele sageli ligi pääsema. See teostab andmebaasis liitmise (JOIN), seega on see üldiselt kiirem väikeste seotud andmemahtude toomiseks.prefetch_related: Kasutage mitu-mitmele ja pöördvõõrvõtme suhete jaoks või suurte seotud andmehulkade korral. See teostab eraldi päringud ja kasutab tulemuste liitmiseks Pythonit, mis võib olla tõhusam kui suured liitmised. Kasutage ka siis, kui peate seotud objektidel kasutama kohandatud päringukomplekti filtreerimist.
Kokkuvõttes:
- Suhte tĂĽĂĽp:
select_related(ForeignKey, OneToOne),prefetch_related(ManyToManyField, pöördvõõrvõti) - Päringu tüüp:
select_related(JOIN),prefetch_related(Eraldi päringud + Pythoni liitmine) - Andmete suurus:
select_related(Väikesed seotud andmed),prefetch_related(Suured seotud andmed)
Praktilised näited ja parimad praktikad
Siin on mõned praktilised näited ja parimad praktikad select_related ja prefetch_related kasutamiseks reaalsetes stsenaariumides:
- E-kaubandus: Toote ĂĽksikasjade kuvamisel kasutage
select_related, et tuua toote kategooria ja tootja. Kasutageprefetch_relatedtoote piltide või seotud toodete toomiseks. - Sotsiaalmeedia: Kasutaja profiili kuvamisel kasutage
prefetch_related, et tuua kasutaja postitused ja jälgijad. Kasutageselect_related, et tuua kasutaja profiili teave. - Sisuhaldussüsteem (CMS): Artikli kuvamisel kasutage
select_related, et tuua autor ja kategooria. Kasutageprefetch_related, et tuua artikli sildid ja kommentaarid.
Ăśldised parimad praktikad:
- Profileerige oma päringuid: Kasutage Django debug toolbari või muid profileerimisvahendeid, et tuvastada aeglaseid päringuid ja potentsiaalseid N+1 probleeme.
- Alustage lihtsalt: Alustage naiivse teostusega ja optimeerige seejärel profileerimistulemuste põhjal.
- Testige põhjalikult: Veenduge, et teie optimeerimised ei tooks kaasa uusi vigu ega jõudluse langust.
- Kaaluge vahemälu kasutamist: Sageli kasutatavate andmete jaoks kaaluge vahemälu mehhanismide (nt Django vahemälu raamistik või Redis) kasutamist jõudluse edasiseks parandamiseks.
- Kasutage andmebaasis indekseid: See on optimaalse päringu jõudluse tagamiseks hädavajalik, eriti tootmiskeskkonnas.
Täiustatud optimeerimistehnikad
Lisaks select_related ja prefetch_related funktsioonidele on olemas ka teisi täiustatud tehnikaid, mida saate oma Django ORM-i päringute optimeerimiseks kasutada:
only()jadefer(): Need meetodid võimaldavad teil määrata, milliseid välju andmebaasist tuua. Kasutageonly(), et tuua ainult vajalikud väljad, jadefer(), et välistada väljad, mida kohe ei vajata.values()javalues_list(): Need meetodid võimaldavad teil andmeid tuua sõnastike või ennikutena, mitte Django mudeli instantsidena. See võib olla tõhusam, kui vajate ainult osa mudeli väljadest.- Toored SQL-päringud: Mõnel juhul ei pruugi Django ORM olla kõige tõhusam viis andmete toomiseks. Saate kasutada tooreid SQL-päringuid keerukate või kõrgelt optimeeritud päringute jaoks.
- Andmebaasipõhised optimeerimised: Erinevatel andmebaasidel (nt PostgreSQL, MySQL) on erinevad optimeerimistehnikad. Uurige ja kasutage andmebaasipõhiseid funktsioone jõudluse edasiseks parandamiseks.
Rahvusvahelistamise kaalutlused
Django rakenduste arendamisel globaalsele publikule on oluline arvestada rahvusvahelistamise (i18n) ja lokaliseerimisega (l10n). See võib teie andmebaasipäringuid mitmel viisil mõjutada:
- Keelepõhised andmed: Võimalik, et peate oma andmebaasis säilitama sisu tõlkeid. Kasutage Django i18n raamistikku tõlgete haldamiseks ja veendumaks, et teie päringud toovad andmete õige keeleversiooni.
- Märgistikud ja kollatsioonid: Valige oma andmebaasi jaoks sobivad märgistikud ja kollatsioonid, et toetada laia valikut keeli ja märke.
- Ajavööndid: Kuupäevade ja kellaaegade käsitlemisel olge ajavöönditega tähelepanelik. Salvestage kuupäevad ja kellaajad UTC-s ning teisendage need kuvamisel kasutaja kohalikku ajavööndisse.
- Valuuta vormindamine: Hindade kuvamisel kasutage vastavalt kasutaja lokaadile sobivaid valuutasĂĽmboleid ja vormingut.
Kokkuvõte
Django ORM-i päringute optimeerimine on skaleeritavate ja suure jõudlusega veebirakenduste loomisel hädavajalik. Mõistes ja tõhusalt kasutades select_related ja prefetch_related funktsioone, saate oluliselt vähendada andmebaasipäringute arvu ja parandada oma rakenduse üldist reageerimisvõimet. Ärge unustage oma päringuid profileerida, optimeerimisi põhjalikult testida ja kaaluda teisi täiustatud tehnikaid jõudluse edasiseks parandamiseks. Neid parimaid praktikaid järgides saate tagada, et teie Django rakendus pakub sujuvat ja tõhusat kasutajakogemust, olenemata selle suurusest või keerukusest. Arvestage ka sellega, et hea andmebaasi disain ja õigesti konfigureeritud indeksid on optimaalse jõudluse tagamiseks hädavajalikud.