Кэширование в Go (Golang): Redis и другие инструменты
Кэширование — один из ключевых механизмов оптимизации производительности веб-приложений и микросервисов. В экосистеме Go (Golang) существует множество библиотек для реализации кэширования, от простых in-memory решений до распределённых систем на основе Redis. В этой статье мы подробно разберём три основных подхода: встроенные структуры данных (sync.Map), популярные in-memory библиотеки (go-cache, freecache, bigcache) и интеграцию с Redis.
1. Встроенные средства: sync.Map
Go предоставляет потокобезопасную map — sync.Map. Она подходит для простого кэширования с небольшим объёмом данных и редкими обновлениями. Однако её производительность на высоких нагрузках уступает специализированным библиотекам.
Пример использования sync.Map:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var cache sync.Map
// Запись
cache.Store("user:42", map[string]interface{}{
"name": "Alice",
"age": 30,
})
// Чтение с проверкой
if val, ok := cache.Load("user:42"); ok {
fmt.Println("Кэш найден:", val)
}
// Удаление
cache.Delete("user:42")
// Кэширование с TTL (придётся реализовать вручную)
cache.Store("session:abc", time.Now().Unix())
}
Недостатки: отсутствие автоматического TTL, ограниченный набор операций, возможные проблемы с производительностью при частых записях.
2. In-memory библиотеки
Для более сложных сценариев используйте специализированные библиотеки. Рассмотрим три популярные:
2.1 go-cache
Простая библиотека с поддержкой TTL. Идеальна для небольших проектов.
import "github.com/patrickmn/go-cache"
func main() {
c := cache.New(5*time.Minute, 10*time.Minute) // default TTL, cleanup interval
c.Set("key", "value", cache.DefaultExpiration)
val, found := c.Get("key")
if found {
fmt.Println(val)
}
}
2.2 freecache
Библиотека с фиксированным размером памяти (no GC overhead). Поддерживает TTL и высокую производительность.
import "github.com/coocood/freecache"
func main() {
cache := freecache.NewCache(100 * 1024 * 1024) // 100 MB
cache.Set([]byte("mykey"), []byte("myvalue"), 60) // TTL 60 сек
val, err := cache.Get([]byte("mykey"))
if err == nil {
fmt.Println(string(val))
}
}
2.3 bigcache
Оптимизирована для хранения больших объёмов данных без GC-пауз. Работает с сериализованными данными.
import "github.com/allegro/bigcache/v3"
func main() {
cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute))
cache.Set("key", []byte("value"))
val, _ := cache.Get("key")
fmt.Println(string(val))
}
3. Redis: распределённое кэширование
Для распределённых систем и микросервисов Redis остаётся стандартом де-факто. В Go используйте библиотеку go-redis/redis (v9+).
Подключение и базовые операции:
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
// Установка значения с TTL
err := rdb.Set(ctx, "user:42", `{"name":"Bob"}`, 10*time.Minute).Err()
if err != nil {
panic(err)
}
// Чтение
val, err := rdb.Get(ctx, "user:42").Result()
if err == redis.Nil {
fmt.Println("Кэш не найден")
} else if err != nil {
panic(err)
} else {
fmt.Println("Значение:", val)
}
}
Продвинутые возможности Redis в Go:
- Pipeline — выполнение нескольких команд за один сетевой вызов
- Pub/Sub — для инвалидации кэша через каналы
- Cluster — автоматическое шардирование
- RedisJSON — работа с JSON-документами
Пример с Pipeline:
pipe := rdb.Pipeline()
pipe.Set(ctx, "key1", "value1", 0)
pipe.Get(ctx, "key2")
cmds, _ := pipe.Exec(ctx)
for _, cmd := range cmds {
fmt.Println(cmd.(*redis.StringCmd