加速嵌套的 Django ORM 查询

Posted

技术标签:

【中文标题】加速嵌套的 Django ORM 查询【英文标题】:Speeding up nested Django ORM queries 【发布时间】:2017-03-21 16:37:12 【问题描述】:

我在我的 Django 项目中使用方便的Django sessions library。这允许我通过 ORM 查询处理 Session 对象。

我可以为每个 Session 对象访问的属性是:

    Column     |           Type           | Modifiers 
---------------+--------------------------+-----------
 session_key   | character varying(40)    | not null
 session_data  | text                     | not null
 expire_date   | timestamp with time zone | not null
 user_id       | integer                  | 
 user_agent    | character varying(200)   | not null
 last_activity | timestamp with time zone | not null
 ip            | inet                     | not null

user_id 来自 Django User 模型。

使用 Session 库,我需要在我的应用程序中找到当前在 Session 表中没有条目的用户数(以及他们对应的IDs)。

我通过以下方式进行了尝试:

logged_in_users = set(Session.objects.values_list('user_id',flat=True))
logged_in_users = [user_pk for user_pk in logged_in_users if user_pk is not None]
logged_out_users = set(User.objects.exclude(id__in=logged_in_users).values_list('id',flat=True))
num_logged_out = len(logged_out_users) #passed to template to display

My Session 表包含 1.7M 行,而 User 表包含 408K 行。上面的代码需要异常大量的处理时间(即几分钟),最终在生产中给我一个 500 错误(在开发中的有限数据集上正常工作)。

在解决问题之前,我觉得我还应该优化查询以更便宜地获得结果。

您认为我的代码需要哪些明显的优化?我知道我可以通过从total_users 中减去logged_in_users 的数量来找到logged_out_users 的数量。但是获取他们所有的 ID 呢?

【问题讨论】:

user_idForeignKeyUser 模型还是只是一个整数? 这是一个 FK。表定义包括Foreign-key constraints: "user_id_refs_id_898fa0fe" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED 【参考方案1】:

[更新]:在 cmets 中进行了一些讨论后,该问题询问了有关检索(和计数)已注销 Users 的 ids(即不在Session 表中有一个条目)。

所以:

# Get a (flat) list of user ids where the user entry in not null
logged_in_users = Session.objects.filter(user__isnull=False).values_list('user__id', flat=True).distinct()

# Get a (flat) list of user ids excluding the previous logged ones (thus, this list will contain the logged out users)
logged_out_users = User.objects.exclude(id__in=logged_in_users).values_list('id', flat=True).distinct()

# Count the logged out users
logged_out_users_count = logged_out_users.count()

这个怎么样:

# Fetch all user_id values from the Session table where the user ForeignKey has
# no value (thus, it's null)
null_user_ids = Session.objects.filter(user__isnull=True).values_list('user__id', flat=True)

# Count the null users
no_of_users = null_user_ids.count()

【讨论】:

已更新。这是否为您澄清?您询问了当前在会话表中没有条目的用户。这意味着user 条目是null 那不是为 fk userNone 的行过滤会话表吗?未经授权的用户使用user_id = None在会话表中创建条目 是的。嗯,不完全是None,但数据库说它是null 因此User 表中可能有用户在Session 表中根本没有条目(例如,他们已注销)。那些会被这个捕获吗? 由于他们在Session 表中没有条目,并且上面的查询“扫描”了Session 表,因此这些用户不会出现在结果中。

以上是关于加速嵌套的 Django ORM 查询的主要内容,如果未能解决你的问题,请参考以下文章

Django ORM 嵌套模型

通过 Django ORM 重命名嵌套注释字段

python 之 Django框架(orm单表查询orm多表查询聚合查询分组查询F查询 Q查询事务Django ORM执行原生SQL)

django orm 的查询条件

Django的ORM表查询

如何使用嵌套循环加速查询