Django Paginate CPU Time масштабирование с количеством выбранных объектов, не отображаемых объектов - PullRequest
2 голосов
/ 01 ноября 2010

У меня есть простая база данных с приблизительно 3900 записями, и я использую универсальное представление (django.views.generic.list_detail.object_list) с его django-pagination (через paginate_by), чтобы просмотреть данные в базе данных, но некоторые запросыочень медленные.

Странная вещь заключается в том, что, несмотря на отображение только 50 объектов на странице, время рендеринга масштабируется приблизительно линейно с количеством выбранных объектов (и я не делаю никакой сортировки объектов).Например, если я выполняю запрос с ~ 3900, ~ 1800, ~ 900, ~ 54 выбранными объектами, это соответственно занимает ~ 8500 мс, ~ 4000 мс, ~ 2500 мс, ~ 800 мс процессорного времени (используя django-debug-toolbar)в то время как SQL занимал всего ~ 50 мс, ~ 40 мс, ~ 35 мс, ~ 30 мс, опять же, в то время как на всех страницах было ровно 50 объектов.Я минимизировал количество SQL-запросов, используя select_related, как предложено на странице оптимизации django .

Использование промежуточного программного обеспечения для профилирования подавляющее большинство времени затрачивается на длинные запросыделаю дб:

         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 может масштабироваться с количеством выбранных записей.Тем не менее, я не понимаю, почему остальное время процессора должно быть так или иначе затронуто.Это очень нелогично, и мне было интересно, есть ли какие-нибудь советы по отладке / профилированию, с которыми кто-нибудь мог бы мне помочь.

Использование django-1.2.3 с sqlite, python2.6, apache2-prefork (хотя переход на mpm-workerничего существенно не изменило).Любые советы / хитрости будут с благодарностью.Использование памяти, кажется, не является фактором (машина имеет 2 ГБ ОЗУ и свободно говорит, что использует только 300 МБ (дополнительно 600 МБ кэш-памяти)), и база данных находится на том же сервере, что и компьютер.

нашел мою ошибку .Я нашел свою ошибку.Я проверил длину исходного набора запросов, чтобы увидеть, была ли это длина 1 (а затем перешел к object_detail, если так).Это привело к оценке полного набора запросов (который по-прежнему занимал всего 5 мс согласно django-debug-toolbar), но значительно замедлило все.

В основном было что-то глупое, вроде:

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

, который оценивал полный запрос;не разбитый на страницы запрос.

1 Ответ

3 голосов
/ 01 ноября 2010

Когда django выполняет разбиение на страницы, для получения результатов он будет использовать стандартную нарезку QuerySet, это означает, что он будет использовать LIMIT и OFFSET.

Вы можете просмотреть SQL, который генерирует ORM, вызвав str() атрибута .query в QuerySet:

    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

...