2010-11-01 45 views
2

我有一個簡單的數據庫約3900條目,並使用通用視圖(django.views.generic.list_detail.object_list)與其Django的分頁(通過paginate_by)瀏覽數據庫中的數據,但有些查詢速度很慢。Django Paginate CPU時間縮放與所選對象的數量不顯示對象

奇怪的是,儘管每頁僅顯示50個對象,但渲染時間與選擇多少個對象大致呈線性關係(並且我不對對象進行任何排序)。例如,如果我使用〜3900,〜1800,〜900,〜54選擇的對象進行查詢,它將分別花費〜8500 ms,〜4000 ms,〜2500 ms,〜800 ms CPU時間(使用django-debug-toolbar)而SQL只需要約50毫秒,約40毫秒,約35毫秒,約30毫秒,而所有頁面恰好有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時間應該受到影響。這非常違反直覺,我想知道是否有任何調試/分析技巧可以幫助我。

在sqlite,python2.6,apache2-prefork中使用django-1.2.3(儘管切換到mpm-worker並沒有明顯的改變)。任何提示/技巧將不勝感激。內存使用似乎並不是一個因素(機器有2Gb RAM和免費說只使用300Mb(另外600Mb的緩存)),並且數據庫與機器在同一臺服務器上。

發現我的錯誤。我發現我的錯誤。我檢查了原始查詢集的長度,看它是否是長度爲1(如果是的話,然後轉到object_detail)。這導致評估完整的查詢集(根據django-debug-toolbar,它仍然只用了5ms),但是顯着減緩了所有查詢集。

基本上沒有像一些愚蠢的事:

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

評價的結果,全面查詢;而不是分頁查詢。

+1

你有在你的sqlite數據庫設置索引嗎?否則,sqlite可能會進行線性掃描以進行過濾或排序。 – 2010-11-01 18:54:44

+0

我目前沒有設置任何索引;但是我並沒有對結果進行排序,而且我的印象是django-debug-toolbar使用SQL查詢。因此,如果SQL查詢總共需要50毫秒來選擇〜3900個對象的1-50個參數,並且選擇約50個對象的1-50個參數需要30毫秒,CPU時間差應該只有20毫秒,而不是7800毫秒的當前差異。 – 2010-11-01 19:19:00

回答

3

當django進行分頁時,它將使用標準的QuerySet分片來獲得結果,這意味着它將使用LIMITOFFSET

您可以通過在查詢集的.query屬性調用str()查看ORM生成SQL:

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

你可以再問問的SQLite到EXPLAIN查詢,看看數據庫嘗試做的。我猜你正在對沒有索引的字段進行排序。 EXPLAIN QUERY PLAN會告訴你將使用什麼索引,根據http://www.sqlite.org/lang_explain.html的sqlite文檔

+0

這很有幫助,並讓我沿着軌道真正閱讀SQL查詢(在上面編輯的問題中進行了解釋)。 – 2010-11-01 19:45:06

+1

如果您想了解有關何時評估查詢的更多信息,請參閱http://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated您可以嘗試使用QuerySet ... count()而不是len()。 – knutin 2010-11-01 19:52:04