Какие подходы и стратегии кэширования существуют в Node.js для улучшения производительности работы с базами данных
Node.jsбазы данныхкэшированиеоптимизацияпроизводительность
Что проверяют
Понимаете ли вы, где в Node.js приложении может жить кэш и как держать его согласованным с базой при нескольких инстансах. Следом почти всегда спрашивают про инвалидацию и про то, что будет, когда популярный ключ протухнет под нагрузкой.
Как отвечать
Сначала уровни:
- в памяти процесса:
Mapили пакетlru-cacheс лимитом размера и TTL. Самый быстрый вариант, но у каждого воркера PM2 и каждого пода свой кэш, он пропадает при рестарте и живёт в куче V8; - распределённый кэш: Redis или Memcached через
ioredisилиredis. Общий для всех инстансов, переживает деплой, стоит сетевого запроса и сериализации; - на стороне базы: PostgreSQL держит страницы в
shared_buffers, но не результаты запросов. Query Cache удалён из MySQL в 8.0, опираться на него нельзя. Для тяжёлых агрегатов подходит материализованное представление; - HTTP:
Cache-Control,ETagи CDN для ответов API, если данные одинаковы для всех.
Затем паттерны:
- cache-aside: читаем из кэша, при промахе идём в базу и кладём результат. Самый распространённый;
- write-through и write-behind: первый пишет в базу и сразу обновляет кэш, второй пишет в кэш и откладывает запись в базу, рискуя потерять данные;
- инвалидация: TTL как страховка плюс явное удаление ключа при записи. Локальные кэши на нескольких инстансах сбрасывают по событию из Redis pub/sub;
- защита от stampede: протух ключ, и сотня запросов одновременно идёт в базу. Помогают объединение запросов (хранить в
Mapсам промис, а не результат), блокировка черезSET key value NX PXи отдача устаревшего значения на время обновления.
Где спотыкаются
Кэшируют в Map без лимита и получают утечку памяти, а на нескольких инстансах разные пользователи видят разные данные.
Удаляют ключ до записи в базу, а не после коммита. Параллельное чтение успевает положить в кэш старое значение, и оно живёт до конца TTL.
Называют CDN или Query Cache в MySQL способом кэшировать запросы к базе. Первый кэширует HTTP-ответы, второго в актуальных версиях MySQL нет.