优化Django Rest ORM查询

时间:2019-03-24 18:05:19

标签: django performance django-models django-rest-framework query-performance

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

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

然后有很多嵌套的序列化器:

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

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

  1. 我应该尝试使用select_related和prefetch进行调查,还是跳过所有这些,而只是尝试编写一个sql查询以执行多个联接并一次以json形式获取所有数据?

    < / li>
  2. 当我传递单个对象(获取结果),而不是将查询集传递给序列化程序时,如何定义预取/ select_related?

  3. 某些数据库实体之间没有链接,这意味着没有fk或许多关系,仅保存一个具有另一个ID的字段,但是该关系在数据库中未强制执行吗?这对我来说会是个问题吗?

  4. 这是否再次意味着我应该跳过与select_related有关的方法,并编写一条客户sql以进行提取?

  5. 您如何建议在这个噩梦般的查询中进行性能调整?

2 个答案:

答案 0 :(得分:3)

我建议一开始看看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结婚,那么如果您能够接受这些框架的习惯用法和模式,而不是尝试使用自己的修补程序购买它们,就会有更好的开发人员经验。

答案 1 :(得分:1)

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

唯一的问题就是在模型和数据库中强制执行关系。这样可以防止大量的错误,鼓励使用标准化的高性能访问方式(而不是在现场编写SQL,而时常会出现错误)并且使您的代码都更短而且更具可读性。

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

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

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

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

然后,您可以查看痛点,并在重要的时候抽出时间。使用完整的代码示例在SO上提出具体问题,也可以使您走得更快。


如果只有一个顶级对象,则可以改进@jensmtg提供的方法,在该级别进行所有所需的预取,然后仅使用ModelSerializer(对于{ {1}}个访问预取对象的对象。查看允许嵌套预取的Prefetch对象。

但是请注意,SerializerMethodField不是免费的,它涉及到Python中的许多处理。您最好使用带有values()prefetch_related的平面(类似于数据库视图的)联接查询。