2010-11-01 3 views
2

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

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

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

Использование 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

У вас есть индексы, установленные в вашей базе данных sqlite? В противном случае sqlite может выполнять линейное сканирование для фильтрации или сортировки. –

+0

У меня нет указателей, настроенных на данный момент; но я тоже не сортирую результаты, и у меня создается впечатление, что панель django-debug-toolbar вызывает SQL-запросы. Поэтому, если SQL-запрос составляет 50 мс для выбора 1-50 из ~ 3900 объектов и ~ 30 мс для выбора 1-50 из ~ 54 объектов, разница в процессорном времени должна составлять только 20 мс, а не текущая разница 7800 мс , –

ответ

3

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

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

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

+0

Это было полезно и привело меня по дорожке, чтобы действительно прочитать SQL-запросы (объясненные в редактированном вопросе выше). –

+1

Если вы хотите получить дополнительную информацию о ходе запроса, см. Http://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated. Вы можете попробовать использовать QuerySet ... count() вместо len(). – knutin

 Смежные вопросы

  • Нет связанных вопросов^_^