Опирается на правила:
R-CACHE-OBS-1…R-CACHE-OBS-4иR-CACHE-OBS-X1из Caching Style Guide → раздел 8. Observability.
Важно знать
- Spring Cache metrics автоматически через Micrometer:
cache_gets_total{cache,result},cache_puts_total,cache_evictions_total,cache_size.- Hit rate =
hits / (hits + misses)— основная метрика здоровья.- Алерт при hit rate < 70% для долго существующих кешей — либо неподходящий TTL, либо слишком частые invalidation.
- Eviction логируется на DEBUG с key. INFO/WARN — будет шумно.
- Redis-side метрики отдельно через Redis Exporter для Prometheus.
- Отключение
management.metrics.enable.cache=false— антипаттерн: без метрик не видно, что кеш бесполезен.
Кеш без observability — это «надеемся, что работает». Hit rate 5% означает «потратили инфру на Redis, эффект нулевой» — а узнаёшь об этом только когда вспомнишь проверить.
Автоматический экспорт через Micrometer
R-CACHE-OBS-1: Spring Cache + Actuator экспортирует метрики без custom-кода.
| Метрика | Что показывает |
|---|---|
cache_gets_total{cache, result="hit"} | Сколько hits в этом кеше |
cache_gets_total{cache, result="miss"} | Сколько misses |
cache_puts_total{cache} | Сколько puts (load from DB + put) |
cache_evictions_total{cache} | Сколько evictions |
cache_size{cache} | Размер кеша (для bounded) |
Включение метрик в application.yml:
management:
metrics:
enable:
cache: true
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
management.metrics.enable.cache: true — дефолт, но явно прописываем чтобы не отключить случайно.
В Prometheus метрики появляются автоматически с тегами service, env, version, cache.
Hit rate — главная метрика
R-CACHE-OBS-2: расчёт hit rate в Prometheus.
sum by (cache) (rate(cache_gets_total{result="hit"}[5m]))
/
sum by (cache) (rate(cache_gets_total[5m]))
Дашборд: hit rate per cache, исторический trend.
Алерт:
- alert: CacheHitRateLow
expr: |
(
sum by (cache, service) (rate(cache_gets_total{result="hit"}[1h]))
/
sum by (cache, service) (rate(cache_gets_total[1h]))
) < 0.7
for: 30m
annotations:
summary: "Cache {{ $labels.cache }} hit rate < 70%"
runbook: https://runbooks.internal/cache-low-hit-rate
Hit rate < 70% значит одну из проблем:
- TTL слишком короткий. Данные истекают до того, как клиенты их прочитали повторно. Увеличить TTL.
- TTL слишком длинный + частая invalidation. Каждый write evict-ит — кеш пустой большую часть времени. Перепроектировать паттерн.
- Ключи unbounded. Каждый запрос — уникальный ключ (
order-searchс детальными фильтрами). Кеш не помогает; убрать или сделать менее гранулярным. - Кеш для редко читаемых данных. Если ratio read/write < 10:1 — кеш приносит больше работы, чем экономии. Отключить.
Runbook должен включать процедуру диагностики через cache_gets_total, cache_puts_total, cache_evictions_total — чтобы оператор быстро определил, что именно ломается.
Eviction — DEBUG, не INFO
R-CACHE-OBS-3: каждый @CacheEvict потенциально срабатывает на каждом write. Логирование на INFO даст 100 строк/секунду в высоконагруженном сервисе.
@Component
@Slf4j
public class CacheEvictionLogger {
@EventListener
public void onCacheEvent(CacheEvent event) {
if (event.type() == CacheEvent.Type.EVICTION) {
log.debug("Cache evict: cache={} key={}", event.cacheName(), event.key());
}
}
}
DEBUG включается per-environment при инциденте, когда нужно понять «инвалидировался ли кеш после write». На проде по умолчанию выключен.
Redis-side метрики
R-CACHE-OBS-4: Spring Cache видит только Java-side. Redis-side состояние (memory pressure, cluster health, replication lag) мониторим отдельно через Redis Exporter.
| Метрика | Что показывает |
|---|---|
redis_up | Доступность Redis |
redis_memory_used_bytes | Использованная память |
redis_memory_max_bytes | Лимит памяти (maxmemory) |
redis_keys_total{db} | Количество ключей |
redis_evicted_keys_total | Сколько ключей evicted Redis-ом по LRU/LFU |
redis_cluster_state | Health cluster mode |
redis_master_replication_lag_seconds | Replication delay |
Алерт на redis_memory_used / redis_memory_max > 0.9 — близко к eviction. Алерт на redis_evicted_keys_total rate > 0 для не-eviction политики (noeviction) — что-то не так.
Что запрещено
Отключение Spring Cache metrics
R-CACHE-OBS-X1: management.metrics.enable.cache: false.
Сценарий: команда подключила Redis, написала @Cacheable, всё работает локально. На проде через месяц SRE замечает, что Redis не растёт. Открывает Grafana — метрик нет. Включает — hit rate 0%. Оказывается, @EnableCaching без CacheManager-бина → NoOpCacheManager (см. Конфигурация).
Метрики — единственная защита от silent fail. Не отключаем.
Что запрещено — таблица
| Антипаттерн | Правило | Что взамен |
|---|---|---|
management.metrics.enable.cache: false | R-CACHE-OBS-X1 | оставить включёнными |
| Eviction на INFO | R-CACHE-OBS-3 | DEBUG |
| Нет алерта на hit rate < 70% | R-CACHE-OBS-2 | алерт for: 30m с runbook |
| Только Java-side метрики, без Redis Exporter | R-CACHE-OBS-4 | Redis-side тоже |
Кеш без cache тега в метриках | R-CACHE-OBS-1 | per-cache именование (auto через Spring) |
| Hit rate измеряется avg, не rate | R-CACHE-OBS-2 | rate(...)[5m] |
Куда дальше
- Caching → раздел 8. Observability — нормативные формулировки.
- Observability → Metrics — стандарт
service/env/versionтеги. - Observability → SLO и алерты — runbook для cache hit rate.
- Конфигурация —
@EnableCachingбез CacheManager → no metrics. - TTL — низкий hit rate часто из-за неправильного TTL.
- Где кешируем — низкий hit rate часто из-за wrong cacheable candidate.