Django Фильтрация запросов замедлена на наборе данных больших строк - PullRequest
1 голос
/ 14 января 2020
  • Я столкнулся с низкой производительностью моего сайта при выполнении фильтрации больших строк по следующим данным поля. Тип данных, которые я храню в поле: RAW HTTP Response Body в поле body с большим содержимым, у меня 32 000 объектов для моделей ниже Project.
class Project(models.Model):
    body = models.TextField(null=True,blank=True)
  • Выполнение фильтрации в течение body занимает несколько секунд.
>>> Project.objects.filter(body__icontains='x').count() ### 5-6 seconds
20472

Я использую Django paginator для нарезки данных для каждой страницы.

>>> from django.core.paginator import Paginator
>>> data = Project.objects.filter(body__icontains='x')
>>> p = Paginator(data,10)
>>> p.page(1) ### takes 5-6 seconds here again because this function counts the total number of pages based on data.count()
<Page 1 of 2048>
  • И затем я передаю данные paginator в шаблон, который просто l oop и показывает тело.
{% for each_data in data %}
    {{ each_data.body }}
{% endfor %}
  • Для каждой страницы paginator, это займет 5-6 секунд ожидания, потому что функция paginator выполняет подсчет общего набора данных и возвращает диапазон страниц. что является самым большим недостатком.

Использование нормального Python Нарезка:

>>> data = Project.objects.filter(body__icontains='x')
>>> for each_data in data[0:10]:  ### 0.1 seconds
        print(each_data.body)
  • Как вы можете видеть, если мы надеваем не ударит базу данных вообще, а просто нарежет данные и передаст их по шаблону, после чего он сразу же покажет данные.

  • Я полагаю, запрос становится медленным, когда Paginator функция подсчитывает общее количество страниц для наших отфильтрованных результатов для каждой страницы.
  • Я считаю, что пагинатор не подходит для большого набора данных.

Недостатки использования функции нарезки:

  • Мы не можем подсчитать общее количество объектов результатов, возвращаемых фильтром.
  • не можем увидеть количество страниц, которое должен иметь paginator, поскольку мы не учитываем общий набор данных.

Есть ли способ решить эту проблему для более быстрого результата наряду с нумерацией страниц?

1 Ответ

1 голос
/ 14 января 2020

Почему это происходит?

Рассмотрим этот пример:

Project.objects.filter(body__icontains='x').count()

Это будет переведено в этот запрос (PostgreSQL):

SELECT COUNT(*) AS res
  FROM project
 WHERE UPPER(project.body::text) LIKE UPPER('%x%')

Если Есть много записей, это будет очень медленно. Как предложил @bruno - попытайтесь найти способ ее оптимизации (это может зависеть от вашей СУБД).

Классическая нумерация страниц в Django реализована с помощью операторов limit и offset SQL. В результате:

COUNT - (Время выполнения: 10,7 с)

OFFSET X - БД должна прочитать X записей с диска после соответствующей сортировки; Неверный список элементов, если новый элемент был вставлен в уже запрошенную страницу.

Обычно счетчик хэшируется, и когда добавляются новые записи, мы просто делаем обновления. OFFSET довольно сложно оптимизировать (кэширование?).

Есть ли лучшие решения?

KEYSET нумерация страниц, алгоритм следующий:

  • Мы помним идентификаторы первого FIRST_TIMESTAMP и последнего LAST_TIMESTAMP элемента на странице, например, это может быть дата публикации.
  • Чтобы получить следующую страницу, мы используем конструкцию:
WHERE table.date > LAST_TIMESTAMP ORDER_BY date ASC LIMIT <PAGE SIZE> 

Преимущества:

  • Работает намного быстрее на больших наборах запросов.
  • Корректно обрабатывает изменения на предыдущих страницах при добавлении новых элементов , Есть несколько реализаций для Django.

Недостатки:

  • Невозможно переключиться на случайную страницу, без предварительной загрузки данных, поэтому она подходит только для бесконечной прокрутки.

Так что, если это ваш случай, вы можете значительно ускорить нумерацию страниц.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...