Django - 生产环境中特定视图的等待时间过长

3 投票
1 回答
2651 浏览
提问于 2025-04-16 17:54

我已经为这个问题纠结了至少两天。这个视图会生成一个页面,显示某个用户或电话分机拨打的所有电话。没什么特别的,就是一长页,最多1000行。

这个视图函数会从网址中接收一些参数,以决定在页面上显示什么。有两种情况,一种是网址中传入“extension=xxxxxx”,另一种是传入“user=xxxx”:

if request.GET.get("extension", None):
    # extension_query expects only one Extension object
    extension_query = Extension.objects.filter(number=request.GET["extension"])
    ... # Here I do some conditionals to match the right Extension object.
elif request.GET.get("user", None):
    ... # Simple stuff, nothing to significant.
# at the end I call render_to_response normally

编辑 - 这是调用我自定义的render_to_response的代码片段:
编辑2 - 在John C的建议下,我强制评估查询集后,加载时间从1.6分钟减少到14秒:现在我在将调用传递给自定义的render_to_response之前,将其转换为list():

if (request.GET.get("format", None) == "screen"
    or request.GET.get("print", False)):
    ctx = dict(calls=list(calls), client=client, extension=extension,
               no_owner_extension=no_owner_extension,
               start=start_date, end=end_date, tab="clients",
               owner=user)
    return finish_request(
        request, "reports/exporting_by_call_type.html", ctx)

这是我自定义的render_to_response:

def finish_request(request, template, context):
    if "print" in request.GET or "print" in request.POST:
        context.update(printing=True)
    return render_to_response(
        template, RequestContext(request, context))

在我的开发机器上,根据Chrome的审计,处理这个视图在现实数据场景下需要3-4秒,适用于两种情况。在生产环境中,当网址中传入user参数时,加载视图也需要3-4秒,但当传入extension时,却需要2分钟!

编辑:需要说明的是,传入userextension在网址中并不会改变最终渲染的页面,除了顶部的一行,我会指明这个分机号码属于谁。其余的数据完全一样。

我对生成这个视图的代码中的每个小块进行了性能分析。我测量了在我的生产代码中render_to_response的时间(0.05秒)。我删除了模板中调用extension的部分,但没有成功。我还使用了django_debug_toolbar来查看每条SQL语句的执行情况,最多查询也只需要2秒。

我还要补充的是,我在生产设置中使用mod_wsgi,debug=False...尽管加载时间需要2分钟,YSlow还是给这个页面打了94分。

有没有人能给点建议?

1 个回答

2

我觉得这段代码看起来没什么明显的问题。我有一个问题,你是希望正好匹配到 一个 对象吗?如果是这样,可以试试这段代码:

getData = request.GET.copy() # optional, I like my own copy.
if 'extension' in getData:
    ext = getData['extension']
    extObj = Extension.objects.get(number__exact=ext) # double-underline
# elif...

注意,这样会得到一个扩展 对象,而不是一个查询集。如果你有多个扩展,那就需要用 filter,不过我还是建议使用 exact,并且把从字典中读取 ext 的操作放到 filter 语句外面。

更新(根据我的评论) - 尝试强制立即评估查询集,使用 list() 来看看这对 render_to_response 有没有影响。

更新 2 - 既然 确实有影响 - 我觉得可能发生了什么。因为 Django 的查询集使用的是懒惰评估,当模板最终执行时,它调用的是一个迭代器,而不是一个现成的列表。也许函数调用太多了,每次调用迭代器获取新值时,都会产生很多不必要的开销。或者可能是个bug。:)

撰写回答