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. Ini adalah prioritas nomor satu, setelah anda menentukan dari menggambarkan indeks apa harus ditambahkan. Gunakan
Field.db_index
atauMeta.index_together
untuk menambah ini dari Django. Pertimbangkan menambahkan indeks ke bidang yang anda sangat sering meminta menggunakanfilter()
,exclude()
,order_by()
, dll. ketika indeks mungkin membantu anda mempercepat pencarian. Catat bahwa menentukan indeks terbaik adalah topik berdiri-sendiri-basisdata rumit yang akan bergantung pada aplikasi tertentu anda. Diatas dari merawat sebuah indeks mungkin lebih penting dari keuntungan dalam kecepatan.
- Penggunaan sesuai dari jenis-jenis bidang
Kami akan menganggap anda telah melakukan hal-hal tidak jelas diatas. Sisa dari dokumen ini fokus pada bagaimana menggunakan Django dalam seperti itu yang anda tidak melakukan pekerjaan tidak penting. Dokumen ini tidak mengalamatkan teknik-teknik optimalisasi lain yang berlaku ke semua tindakan, seperti general purpose caching 1.
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()
¶
Ketika anda hanya ingin nilai-nilai dict
atau list
, dan tidak butuh obyek model ORM, buat penggunaan sesuai dari values()
. Ini dapat berguna untuk mengganti obyek model dalam kode cetakan - selama dictionary anda sokong mempuyai atribut sama seperti itu digunakan dalam cetakan, anda baik-baik saja.
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.
Juga, waspada bahwa ada beberapa (sedikit tambahan) overhead terjadi didalam Django ketika membangun model dengan bidang-bidang yang ditangguhkan. Jangan terlalu agresif dalam menangguhkan bidang-bidang tanpa memprofilkan sebagai basisdata harus membaca kebanyakan bukan-teks, data bukan-VARCHAR dari cakram untuk baris tunggal dalam hasil, bahkan jika itu berakhir hanya menggunakan sedikit kolom. Metode defer()
dan only()
paling berguna ketika anda dapat menghindari memuat banyak data teks atau untuk bidang yang mungkin mengambil banyak pengolahan untuk merubah kembali ke Python. Seperti biasanya, profilkan dahulu, kemudian optimalkan.
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 akan butuh data lain dari QuerySet, cukup nilai itu.
Sebagai contoh, menganggap sebuah model Email yang mempunyai atribut body
dan hubungan many-to-many pada User, kode cetakan berikut adalah optimal:
{% if display_inbox %}
{% with emails=user.emails.all %}
{% if emails %}
<p>You have {{ emails|length }} email(s)</p>
{% for email in emails %}
<p>{{ email.body }}</p>
{% endfor %}
{% else %}
<p>No messages today.</p>
{% endif %}
{% endwith %}
{% endif %}
DIa optimal karena:
- Sejak QuerySet adalah lazy, ini tidak melakukan permintaan basisdata jika 'display_inbox' adalah False.
- Penggunaan
with
berarti bahwa kami menyimpanuser.emails.all
dalam sebuah variabel untuk penggunaan akhir, mengizinkan cache itu untuk digunakan-kembali. - Baris
{% if emails %}
menyebabkanQuerySet.__bool__()
dipanggil, yang menyebabkan permintaanuser.emails.all()
dijalankan pada basisdata, dan setidaknya baris pertama dirubah menjadi obyek ORM. Jika tidak ada hasil apapun, itu akan mengembalikan False, sebaliknya True. - Penggunaan
{{ emails|length }}
memanggilQuerySet.__len__()
, mengisi sisa dari cache tanpa melakukan permintaan lain. - Putaran
for
berulang terhadap cache sudah diisi.
Dalam jumlah, kode ini melakukan baik satu atau nol permintaan basisdata. Satu-satunya optimalisasi kesengajaan dilakukan adalah menggunakan etiket with
. Menggunakan QuerySet.exists()
atau QuerySet.count()
pada titik apapun akan menyebabkan tambahan permintaan.
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.
Masuk dalam 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.
Ini juga berlaku pada ManyToManyFields
, demikian:
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.