Django QuerySet 与原始 SQL 性能注意事项

Posted

技术标签:

【中文标题】Django QuerySet 与原始 SQL 性能注意事项【英文标题】:Django QuerySet vs. raw SQL performance considerations 【发布时间】:2017-11-03 21:56:09 【问题描述】:

我正在学习 Django 及其 ORM 数据访问方法,我对某些东西感到好奇。在一个特定的端点中,我进行了许多数据库调用(对 Postgres) - 下面是一个示例:

projects = Project.objects\
            .filter(Q(first_appointment_scheduled=True) | (Q(active=True) & Q(phase=ProjectPhase.meet.value)))\
            .select_related('customer__first_name', 'customer__last_name',
                            'lead_designer__user__first_name', 'lead_designer__user__last_name')\
            .values('id')\
            .annotate(project=F('name'),
                      buyer=Concat(F('customer__first_name'), Value(' '), F('customer__last_name')),
                      designer=Concat(F('lead_designer__user__first_name'), Value(' '), F('lead_designer__user__last_name')),
                      created=F('created_at'),
                      meeting=F('first_appointment_date'))\
            .order_by('id')[:QUERY_SIZE]

如您所见,这不是一个小查询 - 我正在提取大量特定的相关数据并进行一些字符串操作。我比较关心性能,所以我尽我所能通过使用select_related()values() 来提高效率,只得到我需要的东西。

我的问题是,从概念上和广义上讲,在什么时候使用参数化 SQL 而不是使用 ORM 编写查询会变得更快(因为 ORM 必须首先“翻译”上述“混乱” )?我应该在多大的查询复杂度级别切换到原始 SQL?

任何见解都会有所帮助。谢谢!

【问题讨论】:

【参考方案1】:

我的问题是,从概念上和广义上讲,在什么时候 使用参数化 SQL 编写查询是否会变得更快 而不是使用 ORM(因为 ORM 必须首先“翻译” 上面的“混乱”)?

如果你问的是性能,从不。

与实际执行该查询所需的时间相比,将 ORM 查询转换为 SQL 所需的时间将非常短。脑细胞无可替代,服务器便宜。

如果您确实有性能问题,首先要看的是模型中的索引。尝试打印出 ORM 生成的每个查询,并在您的 psql 控制台中通过添加 EXPLAIN ANALYSE 前缀来运行它们。

您也可以使用 django-debug-toolbar 来自动执行此操作。事实上 django-debug 工具栏是寻找瓶颈的重要工具。您会惊讶地发现您错过了一个简单的select_related 的频率以及这如何导致执行数百个额外的查询。

我应该切换到什么近似级别的查询复杂性 原始 SQL?

如果您询问编码的难易程度,这取决于。

如果查询非常难以使用 ORM 编写并且不可读,是的,那么使用原始查询非常好。例如,具有多个聚合、使用公用表表达式、多个连接等的查询有时可能难以编写为 ORM 查询,在这种情况下,如果您对原始 sql 感到满意,那么这样编写就可以了。

【讨论】:

【参考方案2】:

同意@e4c5 所说的。

用于将 ORM 查询转换为原始 SQL 查询的附加转换层会影响性能。

但是,这种效果将取决于您的查询有多复杂?

当您使用 ORM 时,您可以通过增加应用程序中的处理来控制 DB 上的负载。此外,这提供了将结果缓存在应用程序本身中的机会。

最后,这完全取决于您的架构、查询的复杂程度以及您如何扩展数据库(索引、副本等)

更多信息请阅读here

【讨论】:

好文章。我想我可以打印出那个图表。 很高兴您喜欢它,如果您觉得有用,请点赞我的回答.. 谢谢 '用于将 ORM 查询转换为原始 SQL 查询的附加翻译层将影响性能' 如果您进行基准测试,您会发现完全相反。影响可以忽略不计。

以上是关于Django QuerySet 与原始 SQL 性能注意事项的主要内容,如果未能解决你的问题,请参考以下文章

Django QuerySet 与原始查询性能

如何向 Django QuerySet 添加附加列

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

Django 1.9:为 QuerySet 创建复杂的自定义过滤器方法

Django 1.2 等价于 QuerySet.query.as_sql()

在Django REST List API视图中对原始SQL查询进行分页的最佳方法是什么?