优化 Django 管理后台 SQL

1 投票
1 回答
664 浏览
提问于 2025-04-18 04:35

我有以下的数据库结构:

class Book(models.Model):
    id = models.AutoField(primary_key=True, db_column='id')
    name = models.CharField(max_length=255, db_column='name')
    author = models.ForeignKey('Author', to_field='id', db_column='author_id')


class Author(models.Model):
    id = models.AutoField(primary_key=True, db_column='id')
    fio = models.CharField(max_length=255, db_column='fio')

    def __unicode__(self):
        return self.name

还有这个用于书籍的Admin类:

class BookAdmin(admin.ModelAdmin):
    list_display = ('id', 'name',)
    fields = ('id', 'name', 'author',)
    readonly_fields = ('id',)
    raw_id_fields = ('author',)

当书籍记录在500到1000条的时候,一切都运行得很好,但当记录达到100万到200万条时,页面会卡住几分钟才显示内容。性能分析工具告诉我,django在连接书籍和作者时,最后只取了100条记录。

SELECT ••• FROM `books` INNER JOIN `authors` ON ( `books`.`author_id` = `authors`.`id` ) ORDER BY `books`.`id` DESC LIMIT 100

我该如何优化django,让它在从数据库中选择书籍后再连接作者?或者使用类似的方式:

select * from (SELECT * FROM books ORDER BY books.id DESC LIMIT 100) t, authors a where t.author_id = a.id

1 个回答

1

你遇到了一个已知的问题:在MySQL中,慢速的INNER JOIN可以通过Django ORM来修复,但这样做是否合适呢?

有一个项目叫做django-mysql-fix,这个项目是在2014年PyCon大会的开发活动中开始的,目的是为了解决INNER JOIN的问题,专门为MySQL提供了一个自定义的数据库后端:

这个项目包含了针对Django ORM的MySQL优化(可以理解为一些小技巧)。

有两种非常简单的方法可以捕捉到INNER JOIN的错误:

  • 当你在Django管理后台模型中指定了外部表的字段到列表展示时;

  • 当你尝试按外部表的字段进行排序时。

据我了解,这个项目在后台会把所有的INNER JOIN替换成STRAIGHT_JOIN,引用自MySQL文档

STRAIGHT_JOIN和JOIN类似,不同的是左边的表总是会在右边的表之前被读取。这可以用于那些(少数)情况下,连接优化器把表的顺序搞错了。

另外可以参考:

撰写回答