Ответ
Инвалидация кеша — это процесс удаления или пометки данных в кеше как неактуальных. Это одна из самых сложных проблем в кешировании. В Go-приложении можно реализовать несколько основных стратегий.
Рассмотрим на примере простого конкурентно-безопасного кеша:
import (
"sync"
"time"
)
type CacheItem struct {
Value interface{}
ExpiresAt time.Time
}
type Cache struct {
mu sync.RWMutex // RWMutex для оптимизации чтений
items map[string]CacheItem
}
func NewCache() *Cache {
return &Cache{
items: make(map[string]CacheItem),
}
}
1. Инвалидация по времени жизни (Time-To-Live, TTL)
Самая распространенная стратегия. Каждой записи в кеше присваивается срок жизни, по истечении которого она считается недействительной. Проверка происходит в момент запроса данных.
// Set добавляет элемент с TTL
func (c *Cache) Set(key string, value interface{}, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = CacheItem{
Value: value,
ExpiresAt: time.Now().Add(ttl),
}
}
// Get извлекает элемент, проверяя его TTL
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
item, found := c.items[key]
if !found {
return nil, false
}
// Проверка TTL. Если срок истек, элемент считается отсутствующим.
if time.Now().After(item.ExpiresAt) {
// В реальном приложении здесь можно запустить горутину для удаления
// или иметь фоновый процесс очистки.
return nil, false
}
return item.Value, true
}
2. Явная (ручная) инвалидация
Данные удаляются из кеша по прямому вызову, например, после обновления этих данных в базе данных.
// Delete удаляет элемент из кеша по ключу
func (c *Cache) Delete(key string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.items, key)
}
// Пример использования:
// db.UpdateUser(user)
// userCache.Delete(user.ID)
3. Политики вытеснения (Eviction Policies)
Когда кеш достигает максимального размера, старые данные вытесняются, чтобы освободить место для новых. Распространенные политики:
- LRU (Least Recently Used): Вытесняется элемент, который дольше всех не использовался.
- LFU (Least Frequently Used): Вытесняется элемент, который использовался реже всего.
- FIFO (First-In, First-Out): Вытесняется самый старый элемент.
Реализация таких политик сложна, поэтому для production-систем настоятельно рекомендуется использовать готовые библиотеки, которые предоставляют эти и другие возможности "из коробки":
groupcache
: Распределенный кеш от Google.ristretto
: Высокопроизводительный, конкурентный кеш с LFU-политикой.go-cache
: Простой кеш в памяти с TTL, похожий на наш пример, но с фоновой очисткой.