Django REST Framework 序列化极慢

Posted

技术标签:

【中文标题】Django REST Framework 序列化极慢【英文标题】:Django REST Framework Serialize extremely slow 【发布时间】:2015-01-12 04:23:51 【问题描述】:

我在 Python 2.7 和 Django 1.7.1 中使用 django-restframework 我有一个 API 可以返回从数据库中获取的一些特定值,它使用这样的自定义序列化器:

class Inventarioserializer(serializers.ModelSerializer):
    item = serializers.RelatedField(source='producto.item')
    ubicacion = serializers.RelatedField(source='ubicacion.nombre')
    class Meta:
        model = Inventario
        fields = ('epc','item','cantidad','ubicacion')

我的 API 视图是这样调用的:

class ItemEnInventarioViewSet(InventarioListModelMixin, viewsets.ModelViewSet):
    serializer_class = InventarioSerializer
    renderer_classes = (UnicodeJSONRenderer,)

我的 ListModelMixin 是这样的:

class InventarioListModelMixin(object):
    def list(self, request, *args, **kwargs):
        item = request.QUERY_PARAMS.get('item', None)
        inventario = Inventario.objects.filter(producto__item = item)
        if inventario.count() == 0:
            return HttpResponse(u"El item %s no se encuentra en el inventario" % item,status=400)
        self.object_list = inventario
        # Switch between paginated or standard style responses
        page = self.paginate_queryset(self.object_list)
        if page is not None:
            serializer = self.get_pagination_serializer(page)
        else:
            serializer = self.get_serializer(self.object_list, many=True) <<--THIS IS THE PROBLEM
        return Response(serializer.data)

它工作正常,但是当我尝试从数据库中获取大约 1000 个或更多条目时,序列化程序使其非常慢,大约 25 到 35 秒。

对数据库的查询非常简单,所以数据库根本不是问题。

如果我用这个函数“data = serializers.serialize('json', myQuerySet)”序列化查询集,最多需要 3 秒,但我没有得到我想要的信息,这就是我使用自定义序列化程序的原因

有没有最快的方法来获取这么多值?也许与另一个序列化器?有什么想法吗?

**回答感谢凯文** 将查询更改为:

inventario = Inventario.objects.select_related('producto__item','ubicacion__nombre').filter(producto__item = item)

...使序列化程序不会在每个结果行都访问数据库来检索外部值。

【问题讨论】:

看起来像一个查询问题... 我的查询非常非常简单,查找8个字段,1个INNER JOIN和1个WHERE,数据库不到0.25秒就返回数据,所以肯定不是query或者db 【参考方案1】:

对我来说,N + 1 个数据库查询并不是答案。花了一个下午的时间进行分析,但在这样做之后,令人沮丧的是,我的序列化程序中有几个DecimalField 字段。

我的用例很简单:需要序列化 ​​3000-4000 个实例。所有select_related 优化都已执行,但是我仍然看到2-3 秒的序列化时间,而不是我预期的0.5-1.5 秒。经过几个小时的反复试验(注释掉/取消注释字段),当我将所有的 DecimalField 注释掉时,我发现运行时大幅下降(50%)。

对我来说,解决方案是将我的 DecimalField 更改为 FloatField。当然,这样做是以牺牲精度为代价的,但就我的目的而言,这很好。

【讨论】:

我找不到任何其他说明 DecimalField 为何如此缓慢的信息(我知道这是两年前的事了)。除了更改模型字段定义之外,有没有不同的解决方案?【参考方案2】:

对数据库的查询非常简单,所以数据库根本不是问题。

确保您的查询中没有 N+1 issue。它们可能很简单,但如果它们有很多,那么它将占用相当多的时间。我在这里写了很多关于fixing performance issues in Django REST Framework 的文章,你可以通过搜索找到很多关于它的内容。

是否有最快的方法来获取该数量的值?也许与另一个序列化器?有什么想法吗?

如果您的数据不经常更改,或者您可以处理任何可能的缓存问题,您可能会从向 API 添加一些缓存中受益匪浅。 drf-extensions 提供了很多有用的缓存 mixin,如果您的问题实际上与查询无关,它们可能会对您有所帮助。

当我尝试从数据库中获取大约 1000 个或更多条目时

我了解您的代码已内置分页,但我想强调在处理大量数据时使用分页 的价值。请求的性能往往非常线性,您需要检索的数据越多,检索所有数据所需的时间就越长。

【讨论】:

我直接在数据库 (mssql) 中进行了查询,给我的值不到 0.25 秒,我的数据在一天内改变了很多次,所以缓存不是最好的方法,因为我可以将缓存数据再取一两次,然后再次更改。分页听起来不错,但在我的代码中 page 总是 None 所以 get_pagination_serializer 永远不会被触及,我怎样才能进行分页?它更快吗? 如果您只是在执行主查询 (Blah.objects.all()),那么您将缺少关系所需的额外查询。因为您使用的是productoubicacion,所以需要对它们进行额外的查询。因此,如果您要获取一个对象,则需要进行三个查询。如果每个查询需要 100 毫秒,那就是 300 毫秒。如果您获得 100 个对象,那么仅在查询中就需要 10 秒。这称为 N+1 查询问题,我一定会查找(并阅读链接的答案)。 Django REST 框架内置分页:django-rest-framework.org/api-guide/pagination 你完全正确@KevinBrown,有一个 N+1 问题 U_U。我正在查看文档,并按照您所说的更改我的主要查询,这很神奇,我将编辑主要帖子以反映答案。非常感谢,我不知道 N+1 个问题!

以上是关于Django REST Framework 序列化极慢的主要内容,如果未能解决你的问题,请参考以下文章

python django-rest-framework 3.3.3 更新嵌套序列化程序

django-rest-framework、多表模型继承、ModelSerializers 和嵌套序列化器

Django.rest_framework:如何序列化一对多?

Django 序列化器与 rest_framework 序列化器

Django-Rest-Framework 中的序列化程序问题

django.core 序列化器和 Django Rest Framework 序列化器之间的区别