为啥通过 django QuerySet 进行查询比在 Django 中使用游标慢得多?

Posted

技术标签:

【中文标题】为啥通过 django QuerySet 进行查询比在 Django 中使用游标慢得多?【英文标题】:Why is quering via a django QuerySet much slower than just using a cursor in Django?为什么通过 django QuerySet 进行查询比在 Django 中使用游标慢得多? 【发布时间】:2013-07-10 06:41:03 【问题描述】:

简单版:

为什么 Django 中的原始 SQL 比 QuerySet 接口更高效?

一些细节:

我有一个从 PostgreSQL 数据库返回 ~ 700,000(可能更多)行的查询。每行包含一些双精度值、一些字符串和一些整数。所以是一个中等复杂的回报。

形式简单(过于简单的例子):

SELECT (a,b,c) FROM table WHERE d=something AND e=somethings ORDER BY a;

当我使用模型接口和 .filter() 进行查询时,查询的执行大约需要 30 秒。这是不可接受的。

我已尝试使用所有建议的方法。 (迭代器、内存高效迭代器等...)

但是,当我在 Django 中使用 connection.cursor ... 和 fetchall 执行完全相同的查询时,查询的执行时间会下降到大约 5 秒。

使用 django 模型接口会产生哪些开销来解释这种显着的性能差异?

更新:

Django 查询集代码:

c_layer_points = models.layer_points.objects.filter(location_id__location_name=region,season_id__season_name=season,line_path_id=c_line_path.pk,radar_id=c_radar.pk,gps_time__gte=start_gps,gps_time__lte=stop_gps).order_by('gps_time').values_list('gps_time','twtt','pick_type','quality','layer_id')

快速版本中完全相同的查询:

# OPEN a cursor
cursor = connection.cursor()

# EXECUTE the query
cursor.execute(query)
transaction.commit_unless_managed()

# FETCH all the rows
rows = cursor.fetchall()

其中 'query' 是从 Queryset 生成的 connection.queries 代码的精确字符串表示形式。

更新 2:

计时是使用line_profiler 完成的,并采用从初始查询到返回的元组列表的时间总和(两个选项的返回完全相同)。我还测试了原始查询直接在数据库上花费的时间(两者完全相同)。时间上的差异是通过每种方法从 python 完成的。

【问题讨论】:

你能给我们你使用的django ORM代码吗?因为在不知道你到底做了什么的情况下,我们确实无法判断是 django 的错还是你的错。 @danihp:是的,这可以解释糟糕的结果。 您如何衡量查询持续时间?额外的时间是否可以将数据编组到 python 对象/django 模型中? 我会投票结束这个问题,但“不是一个真正的问题”目前不是关闭它的选项。 添加了测量查询时间的更新。 @danihp 这不是一个真正的问题吗?我在问为什么在执行相同任务时一种方法比另一种方法慢得多。 这正是我所做的。我从 c_layer_points 获取生成的查询,并在这两种方法中测试了 EXACT 字符串。我直接通过 psql 接口对其进行了测试,然后使用两种描述的方法通过 django 对其进行了测试。差异在于每种方法的时间 - 已知的数据库时间。 【参考方案1】:

如果您对问题的更新中的两个代码段进行计时,那么是的,不同之处在于 django 将 DB 查询的结果编组为 700,000 个 python 对象(即,它调用了 object.__init__() 700,000 次)。

使用原始 sql 进行查询没有任何问题。这种情况可能会被建议,具体取决于您对信息的处理方式。

也就是说……你需要 700,000 个对象作为响应吗? dict 中的 700,000 个项目会代替吗(这就是原始 sql 查询返回的内容)?或者您可以通过分页或查询集切片来限制返回的行数?

【讨论】:

被绑定到响应中的所有对象。远程 MATLAB 客户端正在联系数据库以获取数据,然后将实际数据用于某些事情。使用原始 sql,我们将时间缩短到合理的程度。 如果数据是为远程客户端收集的,那么您肯定不需要 python 对象。普通的 dict 完全可以满足您的需求。 我不太清楚你所说的“普通字典”是什么意思。如果您指的是原始查询的返回,那么是的,您是正确的,这就是我们选择的路径。不需要 Django 对象。如果不是,你指的是什么? 我的意思是一个包含键和值字符串的字典。其实我之前误导过你:Model.objects.raw() 返回一个对象的查询集。 docs.djangoproject.com/en/1.5/topics/db/sql/… 以字符串形式返回 DB 内容的是 docs.djangoproject.com/en/1.5/topics/db/sql/…。对困惑感到抱歉。还有迟到的回复。

以上是关于为啥通过 django QuerySet 进行查询比在 Django 中使用游标慢得多?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效的遍历django的QuerySet

Pycharm开发Django项目QuerySet API教程

Django——惰性机制

Django查询集QuerySet及两大特性

查询集 QuerySet

对queryset的理解