Skip to main content
 首页 » 编程设计

python之Django Paginate CPU Time scaling with number of selected objects not displayed objects

2024年09月03日33zdz8207

我有一个包含大约 3900 个条目的简单数据库,我使用一个通用 View (django.views.generic.list_detail.object_list) 及其 django-pagination(通过 paginate_by)来浏览数据库中的数据,但是一些查询非常慢。

奇怪的是,尽管每页只显示 50 个对象,渲染时间与选择的对象数量大致呈线性关系(而且我没有对对象进行任何排序)。例如,如果我使用 ~3900、~1800、~900、~54 个选定对象进行查询,它分别需要 ~8500 ms、~4000 ms、~2500 ms、~800 ms 的 CPU 时间(使用 django-debug-toolbar)而 SQL 只用了 ~50 ms、~40 ms、~35 ms、~30 ms,而所有页面都恰好有 50 个对象。我已按照 django optimization page 中的建议使用 select_related 最小化了 SQL 查询的数量。 .

使用 profiling middleware长时间查询的绝大部分时间都花在做数据库的事情上:

         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 RAM,免费说只使用了 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=  ...) 

评估了完整的查询;不是分页查询。

请您参考如下方法:

当 django 进行分页时,它将使用标准的 QuerySet 切片来获取结果,这意味着它将使用 LIMIT 和 OFFSET。

您可以通过在 QuerySet 的 .query 属性上调用 str() 查看 ORM 生成的 SQL:

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

然后您可以让 sqlite EXPLAIN 查询并查看数据库试图做什么。我猜您正在对某个没有索引的字段进行排序。根据位于 http://www.sqlite.org/lang_explain.html 的 sqlite 文档,EXPLAIN QUERY PLAN 将告诉您将使用哪些索引。