Раздел 37 · Redis с нуля

Паттерн cache-aside

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Страницу книги в магазине открывают тысячу раз в минуту, и каждый раз сервер спрашивает PostgreSQL одно и то же: название, автора, цену «Мастера и Маргариты». Цена меняется раз в неделю, а база отвечает на один и тот же вопрос тысячу раз. В первом уроке раздела кэш стоял первым в списке задач Redis. Пора разобраться, как он устроен в приложении.

Стикер с номером

Тебе надо часто звонить в поликлинику. Номер ты находишь в толстом справочнике: долго, зато точно. После первого поиска ты пишешь номер на стикер и клеишь на холодильник. Дальше смотришь на стикер, справочник не нужен. Если стикер потерялся, снова лезешь в справочник и пишешь новый. А если поликлиника сменила номер, стикер надо выбросить, иначе будешь звонить в никуда.

Здесь справочник это база данных, а стикер это кэш. Нашёл на стикере значит попадание, не нашёл значит промах. Выбросить устаревший стикер значит инвалидировать кэш.

Как работает cache-aside

Приложение отвечает на запрос «покажи книгу 1» так:

  запрос книги 1
        │
        ▼
  GET cache:book:1 ──── есть ────▶ отдать из кэша (попадание)
        │
       нет (промах)
        │
        ▼
  SELECT ... FROM books WHERE id = 1
        │
        ▼
  SET cache:book:1 ... EX 300
        │
        ▼
  отдать ответ

Это cache-aside, по-русски «кэш сбоку». Redis ничего не знает о базе: всю работу делает приложение. Оно смотрит в кэш, при промахе идёт в базу и само кладёт ответ в кэш. Поэтому паттерн ещё называют ленивой загрузкой: в кэш попадает только то, что кто-то запросил.

То же в redis-cli

Пройдём путь руками. Первое чтение ничего не находит:

127.0.0.1:6379> GET cache:book:1
(nil)

Промах. Приложение выполняет запрос к PostgreSQL, как в уроке про SELECT, упаковывает строку в JSON и кладёт в Redis на пять минут:

127.0.0.1:6379> SET cache:book:1 "{\"id\":1,\"title\":\"Master and Margarita\",\"price\":690}" EX 300
OK
127.0.0.1:6379> GET cache:book:1
"{\"id\":1,\"title\":\"Master and Margarita\",\"price\":690}"
127.0.0.1:6379> TTL cache:book:1
(integer) 300

Следующие пять минут все читатели получают книгу из Redis. TTL у тебя покажет чуть меньше 300, если между командами прошло время.

Теперь цену снизили. Приложение обновляет строку в базе и удаляет копию:

127.0.0.1:6379> DEL cache:book:1
(integer) 1
127.0.0.1:6379> GET cache:book:1
(nil)

Следующий читатель промахнётся, возьмёт из базы новую цену и положит свежую копию:

127.0.0.1:6379> SET cache:book:1 "{\"id\":1,\"title\":\"Master and Margarita\",\"price\":650}" EX 300
OK

Карточку мы храним строкой JSON, а не хешем из урока 9: копия всегда читается и пишется целиком, поля по отдельности не меняются.

урок из курса

Урок из курса «Redis с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.