Django分页CPU时间随选中对象数量变化的情况

2 投票
1 回答
1024 浏览
提问于 2025-04-16 06:24

我有一个简单的数据库,大约有3900条记录。我正在使用一个通用视图(django.views.generic.list_detail.object_list)和django的分页功能(通过paginate_by)来浏览这些数据,但有些查询速度非常慢。

奇怪的是,尽管每页只显示50个对象,但渲染时间大致与选择的对象数量成线性关系(而且我没有对对象进行任何排序)。例如,当我查询大约3900、1800、900和54个对象时,分别需要大约8500毫秒、4000毫秒、2500毫秒和800毫秒的CPU时间(使用django-debug-toolbar监测),而SQL查询的时间仅为大约50毫秒、40毫秒、35毫秒和30毫秒,所有页面都正好有50个对象。我已经按照django优化页面的建议,尽量减少SQL查询的数量,使用了select_related。

使用性能分析中间件,我发现大部分时间都花在了数据库操作上:

         735924 function calls (702255 primitive calls) in 11.950 CPU seconds

   Ordered by: internal time, call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
35546/3976    4.118    0.000    9.585    0.002 /usr/local/lib/python2.6/dist-packages/django/db/models/query.py:1120(get_cached_row)
    30174    3.589    0.000    3.991    0.000 /usr/local/lib/python2.6/dist-packages/django/db/models/base.py:250(__init__)

 ---- By file ----

      tottime
47.0%   3.669 /usr/local/lib/python2.6/dist-packages/django/db/models/base.py
 7.7%   0.601 /usr/local/lib/python2.6/dist-packages/django/db/models/options.py
 6.8%   0.531 /usr/local/lib/python2.6/dist-packages/django/db/models/query_utils.py
 6.6%   0.519 /usr/local/lib/python2.6/dist-packages/django/db/backends/sqlite3/base.py
 6.4%   0.496 /usr/local/lib/python2.6/dist-packages/django/db/models/sql/compiler.py
 5.0%   0.387 /usr/local/lib/python2.6/dist-packages/django/db/models/fields/__init__.py
 3.1%   0.244 /usr/local/lib/python2.6/dist-packages/django/db/backends/util.py
 2.9%   0.225 /usr/local/lib/python2.6/dist-packages/django/db/backends/__init__.py
 2.7%   0.213 /usr/local/lib/python2.6/dist-packages/django/db/models/query.py
 2.2%   0.171 /usr/local/lib/python2.6/dist-packages/django/dispatch/dispatcher.py
 1.7%   0.136 /usr/local/lib/python2.6/dist-packages/django/template/__init__.py
 1.7%   0.131 /usr/local/lib/python2.6/dist-packages/django/utils/datastructures.py
 1.1%   0.088 /usr/lib/python2.6/posixpath.py
 0.8%   0.066 /usr/local/lib/python2.6/dist-packages/django/db/utils.py
...
 ---- By group ---

      tottime
89.5%   6.988 /usr/local/lib/python2.6/dist-packages/django/db
 3.6%   0.279 /usr/local/lib/python2.6/dist-packages/django/utils
...

我能理解为什么SQL查询的时间会随着选择的条目数量增加而增加。然而,我不明白为什么其余的CPU时间会受到影响。这让我感到很困惑,我想知道是否有人能给我一些调试或分析的建议。

我使用的是django-1.2.3,sqlite,python2.6,apache2-prefork(虽然切换到mpm-worker并没有显著改变情况)。任何建议都会非常感谢。内存使用似乎也不是问题(机器有2GB内存,free命令显示只使用了300MB,另外还有600MB的缓存),而且数据库和机器在同一台服务器上。

我发现了我的错误。我发现了我的错误。我检查了原始查询集的长度,看它是否为1(如果是,就转到object_detail)。这导致我评估了整个查询集(根据django-debug-toolbar,这仍然只花了5毫秒),但显著减慢了所有操作。

基本上,我写了类似这样的愚蠢代码:

    if len(queryset) == 1:                                 
        return HttpResponseRedirect( fwd to object_detail url ...)
    return object_list(request, queryset=queryset, paginate_by=  ...)

这导致评估了整个查询,而不是分页后的查询。

1 个回答

3

在Django中进行分页时,它会使用标准的查询集切片来获取结果,这意味着它会用到 LIMITOFFSET

你可以通过调用查询集的 .query 属性上的 str() 来查看ORM生成的SQL语句:

    print MyModel.objects.all().query
    print MyModel.objects.all()[50:100].query

然后你可以让sqlite对这个查询进行 EXPLAIN,看看数据库到底在做什么。我猜测你是在某个没有索引的字段上进行排序。 EXPLAIN QUERY PLAN 会告诉你根据sqlite的文档,哪些索引本来会被使用,文档地址是 http://www.sqlite.org/lang_explain.html

撰写回答