TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • flag Russian
    Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Павлин Шарит

8 May, 15:03

Открыть в Telegram Поделиться Пожаловаться

Кеширование на уровне Django ORM - где оно стреляет в ногу

У каждого QuerySet есть встроенный кеш результатов - _result_cache.

QuerySet ленивый - в БД не идет, пока его не начнут вызывать. При первой полной итерации запрос выполняется, результат складывается в кеш инстанса. Вторая итерация того же инстанса - уже из памяти

Один и тот же запрос дважды это два разных QuerySet
# два запроса в БД
print([e.headline for e in Entry.objects.all()])
print([e.pub_date for e in Entry.objects.all()])

# один запрос
entries = Entry.objects.all()
print([e.headline for e in entries])
print([e.pub_date for e in entries])
Кеш живет на инстансе QuerySet, не на запросе. Каждый вызов .all() создает новый инстанс с пустым кешем - даже если SQL идентичный

Не все обращения заполняют кеш
qs = Entry.objects.all()
print(qs) # __repr__ - НЕ кеширует
print(qs[5]) # SELECT с LIMIT/OFFSET - запрос
print(qs[5]) # еще один запрос
Заполняют кеш:
[entry for entry in qs] # полная итерация
list(qs) # материализация
bool(qs) # if qs:
entry in qs # проверка вхождения
print(qs) дергает repr, который берет первые 20 + 1 элемент (REPR_OUTPUT_SIZE + 1, в SQL это LIMIT 21) и не считает это полной оценкой. Кеш пустой
С индексацией - пока QuerySet не выполнен, qs[5] идет в БД с LIMIT/OFFSET. После list() - из кеша

Связанные объекты в кеш не попадают сами
entries = Entry.objects.all()
for e in entries:
print(e.blog.name) # +1 запрос на каждую запись, N+1

entries = Entry.objects.select_related('blog')
for entry in entries:
print(entry.blog.name) # из join
Базовый кеш тянет только поля самой модели. FK кешируется на инстансе после первого обращения - e.blog второй раз для того же entry уже из памяти. Для другого entry - снова запрос. Решается select_related для FK/OneToOne и prefetch_related для M2M и обратных связей

cached_property + QuerySet работает не как ожидаешь
class User(models.Model):
@cached_property
def followers(self):
return User.objects.filter(followed_by=self)
cached_property запоминает то, что вернула функция - один и тот же инстанс QuerySet на все обращения. Полная итерация - заполнит кеш QuerySet, второй проход уже из памяти. Это работает
Ломается на чейнинге и индексации:
user.followers[0] # запрос
user.followers[0] # еще запрос
user.followers.filter(active=True) # новый QuerySet
Любая .filter(), .order_by() создает новый QuerySet с пустым кешем. Хочешь кешировать именно данные - оборачивай в list()
@cached_property
def followers(self):
return list(User.objects.filter(followed_by=self))

iterator() кеш отключает совсем
qs.iterator(chunk_size=2000) - стрим без кеша. Повторная итерация = повторный запрос. By design, но забыть легко

Что вынести
- Сохрани QuerySet в переменную если используешь больше одного раза
- На связанные объекты - select_related/prefetch_related
- В cached_property оборачивай в list() если хочешь данные, а не запрос
- Перед оптимизацией смотри реальные SQL - django-debug-toolbar и connection.queries полезнее интуиции

Это встроенный кеш на уровне инстанса - живет в рамках одного запроса. Шарить между запросами и процессами - это уже Redis и совершенно другая история

Поддержать на Boosty
Посмотреть на Youtube

1.2k 0 13 1 23
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot