Кэширование в Go (Golang): Redis, in-memory и другие решения

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

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

Начать курс

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

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