Кэширование в Go (Golang): Redis и альтернативные инструменты

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Кэширование в 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                    

Рекомендаций