У Prowid Photos три экземпляра API за балансировщиком, но база одна — PostgreSQL. В пике приходит 350 запросов в секунду, и каждый идёт в базу за карточкой фото или профилем автора, хотя фотографии не меняются неделями. Кеш — временная копия данных там, где её быстрее достать. Разберём, где кеши живут на пути запроса, как посчитать пользу, какие бывают стратегии, как держать данные свежими, как не уронить базу, когда популярный ключ «протух», и как работают CDN и Cache-Control.
Зачем нужен кеш: сначала цифры
Аналогия — рабочий стол и архив. Бумаги, нужные каждый час, лежат на столе: достать быстро, но места мало. Остальное — в подвальном архиве: места много, идти долго. Кеш — это стол. Нашли нужное в кеше — попадание (cache hit), не нашли — промах (cache miss), и приходится идти в источник, то есть в базу. Доля попаданий называется hit ratio.
До базы доходят только промахи, то есть пик × (1 − hit ratio). Средняя задержка равна hit × t(hit) + (1 − hit) × t(miss). Пусть чтение из Redis занимает около 2 мс, а промах — запрос в PostgreSQL на 40 мс плюс запись значения в кеш, всего около 44 мс:
const peakRps = 350;
const hitMs = 2; // Redis lookup inside the data center
const missMs = 44; // lookup + 40 ms DB query + saving the value
for (const hit of [0, 0.5, 0.9, 0.99]) {
const avgMs = hit * hitMs + (1 - hit) * missMs;
const dbRps = peakRps * (1 - hit);
const pct = Math.round(hit * 100);
const line = `${pct}% hits: ${avgMs.toFixed(1)} ms`;
console.log(`${line}, DB ${dbRps.toFixed(1)} RPS`);
}| Hit ratio | В базу при пике 350 RPS | Средний ответ |
|---|---|---|
| 0% | 350 RPS | 44 мс |
| 50% | 175 RPS | 23 мс |
| 90% | 35 RPS | 6,2 мс |
| 99% | 3,5 RPS | 2,4 мс |
Каждая лишняя «девятка» в hit ratio снижает нагрузку на базу в разы: с 90% до 99% — в десять. Но кеш покупает скорость ценой свежести и простоты: данные могут устареть (stale), а ещё один компонент можно забыть обновить. Кешируйте то, что читается часто, меняется редко и допускает отставание. Не кешируйте данные, которые читают один раз, и всё, где устаревание недопустимо (остаток на счёте при списании).
Где живёт кеш: пять уровней
Запрос «падает» вниз по уровням и останавливается на первом, где нашлась копия:
- Браузер. Копия на устройстве, сеть не нужна вообще; управляется заголовком
Cache-Control. Кеш личный: у Лейлы свой, у Мурада свой. - CDN. Серверы по всему миру, поближе к людям. Один кеш на всех в регионе; идеален для картинок и публичных ответов API.
- Reverse proxy. Nginx перед приложением может хранить ответы (
proxy_cache) и закрывать API от повторов. - Кеш приложения. Redis: готовые объекты и результаты запросов, ответ за 1–2 мс.
- База данных. PostgreSQL сама держит горячие страницы в памяти (
shared_buffersи кеш ОС).
Чем ближе уровень к пользователю, тем меньше он ждёт, но тем сложнее управлять свежестью: копию в браузерах миллиона людей не удалить. Что и где кешируем в Prowid Photos: