优化 Django Rest ORM 查询

Optimize Django Rest ORM queries

我是 React 前端,Django 后端(用作 REST 后端)。 我继承了这个应用程序,它使用许多模型和序列化加载所有用户数据。它加载非常慢。 它使用过滤器查询单个成员,然后将其传递给序列化程序:

found_account = Accounts.objects.get(id='customer_id')
AccountDetailsSerializer(member, context={'request': request}).data

然后就是各种嵌套的Serializers:

AccountDetailsSerializers(serializers.ModelSerializer):
   Invoices = InvoiceSerializer(many=True)
   Orders = OrderSerializer(many=True)
   ....

通过查看日志,看起来 ORM 发出了很多查询,这太疯狂了,对于某些端点,我们最终会遇到 50 - 60 个查询。

  1. 我应该尝试研究使用 select_related 和预取还是你会跳过所有这些并尝试编写一个 sql 查询来进行多个连接并获取所有数据立即为 json?

  2. 当我传入单个对象(get 的结果)而不是序列化程序的查询集时,如何定义预取/select_related?

  3. 一些数据库实体在它们之间没有链接,这意味着不是 fk 或 manytomany 关系,只是持有一个具有另一个 id 的字段,但这种关系没有在数据库中强制执行?这对我来说是个问题吗?这是否再次意味着我应该跳过 select_related 方法并写一个客户 sql 来获取?

  4. 您建议如何对这种查询噩梦进行性能调优?

我建议先看看 prefetch_related 会产生什么效果。它会对加载时间产生重大影响,并且实现起来相当简单。按照你上面的例子,这样的事情可以单独显着减少加载时间:

AccountDetailsSerializers(serializers.ModelSerializer):

    class Meta:
        model = AccountDetails
        fields = (
            'invoices',
            'orders',
        )

    invoices = serializers.SerializerMethodField()
    orders = serializers.SerializerMethodField()

    def get_invoices(self, obj):
        qs = obj.invoices.all()\
            .prefetch_related('invoice_sub_object_1')\
            .prefetch_related('invoice_sub_object_2')
        return InvoiceSerializer(qs, many=True, read_only=True).data

    def get_orders(self, obj):
        qs = obj.orders.all()\
            .prefetch_related('orders_sub_object_1')\
            .prefetch_related('orders_sub_object_2')
        return OrderSerializer(qs, many=True, read_only=True).data 

至于你的架构问题,我认为还有很多其他因素会影响你是否应该重构代码库以及重构代码库的程度。但总的来说,如果您与 Django 和 DRF 结合,如果您能接受这些框架的习惯用法和模式,而不是试图通过您自己的修复来购买它们,那么您将拥有更好的开发人员体验。

不详细查看代码(和分析结果)就没有灵丹妙药。

唯一不费吹灰之力的事情就是在模型和数据库中加强关系。这可以防止大量错误,鼓励使用标准化、高性能的访问(而不是当场编造 SQL,后者通常很可能是错误的 缓慢) 并使您的代码更短且更易读。

除此之外,50-60 个查询可能很多(如果您可以用一两个查询完成相同的工作),也可以恰到好处 - 这取决于您用它们实现的目标。

prefetch_relatedselect_related 的使用很重要,是的——但前提是使用正确;否则它会减慢你的速度而不是加快你的速度。

如果您需要数据,嵌套序列化程序是正确的方法——但如果您希望它们更快,则需要在视图集中正确设置查询集。

为慢速视图的主要部分计时,检查发送的 SQL 查询并检查您是否真的需要返回的所有数据。

然后你可以看看痛处,在重要的地方争取时间。通过完整的代码示例询问有关 SO 的具体问题也可以让您更快。


如果您只有一个顶级对象,您可以改进@jensmtg 提供的方法,在该级别执行您需要的所有预取,然后仅使用 ModelSerializers (不是 SerializerMethodFields) 访问预取对象。查看允许嵌套预取的 Prefetch 对象。

但要注意 prefetch_related 不是免费的,它在 Python 中涉及相当多的处理;您最好使用带有 values()values_list.

的平面(类数据库视图)连接查询