Ответ
Кэширование — мощный инструмент, но его неправильное применение может принести больше вреда, чем пользы.
Основные сценарии, когда кэш вреден:
Данные часто и непредсказуемо меняются: Если данные обновляются очень часто, стоимость инвалидации (сброса) и обновления кэша может превысить выгоду от его использования. Это приводит к постоянному наличию неактуальных (stale) данных, что может вызвать ошибки в логике приложения.
Кэширование чувствительных данных: Хранение в кэше персональных данных, токенов доступа или паролей (даже в зашифрованном виде) увеличивает поверхность атаки. Если злоумышленник получит доступ к системе кэширования, он сможет извлечь эти данные.
Нагрузка с преобладанием записи (Write-heavy workload): Если в системе гораздо больше операций записи, чем чтения, кэш будет постоянно инвалидироваться и перезаписываться, создавая дополнительную нагрузку вместо её снижения.
Неправильная реализация кэша:
- Отсутствие TTL (Time-To-Live) и политики вытеснения: Кэш может бесконтрольно расти, что приведёт к утечке памяти и ошибке
Out of Memory
(OOM). - Отсутствие синхронизации: Глобальный кэш, доступный из нескольких горутин без мьютексов, приведёт к состоянию гонки (Data Race).
- Отсутствие TTL (Time-To-Live) и политики вытеснения: Кэш может бесконтрольно расти, что приведёт к утечке памяти и ошибке
Пример плохо реализованного кэша:
// Глобальный кэш без синхронизации, TTL и ограничения размера
var userCache = make(map[string]string)
func GetUser(id string) string {
if val, ok := userCache[id]; ok {
return val // Опасно: может вернуть устаревшие данные
}
// Загрузка из БД
data := fetchFromDB(id)
// Опасно: конкурентная запись в map вызовет панику (data race)
userCache[id] = data
return data
}
Рекомендация: Вместо самописных решений для кэша лучше использовать проверенные библиотеки (например, github.com/patrickmn/go-cache
для in-memory кэша или Redis
/Memcached
для распределённого), которые решают проблемы TTL, вытеснения и конкурентного доступа.