Кэширование в Go (Golang): Redis, in-memory и другие решения
Кэширование — одна из ключевых стратегий для повышения производительности и снижения нагрузки на базы данных в приложениях на Go. В этой статье мы рассмотрим основные подходы и библиотеки для реализации кэширования: от простых in-memory решений до распределённого кэша на базе Redis. Вы узнаете, когда какой инструмент выбрать и как правильно организовать кэш в Go-проектах.
1. Встроенные возможности: map и sync.Map
Самый простой способ кэширования в Go — использование обычной map с мьютексом или потокобезопасной sync.Map. Эти подходы подходят для небольших проектов или прототипов, где не требуется сложная политика инвалидации.
Пример с sync.Map:
package main
import (
"fmt"
"sync"
)
type Cache struct {
data sync.Map
}
func (c *Cache) Set(key string, value interface{}) {
c.data.Store(key, value)
}
func (c *Cache) Get(key string) (interface{}, bool) {
return c.data.Load(key)
}
func main() {
cache := &Cache{}
cache.Set("user:1", "Alice")
if val, ok := cache.Get("user:1"); ok {
fmt.Println("Found:", val)
}
}
Плюсы: простота, нулевые зависимости.
Минусы: нет TTL, нет автоматической очистки, память не ограничена.
2. In-memory кэш с TTL: freecache
Freecache — это быстрая библиотека in-memory кэша с поддержкой TTL (времени жизни записи). Она использует сегментированную архитектуру для минимизации блокировок и эффективно управляет памятью.
Установка:
go get github.com/coocood/freecache
Пример использования:
package main
import (
"fmt"
"github.com/coocood/freecache"
)
func main() {
// Создаём кэш размером 100 МБ
cache := freecache.NewCache(100 * 1024 * 1024)
// Устанавливаем значение с TTL 10 секунд
cache.Set([]byte("mykey"), []byte("myvalue"), 10)
// Получаем значение
if val, err := cache.Get([]byte("mykey")); err == nil {
fmt.Println("Value:", string(val))
} else {
fmt.Println("Key not found or expired")
}
}
Плюсы: автоматическая очистка по TTL, низкое потребление CPU, поддержка больших объёмов данных.
Минусы: только для одного процесса (не распределённый).
3. BigCache — альтернатива freecache
BigCache — ещё одна популярная in-memory библиотека, оптимизированная для хранения большого количества записей (миллионы) с минимальным временем доступа. Она также поддерживает TTL и автоматическую эвакуацию старых данных.
Пример:
package main
import (
"fmt"
"github.com/allegro/bigcache/v3"
"time"
)
func main() {
config := bigcache.Config{
Shards: 1024,
LifeWindow: 10 * time.Minute,
CleanWindow: 5 * time.Minute,
MaxEntriesInWindow: 1000 * 10 * 60,
MaxEntrySize: 500,
Verbose: true,
}
cache, _ := bigcache.NewBigCache(config)
cache.Set("mykey", []byte("bigcache value"))
val, _ := cache.Get("mykey")
fmt.Println(string(val))
}
Плюсы: высокая производительность при большом количестве ключей, гибкая конфигурация.
Минусы: как и freecache — локальный кэш.
4. Распределённое кэширование с Redis и go-redis
Для распределённых систем, где несколько экземпляров приложения должны иметь единый кэш, используется Redis. Наиболее популярный клиент для Go — go-redis (ранее go-redis/redis).
Установка:
go get github.com/redis/go-redis/v9
Пример базового кэширования:
package main
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
// Устанавливаем ключ с TTL 1 час
err := rdb.Set(ctx, "user:100", `{"name":"John","age":30}`, time.Hour).Err()
if err != nil {
panic(err)
}
// Читаем ключ
val, err := rdb.Get(ctx, "user:100").Result()
if err == redis.Nil