【问题标题】:django performance on querying models with many foreign keys?django 在查询具有许多外键的模型上的性能?
【发布时间】:2010-12-30 13:03:12
【问题描述】:

我正在建立一项服务,该服务需要按照案例跟踪系统的方式维护某些内容。这是我们的模型:

class Incident(models.Model):    
    title = models.CharField(max_length=128)
    category = models.ForeignKey(Category)
    status = models.ForeignKey(Status)    
    severity = models.ForeignKey(Severity)
    owned_by = models.ForeignKey(User, related_name="owned_by", null=True, blank=True)   
    next_action = models.ForeignKey(IncidentAction)    
    created_date  = models.DateTimeField()
    created_by = models.ForeignKey(User, related_name="opened_by")    
    last_edit_date = models.DateTimeField(null=True, blank=True)
    last_edit_by = models.ForeignKey(User, related_name="last_edit_by", null=True, blank=True)        
    closed_date  = models.DateTimeField(null=True, blank=True)
    closed_by = models.ForeignKey(User, related_name="Closed by", null=True, blank=True)

因为有很多外键被拉入这个模型,所以它产生了有趣的 sql 查询。我们一直在使用djblets data grid 和 django 调试工具栏作为试用版,每次我们为使用外键的视图添加新列时都会遇到大量查询,这让我们感到震惊,它基本上是这种类型的查询工作流程:

#prepare the grid
select * from incident_table;
#render each row
for each row in incident table
    for each column that is a foreign key select row from foreign table with id

每行为试图为外键提取属性的每一列执行一个额外的选择查询。

我认为这是 django 及其 ORM 的一个普遍问题,即从外键模型中提取属性以进行显示。作为测试,我删除了数据网格,只是为查询集创建了一个简单的属性列表,然后看到查询以类似的方式膨胀。

我们希望通过大量使用该模型的用户来扩大规模。作为比较,我在 User 模型上做了一个类似的视图,它的完整显示只通过一个查询完成,因为如果你只从给定模型中提取字段,它不会对每个额外的列进行额外的 db 命中。

我们尝试的一些优化是:

  • django-orm-cache: 似乎不适用于 django 1.0.4
  • django-caching:这对于缓存经常查询的模型很有效
  • 使用 memcached 进行视图级缓存
  • 编辑: 使用 select_related() 可能会加速模板渲染,因为它不需要往返于数据库,但它似乎在使用单个查询的原始查询集上提前遵循外键每个外键。只是似乎提前移动了多数据库查询命中。

但还有一些更深层次的问题,我们正在征求群众的智慧:

  • 对于具有大量外键的模型,高效查询以从外键获取属性的最佳方法是什么?
    • 缓存依赖模型是使用上述 ORM 缓存系统的唯一方法吗?
    • 或者这是超出 ORM 的标准情况,需要使用连接滚动我们自己的自定义 sql 查询以尽可能高效地获得所需的数据网格输出?

引起对缓存和外键关注的相关问题:

DB / performance: layout of django model that rarely refers to its parent more than once, Django ORM: caching and manipulating ForeignKey objects:

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    select_related() 是正确的解决方案;你错了它应该如何工作。如果您仍然在指定的 FK 中获得多个查询,我认为您没有正确使用 select_related。 Python 会话的快速日志(Studio 在此处有 django.auth.user 的 FK):

    >>> from django.db import connection
    >>> studios = Studio.objects.all().select_related('user')
    >>> for studio in studios:
    >>>     print studio.user.email
    >>>        
    email@notadomain.com
    anotheremail@notadomain.com
    >>> len(connection.queries) 
    1
    

    所以,我得到了一个 Studio 对象列表(我的测试数据库中有 2 个),并为每个 SQL 查询中的每个对象获取了用户。如果没有 select_related() 调用,则需要三个查询。

    请注意 select_related doesn't handle many-to-many relationships - 尽管我认为您可以手动查询 m2m 的中间表以遵循这些 FK 任一方向而无需额外查询,只要您可以从中间对象启动查询集.也许这就是吸引你的原因?您只指定了 FK 关系,而不是 m2ms,所以我给出了一个简单的说明。

    【讨论】:

    • 啊,是的。我的立场非常正确 - 看起来数据网格实际上忽略了提供的查询集提供的任何优化并进行自己的查找。使用您的示例对我的模型进行独立测试,使 select_related() 的好处变得一目了然。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 2020-12-23
    • 2020-11-07
    • 2014-11-27
    • 1970-01-01
    相关资源
    最近更新 更多