首页 > 解决方案 > 如何诊断 django 渲染速度问题?

问题描述

我一直在试图弄清楚是什么减慢了我的请求,我所能弄清楚的是响应的呈现速度有所放缓。

我对查询、序列化程序和 JSON 渲染进行了一些测试,如下所示:

client = Client.objects.get(user__username="pugdog")
start = parse("2021-09-01T00:00:00-04:00")
end = parse("2021-10-01T00:00:00-04:00")
db_test_start_time = time()
events = Event.objects.filter(client=client, start__gt=start, end__lt=end)
list(events)
db_test_end_time = time()

factory = APIRequestFactory()
request = factory.get('/')
serializer_context = {'request': Request(request), }
output = []
for event in events:
    serializer = EventReadSerializer(instance=event, context=serializer_context)
    output.append(serializer.data)
renderer = JSONRenderer()
json_output = renderer.render(output)
serializer_test_end_time = time()
print(f"query speed: {db_test_end_time - db_test_start_time}")
print(f"serializer speed: {serializer_test_end_time - db_test_end_time}")
print(f"Total time: {serializer_test_end_time - db_test_start_time}")

输出是:

query speed: 0.006450176239013672
serializer speed: 2.926229238510132
Total time: 2.9326794147491455

这是处理查询并将其转换为 JSON 所需的时间,但是当我尝试使用 API REST 客户端“Insomnia”提取这些数据时,大约需要 43.8 秒,这意味着我在某处出现了大幅减速。我正在开发的同一台机器上运行 Insomnia,所以应该没有网络问题。

这个测试是在我用 100 个事件对象填充数据库时进行的。我决定用不同数量的对象进行测试,看看数据量和响应时间之间的关系是否是线性的,这可能有助于找出问题,例如我的授权/身份验证是否有问题,它与特定查询无关。当我做 20 个事件时,在 Insomnia 中获得响应所需的时间大约是 9 秒,而上面的测试大约是 0.6 秒。在这两个方面,这几乎与速度呈线性关系。

我的大部分 API 响应速度都相当快,但是这个特定的部分非常慢。我还能测试什么来弄清楚是什么让事情变慢了?

编辑 这里是 EventReadSerializer

class EventReadSerializer(serializers.ModelSerializer):
    id = serializers.ReadOnlyField()
    resourceIds = ResourceIdsSerializer(source="resources", many=True)
    rrule = RRuleReadSerializer(many=True)
    customer = SimplifiedCustomerSerializer(many=False)

    class Meta:
        model = Event
        fields = [
            "id",
            "all_day",
            "title",
            "start",
            "end",
            "background_color",
            "border_color",
            "text_color",
            "duration",
            "resourceIds",
            "rrule",
            "customer",
        ]

这是渲染后伪造事件的示例:

{
    "id": 1,
    "all_day": false,
    "title": "Suddenly this mouth take.",
    "start": "2021-09-26T11:02:25Z",
    "end": "2021-09-26T12:02:25Z",
    "background_color": null,
    "border_color": null,
    "text_color": null,
    "duration": "01:00:00",
    "resourceIds": [
      2
    ],
    "rrule": [],
    "customer": {
      "id": 1,
      "user": {
        "first_name": "Craig",
        "last_name": "Johnson"
      },
      "custom_data": [],
      "notes": [],
      "first_name": "Craig",
      "last_name": "Johnson"
    }
  }

这实际上是原始版本的精简版EventSerializer,它返回了更多信息。它减少到只有我正在使用的日历所需的数据。这对最终渲染速度没有明显差异。我还以类似的方式将我的 CustomerSerializer 更改为 SimplifiedCustomerSerializer,以减少返回的数据量。

无论如何,考虑到当我运行脚本来测试速度时,它运行序列化程序的时间大约是呈现页面的 1/15。

我尝试过的另一件事是删除所有我能看到的中间件,看看事情是如何变化的。虽然我确实看到了速度上的小幅提升,但它将呈现页面所需的时间减少了大约 2 秒至 42.8 秒,与运行查询和序列化数据所需的 2.9 秒相比,这似乎仍然是完全不可接受的.

标签: pythondjango

解决方案


您的序列化器不仅序列化 的数据instance,而且还序列化相关对象的数据。这意味着序列化器将对相关模型进行查询。

我们可以通过使用.select_related(…)[Django-doc].prefetch_related(…)[Django-doc]来提高性能:

events = Event.objects.filter(
    client=client, start__gt=start, end__lt=end
).prefetch_related(
    'resources', 'rrule'
).select_related('customer', 'customer__user')

可能Customer还会进行查询以查找custom_datanotes。如果您共享相应的Customer模型和SimplifiedCustomerSerializer序列化程序,我们可能也可以减少对这些项目的查询次数。


推荐阅读