Использовали ли кэши в Go, например Redis, и как кэшировали
Что проверяют
Кэшировали ли вы по-настоящему или только вызывали Set и Get. Интервьюера интересует стратегия: что кэшируете, как инвалидируете, что происходит при недоступном Redis. Следом почти всегда спрашивают про cache stampede и расхождение кеша с базой.
Как отвечать
Опишите свой случай: какие данные, почему они подходят для кеша, какой TTL и почему такой, что изменилось в задержках или нагрузке на базу. Цифры нужны ваши. Технический каркас:
- клиент: актуальный модуль
github.com/redis/go-redis/v9, контекст из стандартного пакетаcontextс таймаутом на каждую операцию; - cache-aside: читаем из Redis, при промахе (
errors.Is(err, redis.Nil)) идём в базу и кладём результат черезSet(ctx, key, val, ttl). Промах это не ошибка, его нужно отличать от сбоя сети; - инвалидация: при изменении данных удаляем ключ после коммита транзакции. TTL оставляем всегда, как страховку от забытой инвалидации, и добавляем к нему случайный разброс, чтобы ключи не истекали одновременно;
- защита от stampede:
golang.org/x/sync/singleflightсклеивает параллельные промахи по одному ключу в один запрос к базе; - сериализация: JSON проще отлаживать, protobuf или msgpack компактнее и быстрее;
- деградация: если Redis недоступен, сервис идёт в базу напрямую, а не отдаёт 500.
Хорошо упомянуть локальный кеш в памяти процесса (ristretto, bigcache или карта под мьютексом) как первый уровень перед Redis для самых горячих ключей и maxmemory-policy вроде allkeys-lru на стороне Redis.
Где спотыкаются
Показывают код на github.com/go-redis/redis/v8 с golang.org/x/net/context. Это устаревший путь модуля и давно лишний пакет контекста, выдают знания из старых статей.
Обновляют кеш записью нового значения вместо удаления. При двух параллельных обновлениях в кеше может остаться старая версия, удаление ключа эту гонку почти снимает.
Ставят ключи без TTL. Через месяц память Redis забита данными, которые никто не читает, и начинается вытеснение нужных ключей.