Django REST框架序列化非常缓慢

10

我在使用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秒,但我无法按照所需格式获得信息,这就是我使用自定义序列化程序的原因。

是否有更快的方法来获取那么多值?也许使用另一个序列化程序?有任何想法吗?

**感谢Kevin的回答**将查询更改为:

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

这使得序列化器不必在每个结果行中命中数据库以检索外部值。


2
似乎是一个n个查询的问题... - Raphael
我的查询非常简单,它查找8个字段,其中包括一个INNER JOIN和一个WHERE条件,数据库在不到0.25秒的时间内返回数据,因此绝对不是查询或数据库的问题。 - Alex Lord Mordor
2个回答

11

查询数据库非常简单,因此数据库根本不是问题。

确保您的查询没有N+1问题。它们可能很简单,但如果有很多查询,那么将占用相当大的时间。我在这里写了很多关于修复Django REST框架中的性能问题的内容,您可以通过搜索找到很多相关信息。

有没有更快的方法来获取这些值?也许使用另一个序列化程序?有什么想法吗?

如果您的数据不经常更改,或者您可以处理任何可能的缓存问题,则可以通过向API添加一些缓存来获得巨大的收益。drf-extensions提供了许多有用的缓存mixin,如果您的问题实际上不是查询方面的问题,则可以帮助您。

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

我知道您的代码已经内置了分页功能,但是我想强调在处理大量数据时使用分页的价值。请求的性能往往是非常线性的,而且您需要检索的数据越多,检索所有数据所需的时间就越长。请注意,这里的使用分页指的是将数据分成一页一页进行检索和显示。

1
我直接在数据库(mssql)中进行了查询,只需不到0.25秒就能得到结果。我的数据每天会发生很多次变化,所以缓存并不是最好的选择,因为我可能会取到缓存数据一两次,然后数据就会再次发生变化。分页听起来不错,但在我的代码中,page 总是为 None,所以 get_pagination_serializer 没有被调用过,我该如何实现分页呢?分页是否更快? - Alex Lord Mordor
如果你只是执行主查询(Blah.objects.all()),那么你会错过与之相关的额外查询。因为你正在使用productoubicacion,所以需要进行额外的查询。所以如果你获取一个对象,需要进行三个查询。如果每个查询需要100毫秒,那就是300毫秒。如果你获取100个对象,仅在查询上就需要10秒钟。这被称为N+1查询问题,我肯定会查找它(并阅读链接的答案)。 - Kevin Brown-Silva
Django REST Framework内置分页功能: http://www.django-rest-framework.org/api-guide/pagination - Kevin Brown-Silva
你完全正确,@KevinBrown,这确实是一个N+1问题U_U。 我查看了文档,并按照你所说的更改了我的主查询,这真是神奇,我将编辑主贴以反映答案。非常感谢你,我之前对N+1问题一无所知! - Alex Lord Mordor

10
对我而言,N + 1 数据库查询并不是答案。花费了一个下午的分析才找到问题所在,但令人沮丧的是,问题竟然出现在我的序列化器中一些 DecimalField 字段上。
我的用例很简单:需要将 3000-4000 个实例进行序列化,已经执行了所有的 select_related 优化,但仍然看到了 2-3 秒的序列化时间,而不是我预期的 0.5-1.5 秒钟。经过几个小时的试错后(注释掉/取消注释字段),当我注释掉所有 DecimalField 后,运行时间大幅下降(50%)。
对我来说,解决方案是将我的 DecimalField 更改为 FloatField。当然,这样做会损失一些精度,但对于我的目的来说是可以接受的。

我找不到其他任何提示为什么DecimalField会如此缓慢(我知道这是两年前的事了)。除了更改模型字段定义,还有其他解决方案吗? - JohnO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接