Как фильтровать данные в 100 раз быстрее в Django с помощью Redis
В последнее время я оптимизировал производительность приложения, построенного на Django и DRF. Производительность была низкой, так как несколько обращений к базе данных выполнялись для одной и той же таблицы, содержащей почти статические данные (данные менялись примерно раз в неделю). Хорошим решением было бы кэшировать весь набор запросов для модели, однако это было не очень удобно, так как для каждого вызова БД к модели применялся другой набор фильтров.
Факторы, способствующие плохой работе
Множественные вызовы базы данных увеличивают задержку в сети, а также создают нагрузку на серверы баз данных.
Отсутствие возможности выполнять фильтры в наборе запросов, если весь набор запросов должен быть кэширован с помощью Redis.
Модель Mixin спешит на помощь
Мы создадим миксин для наших моделей Django, который добавит возможности кэширования наборов запросов и применит относительно простые фильтры в памяти к этим наборам запросов.
Примечание. Это хороший вариант, только если в таблице несколько тысяч строк и данные относительно постоянны в течение определенного периода времени. Для больших таблиц (более 10 000 строк) всегда лучше позволить мощным серверам баз данных и механизмам обработки выполнять фильтрацию.
Давайте начнем с предположения, что простое приложение Django (имя ‘school_management’) настроено вместе с redis (Как настроить Редис).
Создайте файлы в каталоге приложения с именем model_mixins.py со следующим содержание.
Давайте создадим быстрые модели для использования этого миксина. Вы можете найти код модели здесь
post_save.connect(clear_student_cache, sender=Student)
post_delete.connect(clear_student_cache, sender=Student)
Эти строки используют сигналы Django, чтобы позаботиться об очистке кэшированных данных, если данные модели обновляются. Вот слушатель это делает недействительным.
Вот краткий справочник по запросу кэшированного набора данных с помощью миксина вместе с соответствующим запросом Django.
# Get all objects
q_db = Student.objects.all()
q_cache = Student.get_all_from_cache()
# Simple attribute Filter
q_db = Student.objects.filter(name="John")
q_cache = Student.filter_from_cache(name="John")
# Filter using list
q_db = Student.objects.filter(age__in=[15,20,25])
q_cache = Student.filter_from_cache(age=[15,20,25])
# Filter using foreign key reference
q_db = Student.objects.filter(school__name="MIT")
q_cache = Student.filter_related_from_cache(school={"name": "MIT"})
# Chaining Filters
q_db = Student.objects.filter(
age__in=[10,20], school__name="MIT"
)
q_cache = Student.filter_from_cache(age=[10,20])
q_cache = Student.filter_related_from_cache(
q, school={"name": "MIT"}
)
Вывод
Использование вышеуказанного миксина модели помогает кэшировать набор запросов модели и выполнять фильтры в памяти. Это значительно повышает производительность, если данные кэшированной модели обновляются не очень часто и объем данных невелик.
Я хотел бы услышать ваши отзывы об этом подходе, помог ли он вам повысить производительность, можно ли улучшить этот метод и т. д.