优化Django Rest ORM查询

问题描述 投票:0回答:2

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

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和prefetch,或者您是否会跳过所有这些并尝试编写一个sql查询来执行多个连接并一次获取所有数据作为json?
  2. 当我传入单个对象(get的结果)而不是序列化程序的查询集时,如何定义prefetch / select_related?
  3. 一些数据库实体之间没有链接,这意味着不是fk或manytomany关系,只是持有一个具有另一个id的字段,但这种关系在数据库中没有强制执行?这对我来说是个问题吗?这又是否意味着我应该跳过select_related方法并编写客户sql来获取?
  4. 你会如何建议在这个噩梦般的查询中进行性能调优?
django performance django-models django-rest-framework query-performance
2个回答
1
投票

没有详细查看代码(和分析结果)就没有银弹。

唯一明智的做法是在模型和数据库中强制执行关系。这可以防止出现大量的错误,鼓励使用标准化的高性能访问(而不是在现场调制SQL,而这通常是错误和缓慢的),并使您的代码更短,更易读。

除此之外,50-60个查询可以是很多(如果你可以做一两个相同的工作)或者它可能是正确的 - 它取决于你用它们实现了什么。

使用prefetch_relatedselect_related很重要,是的 - 但只有正确使用才能使用;否则它会减慢你的速度,而不是加速你。

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

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

然后你可以看看疼痛部位,并在重要的地方获得时间。通过完整的代码示例询问有关SO的具体问题也可以帮助您快速实现。


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

但要注意prefetch_related不是免费的,它涉及Python中的一些处理;你可能最好使用values()values_list进行平面(类似db-view)的连接查询。


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结婚,如果你能够接受这些框架的习语和模式,而不是试图用自己的修复程序购买它们,那么你将获得更好的开发者体验。

© www.soinside.com 2019 - 2024. All rights reserved.