Optimisasi akses basisdata¶
Lapisan basisdata Django menyediakan beragam cara membantu pengembang mendapatkan sebagian dari basisdata mereka. DOkumen ini mengumpulkan bersama-sama tautan ke dokumentasi bersangkutan, dan menambah beraagam tip, mengorganisasikan dibawah sejumlah dari kepala yang meringkaskan langkah-langkah diambil ketika berusaha mengoptimalkan penggunaan basisdata anda.
Profil dahulu¶
As general programming practice, this goes without saying. Find out what
queries you are doing and what they are costing you.
Use QuerySet.explain()
to understand how specific QuerySet
s are
executed by your database. You may also want to use an external project like
django-debug-toolbar, or a tool that monitors your database directly.
Ingat bahwa anda mungkin dioptimalkan untuk kecepatan atau memori atau keduanya, tergantung pada persyaraan anda. Terkadang mengoptimalkan untuk satu akan merugikan ke yang lain, tetapi terkadang mereka akan membantu satu sama lain. Juga, pekerjaan itu selesai oleh pengolahan basisdata mungkin tidak mempunyai biaya sama (kepada anda) seperti sama banyak dari pekerjaan selesai dalam pengolahan Python anda. Itu terserah anda memutuskan apa prioritas anda, dimana keseimbangan harus berbohong, dan profil semua dari ini seperti diwajibkan sejak ini akan bergantung pada aplikasi anda dan peladen.
Dengan apapun yang mengikuti, ingat untuk menggambarkan raut muka setelah setiap perubahan untuk memastikan bahwa perubahan adalah menguntungkan, dan keuntungan cukup besar diberikan menurunkan dalam kesiapan kode anda. Semua dari saran-saran dibawah datang dengan surat keberatan yang dalam keadaan anda prinsip umum mungkin tidak berlaku, atau mungkin bahkan dibalikkan.
Gunakan teknik-teknik optimalisasi DB standar¶
...termasuk:
- Indexes. This is a number one priority, after you have determined from
profiling what indexes should be added. Use
Meta.indexes
orField.db_index
to add these from Django. Consider adding indexes to fields that you frequently query usingfilter()
,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.
- Penggunaan sesuai dari jenis-jenis bidang
We will assume you have done the things listed above. The rest of this document focuses on how to use Django in such a way that you are not doing unnecessary work. This document also does not address other optimization techniques that apply to all expensive operations, such as general purpose caching.
Memahami QuerySet
¶
Memahami QuerySets adalah vital untuk mendapatkan penampilan baik dengan kode sederhana. Khususnya:
Memahami penilaian QuerySet
¶
Untuk menghindari masalah penampilan, sangatlah penting memahami:
- itu QuerySets malas.
- ketika mereka dinilai.
- bagaimana data ditahan dalam memori.
Memahami atribut tembolok¶
Seperti menyimpan sementara keseluruhan QuerySet
, ada penyimpanan sementara dari hasil atribut pada obyek ORM. Secara umum, atribut-atribut tidak dapat dipanggil akan disimpan sementara. Sebagai contoh, menganggap example Weblog models 1:
>>> entry = Entry.objects.get(id=1)
>>> entry.blog # Blog object is retrieved at this point
>>> entry.blog # cached version, no DB access
Tetapi secara umum, atribut callable menyebabkan pencarian DB setiap waktu:
>>> entry = Entry.objects.get(id=1)
>>> entry.authors.all() # query performed
>>> entry.authors.all() # query performed again
Hati-hati ketika membaca kode cetakan - sistem cetakan tidak mengizinkan penggunaan tanda kurun, tetapi akan memanggil callable secara otomatis, memnyembunyikan perbedaan diatas.
Berhati-hatilan dengan sifat penyesuaian anda sendiri - itu terserah anda menerapkan cache ketika dibutuhkan, sebagai contoh menggunakan penghias cached_property
.
Gunakan etiket cetakan with
¶
Untuk menggunakan perilaku cache dari QuerySet
, anda mungkin butuh menggunakan etiket cetakan with
.
Gunakan iterator()
¶
Ketika anda mempunyai banyak obyek, perilaku cache dari QuerySet
dapat menyebabkan sejumlah besar memori digunakan. Dalam kasus ini, iterator()
mungkin membantu.
Gunakan explain()
¶
QuerySet.explain()
gives you detailed information about how the database
executes a query, including indexes and joins that are used. These details may
help you find queries that could be rewritten more efficiently, or identify
indexes that could be added to improve performance.
Lakukan pekerjaan basisdata di basisdata daripada di Python¶
Sebagai contoh:
- Pada paling tingkat dasar, gunakan filter and exclude melakukan penyaringan di basisdata
- Gunakan
F expressions
untukmenyaring berdasarkan pada bidang-bidang lain dalam model sama. - Gunakan annotate to do aggregation in the database.
Jika ini tidak cukup membangkitkan SQL yang anda butuhkan:
Gunakan RawSQL
¶
Sedikit ringan tetapi metode lebih kuat adalah pernyataan RawSQL
, yangmengizinkan beberapa SQL secara jelas ditambahkan ke permintaan. Jika itu masih tidak cukup kuat:
Gunakan SQL mentah¶
Tulis custom SQL to retrieve data or populate models 1 anda sendiri. Gunakan django.db.connection.queries
untuk menemukan apa yang Django sedang tulis untuk anda dan mulai dari sana.
Mengambil obyek tersendiri menggukan sebuah unik, kolom indeks¶
Ada dua alasan untuk menggunakan kolom dengan unique
atau db_index
ketika menggunakan get()
untuk mengambil obyek tersendiri. Pertama, permintaan akan lebih cepat karena indeks basisdata pokok. Juga, permintaan dapat berjalan lebih lambat jika banyak obyek mencocokan pencarian; memiliki batasan unik pada kolom menjamin ini tidak pernah terjadi.
Jadi menggunakan example Weblog models:
>>> entry = Entry.objects.get(id=10)
akan lebih cepat daripada:
>>> entry = Entry.objects.get(headline="News Item Title")
karena id
diindeks oleh basisdata dan dijamin menjadi unik.
Melakukan berikut berpotensi sangat lambat:
>>> entry = Entry.objects.get(headline__startswith="News")
Pertama-tama, headline
tidak diindeks, yang akan membuat pengambilan pokok basisdata lebih lambat.
Kedua, pencarian tidak menjamin hanya satu obyek akan dikembalikan. Jika permintaan cocok lebih dari satu obyek, itu akan mengambil dan memindahkan semua dari mereka dari basisdata. Hukuman ini dapat besar jika ratusan atau ribuan dari rekaman dikembalikan. Hukuman akan digabungkan jika basisdata berada pada peladen berbeda, dimana overhead jaringan dan latensi memainkan faktor.
Mengambil semuanya sekaligus jika anda mengetahui anda akan membutuhkannya¶
Mengenai basisdata beberapa kali untuk bagian-bagian berbeda dari 'set' tunggal data yang anda akan butuh semua bagian, secara umum, kurang efisien dari mengambil itu semua dalam satu permintaan. Ini khususnya penting jika anda mempunyai sebuah permintaan yang dijalankan dalam putaran, dan dapat karena itu berakhir melakukan banyak permintaan basisdata, ketika hanya satu dibutuhkan. Jadi:
Jangan mengambil hal yang tidak anda butuhkan¶
Gunakan QuerySet.values()
dan 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.
Gunakan QuerySet.defer()
dan only()
¶
Gunakan defer()
dan only()
jika ada kolom basisdata anda ketahui bahwa anda tidak butuh (atau tidak butuh dalam kebanyakan kasus) untuk menghindari memuatkan mereka. Catat bahwa jika anda melakukan menggunakan mereka, ORM akan harus pergi dan mendapatkan mereka dalam permintaan terpisah, membuat ini pesimis jika anda menggunakan itu secara tidak benar.
Don't be too aggressive in deferring fields without profiling as the database
has to read most of the non-text, non-VARCHAR data from the disk for a single
row in the results, even if it ends up only using a few columns. The
defer()
and only()
methods are most useful when you can avoid loading a
lot of text data or for fields that might take a lot of processing to convert
back to Python. As always, profile first, then optimize.
Gunakan QuerySet.count()
¶
..jika anda hanya ingin menghitung, daripada melakukan len(queryset)
.
Gunakan QuerySet.exists()
¶
...jika anda hanya ingin menemukan jika setidaknya satu hasil ada, daripada if queryset
.
Tetapi:
Jangan berlebihan count()
dan exists()
¶
Jika anda butuh data lain dari QuerySet, nilai itu segera.
For example, assuming an Email model that has a subject
attribute and a
many-to-many relation to User, the following code is optimal:
if display_emails:
emails = user.emails.all()
if emails:
print('You have', len(emails), 'emails:')
for email in emails:
print(email.subject)
else:
print('You do not have any emails.')
DIa optimal karena:
- Since QuerySets are lazy, this does no database queries if
display_emails
isFalse
. - Storing
user.emails.all()
in theemails
variable allows its result cache to be re-used. - The line
if emails
causesQuerySet.__bool__()
to be called, which causes theuser.emails.all()
query to be run on the database. If there aren't any results, it will returnFalse
, otherwiseTrue
. - The use of
len(emails)
callsQuerySet.__len__()
, reusing the result cache. - The
for
loop iterates over the already filled cache.
In total, this code does either one or zero database queries. The only
deliberate optimization performed is using the emails
variable. Using
QuerySet.exists()
for the if
or QuerySet.count()
for the count
would each cause additional queries.
Gunakan QuerySet.update()
dan delete()
¶
Daripada mengambil memuat obyek, setel beberapa nilai, dan simpan mereka masing-masing, gunakan pernyataan SQL UPDATE dalam jumlah besar, melalui QuerySet.update() 1. Demikian pula, lakukan bulk deletes 2 dimana memungkinkan.
Catat, bagaimanapun, metode pembaharuan jumlah besar ini tidak dapat memanggil metode save()
atau delete()
dari masing-masing instance, yang berarti bahwa perilaku penyesuaian apapun anda telah tambahkan untuk metode-metode ini tidak akan dijalankan, termasuk apapun didorong dari obyek basisdata biasa signals 1.
Menggunakan nilai foreign key secara langsung¶
Jika anda hanya butuh nilai foreign key, gunakan nilai foreign key yang sudah pada obyek anda telah dapatkan, daripada mendapatkan keseluruhan obyek terkait dan mengambil primary key nya, yaitu. lakukan:
entry.blog_id
dari pada:
entry.blog.id
Jangan urutkan hasil jika anda tidak peduli¶
Pengurutan tidak bebas; setiap bidang yang diurutkan adalah sebuah operai basisdata harus dilakukan. Jika sebuah model awal mengurutkan (Meta.ordering
) dan anda tidak membutuhkannya, pindahkan dia pada QuerySet
dengan memanggil order_by()
dengan tidak ada parameter.
Menambahkan indeks ke basisdata anda mungkin membantu meningkatkan penampilan pengurutan.
Use bulk methods¶
Use bulk methods to reduce the number of SQL statements.
Create in bulk¶
Ketika membuat obyek-obyek, dimana memungkinkan, gunakan metode bulk_create()
untuk mengurangi sejumlah permintaan SQL. Sebagai contoh:
Entry.objects.bulk_create([
Entry(headline='This is a test'),
Entry(headline='This is only a test'),
])
...adalah lebih baik untuk:
Entry.objects.create(headline='This is a test')
Entry.objects.create(headline='This is only a test')
Catat bahwa ada angka dari caveats to this method
, jadi pastikan dia sesuai untuk kasus digunakan anda.
Update in 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'),
])
Contoh berikut:
entries[0].headline = 'This is not a test'
entries[1].headline = 'This is no longer a test'
Entry.objects.bulk_update(entries, ['headline'])
...adalah lebih baik untuk:
entries[0].headline = 'This is not a test'
entries[0].save()
entries[1].headline = 'This is no longer a test'
entries[1].save()
Note that there are a number of caveats to this method
, so make sure it's appropriate
for your use case.
Masuk dalam bulk¶
When inserting objects into ManyToManyFields
, use
add()
with multiple
objects to reduce the number of SQL queries. For example:
my_band.members.add(me, my_friend)
...adalah lebih baik untuk:
my_band.members.add(me)
my_band.members.add(my_friend)
...dimana Bands
dan Artists
mempunyai hubungan many-to-many.
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)
...adalah lebih baik untuk:
my_pizza.toppings.add(pepperoni)
your_pizza.toppings.add(pepperoni, mushroom)
...where Pizza
and Topping
have a many-to-many relationship. Note that
there are a number of caveats to this method
, so make sure it's appropriate
for your use case.
Remove in bulk¶
When removing objects from ManyToManyFields
, use
remove()
with multiple
objects to reduce the number of SQL queries. For example:
my_band.members.remove(me, my_friend)
...adalah lebih baik untuk:
my_band.members.remove(me)
my_band.members.remove(my_friend)
...dimana Bands
dan Artists
mempunyai hubungan many-to-many.
When removing different pairs of objects from ManyToManyFields
, use
delete()
on a
Q
expression with multiple
through
model instances to reduce
the number of SQL queries. For example:
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()
...adalah lebih baik untuk:
my_pizza.toppings.remove(pepperoni)
your_pizza.toppings.remove(pepperoni, mushroom)
...where Pizza
and Topping
have a many-to-many relationship.