仕事がDjango漬けなこともありいろんな点に気付いたりはまったりしてます。本日「ギャー、ヤラレター!」と思ったことがあったので紹介。
Djangoを使うと管理画面はadmin任せになることが多いと思うのだけど、ModelAdminクラスを何も考えずに書いてると、データベースへものすごい負荷がかかることがある。
どういうときにまずいのか。
ModelAdminのlist_displayにForeignKeyのフィールドが含まれていて、レコードの数が多い場合。
このときadminアプリで一覧表示をしようとすると、DBに負荷の高いSQLが投げられてしまう。list_displayがForeignKeyを含んでいる場合にはadminはselect_relatedでDBを引きに行くのが原因。ちなみに、select_relatedが使用されることはドキュメントにもしっかり書かれてる。
http://djangoproject.jp/doc/ja/1.0/ref/contrib/admin.html#list-select-related
コード例を書く。
models.py
from django.db import models from django.contrib import admin class M1(models.Model): name = models.CharField(max_length=100) class M2(models.Model): m1 = models.ForeignKey(M1) class M3(models.Model): m2 = models.ForeignKey(M2) class M4(models.Model): m3 = models.ForeignKey(M3) class M4Admin(admin.ModelAdmin): list_display = ['m3'] admin.site.register(M4, M4Admin)
これで、adminからM4の一覧表示をするとき、以下のクエリが投げられている。
SELECT `hoge_m4`.`id`, `hoge_m4`.`m3_id`, `hoge_m3`.`id`, `hoge_m3`.`m2_id`, `hoge_m2`.`id`, `hoge_m2`.`m1_id`, `hoge_m1`.`id`, `hoge_m1`.`name` FROM `hoge_m4` INNER JOIN `hoge_m3` ON (`hoge_m4`.`m3_id` = `hoge_m3`.`id`) INNER JOIN `hoge_m2` ON (`hoge_m3`.`m2_id` = `hoge_m2`.`id`) INNER JOIN `hoge_m1` ON (`hoge_m2`.`m1_id` = `hoge_m1`.`id`) ORDER BY `hoge_m4`.`id` DESC
さて、このとき「hoge_m1」「hoge_m2」「hoge_m3」「hoge_m4」のそれぞれテーブルが大量のレコードを持っていた場合、結合がさあ大変。DBにかなり負荷がかかっちゃう。
レコードが少ないと特に問題もないので気付きにくいと思った。以上。