首页 > 解决方案 > 反向 django 外键查找的复杂性

问题描述

假设我有这样的模型:

    class Post(models.Model):
         name = models.CharField(max_length=25, unique=True)

    class Picture(models.Model):
         post = models.ForeignKey(to=Post, ondelete=models.CASCADE)
         image = models.ImageField()

现在假设我做了这样的查询:

    p = Post.objects.get(name=foo)
    images = p.picture_set.all()

现在第一个查询显然会搜索所有帖子以获取具有 name 的帖子foo
但我想知道第二个。当我进入第一个查询时,它是否搜索Picture数据库中的所有表以查找所有具有post=p或可用信息的图片? 因为如果是前者,那么我担心可扩展性问题。p

标签: djangodjango-querysetdjango-related-manager

解决方案


但我想知道第二个。它是否搜索数据库中的所有图片表以查找所有具有 post=p 的图片,或者当我在第一个查询中获得 p 时信息是否可用?

简短的回答:默认情况下 aForeignKey添加一个索引,使检索非常快(值的数量是对数的,反向记录的数量是线性的)。

这取决于数据库是否在ForeignKey. 默认情况下,Django构建一个索引。这意味着它不仅存储表的行,而且还存储允许快速查找具有特定值的所有行的数据结构。

索引的实现可以依赖于数据库。在 MySQL 中,它默认使用 a BTREE,这意味着对于一个值的查找,大约需要O(log n)来获取集合,并且O(k)k具有该外键的项目数来检索所有. 但是存在其他索引结构,例如某种哈希表,甚至允许(稍微)更快的查找,尽管例如哈希表在检索ForeignKey小于给定数字的所有元素时效率不高。

您还可以在其他列上添加索引,例如:

class Post(models.Model):
    name = models.CharField(max_length=25, db_index=True, unique=True)

因此,现在检索Post具有给定名称的所有对象也会运行得更快。

使用索引当然不是“免费的”:这意味着每次插入或删除记录时,索引也需要更改(通常这也需要O(log n))。如果您通过更改外键的值来更新记录,则该索引也需要更改。因此索引提供了显着的加速,但应该只将索引放在经常执行查找的列上,否则“维护”索引的成本可能大于加速查找过程的收益。


推荐阅读