Django管理MySQL的慢INNER JOIN(Django admin MySQL slow

2019-08-22 02:10发布

我有3个ForeignKey的领域的简单模型。

class Car(models.Model):
    wheel = models.ForeignKey('Wheel', related_name='wheels')
    created = models.DateTimeField(auto_now_add=True)
    max_speed = models.PositiveSmallIntegerField(null=True)
    dealer = models.ForeignKey('Dealer')
    category = models.ForeignKey('Category')

对于在Django管理列表视图中我得到4个查询。 其中之一是选择具有3内部连接。 这一个查询的方式来放缓。 与STRAIGHT_JOIN更换内部联接将解决这个问题。 有没有办法来修补管理员生成的查询其被评估之前?

Answer 1:

我实现了一个INNER JOIN修复Django的ORM,它会在内部连接订货的情况下使用STRAIGHT_JOIN。 我跟Django的核心开发者,我们决定做这个作为一个独立的后端现在。 所以,你可以看看这里: https://pypi.python.org/pypi/django-mysql-fix

然而,有一个其他的解决办法。 从詹姆斯的答案中使用的摘要,但将其替换select_related:

qs = qs.select_related('').prefetch_related('wheel', 'dealer', 'category')

这将取消INNER JOIN和使用4个单独的查询:1与取车和3人car_id IN(...)。

更新:我发现多了一个解决办法。 一旦你设置的ForeignKey的领域空=真,Django会使用LEFT OUTER连接,而不是INNER JOIN。 LEFT OUTER JOIN的作品,而不在这种情况下的性能问题,但你可能会面临,我是不知道的另外一些问题。



Answer 2:

你可能只是指定list_select_related = ()以防止Django的使用内连接:

class CarAdmin(admin.ModelAdmin):
    list_select_related = ()


Answer 3:

你可能会覆盖

  def changelist_view(self, request, extra_context=None):

在您的管理类中的方法从继承ModelAdmin

像这样(但这个问题已很旧): Django管理:获取根据获取字符串过滤一个QuerySet,正是因为看到更改列表?



Answer 4:

好吧,我找到了一种方法来修补生成的查询管理。 这是丑陋的,但它似乎工作

class CarChangeList(ChangeList):

    def get_results(self, request):
        """Override to patch ORM generated SQL"""
        super(CarChangeList, self).get_results(request)
        original_qs = self.result_list
        sql = str(original_qs.query)
        new_qs = Car.objects.raw(sql.replace('INNER JOIN', 'STRAIGHT_JOIN'))

        def patch_len(self):
           return original_qs.count()
        new_qs.__class__.__len__ = patch_len

        self.result_list = new_qs


class CarAdmin(admin.ModelAdmin):

    list_display = ('wheel', 'max_speed', 'dealer', 'category', 'created')

    def get_changelist(self, request, **kwargs):
        """Return custom Changelist"""
        return CarChangeList

admin.site.register(Rank, RankAdmin)


Answer 5:

我在Django管理(版本1.4.9),其中当由MySQL支持的相当简单的管理列表页面非常缓慢穿过同样的问题来了。

在我的情况下,它是由引起ChangeList.get_query_set()方法增加一个过于广泛的全球select_related()来设置,如果在任意字段查询list_display许多一对一的关系。 对于一个适当的数据库(PostgreSQL的咳嗽咳 ),这不会是一个问题,但它是为MySQL一度超过几联接被触发这种方式。

我找到了干净的解决方案是,以取代全球select_related()用更有针对性的一个,只有加入了真的是必要的表指令。 这是很容易通过调用做select_related()有明确的关系名称。

这种方法可能最终交换的数据库的连接多个后续查询,但如果MySQL在大型查询呛许多小的可能是你更快。

下面是我做什么,或多或少:

from django.contrib.admin.views.main import ChangeList


class CarChangeList(ChangeList):

    def get_query_set(self, request):
        """
        Replace a global select_related() directive added by Django in 
        ChangeList.get_query_set() with a more limited one.
        """
        qs = super(CarChangeList, self).get_query_set(request)
        qs = qs.select_related('wheel')  # Don't join on dealer or category
        return qs


class CarAdmin(admin.ModelAdmin):

        def get_changelist(self, request, **kwargs):
            return CarChangeList


Answer 6:

我有慢管理员查询在MySQL,发现最简单的解决办法是STRAIGHT_JOIN添加到查询。 我想出了一个办法把它添加到QuerySet而不是被迫去.raw()它不会与管理工作,并有开源了它的一部分django-mysql 。 然后,您可以只:

def get_queryset(self, request):
    qs = super(MyAdmin, self).get_queryset(request)
    return qs.straight_join()


文章来源: Django admin MySQL slow INNER JOIN