TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
Павлин Шарит

8 May, 15:03

Open in Telegram Share Report

Кеширование на уровне 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
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot