Django分页CPU时间随选中对象数量变化的情况
我有一个简单的数据库,大约有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 个回答
在Django中进行分页时,它会使用标准的查询集切片来获取结果,这意味着它会用到 LIMIT
和 OFFSET
。
你可以通过调用查询集的 .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