Как устроен Redis и как с ним работать в Go?

Ответ

Redis (REmote DIctionary Server) — это высокопроизводительное in-memory хранилище данных типа «ключ-значение», которое часто используется как база данных, кэш или брокер сообщений.

Ключевые аспекты архитектуры:

  1. Хранение в памяти (In-Memory): Все данные хранятся в оперативной памяти, что обеспечивает сверхнизкие задержки при чтении и записи.
  2. Однопоточная модель (Single-Threaded Event Loop): Redis использует один поток для обработки всех команд. Это исключает гонки данных и необходимость в блокировках, но для утилизации многоядерных процессоров запускают несколько экземпляров Redis. Для операций ввода-вывода используется мультиплексирование (epoll, kqueue), что позволяет эффективно обрабатывать тысячи одновременных подключений.
  3. Персистентность (сохранение на диск): Для сохранности данных при перезапуске Redis предлагает два механизма:
    • RDB (Redis Database): Создание снимков (snapshots) всей базы данных через определённые интервалы. Оптимально для бэкапов.
    • AOF (Append-Only File): Запись каждой операции изменения данных в лог-файл. Обеспечивает лучшую сохранность данных.

Основные типы данных:

Redis — это не просто хранилище строк. Он поддерживает сложные структуры данных:

  • Strings: Строки, числа, битовые поля.
  • Lists: Списки, реализующие двустороннюю очередь (deque).
  • Hashes: Хеш-таблицы (аналог map в Go).
  • Sets: Неупорядоченные множества уникальных строк.
  • Sorted Sets (ZSETs): Упорядоченные множества, где у каждого элемента есть вес (score). Идеально для рейтингов и таблиц лидеров.
  • Streams: Структура для реализации логов и очередей сообщений.
  • HyperLogLogs, Bitmaps, Geospatial indexes.

Пример использования в Go (с библиотекой go-redis):

Библиотека go-redis/redis является современным стандартом для работы с Redis в Go.

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Адрес Redis сервера
        Password: "",               // Пароль, если есть
        DB:       0,                // Номер базы данных
    })

    // Устанавливаем значение ключа
    err := rdb.Set(ctx, "user:1", "Alex", 0).Err()
    if err != nil {
        panic(err)
    }

    // Получаем значение ключа
    val, err := rdb.Get(ctx, "user:1").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("user:1 ->", val)

    // Пример работы с Hash
    rdb.HSet(ctx, "user:2", "name", "Bob", "age", 30)
    name, _ := rdb.HGet(ctx, "user:2", "name").Result()
    age, _ := rdb.HGet(ctx, "user:2", "age").Result()
    fmt.Printf("User 2: Name - %s, Age - %sn", name, age)
}

Основные сценарии использования:

  • Кэширование: Снижение нагрузки на основную базу данных.
  • Хранение сессий: Быстрое сохранение и получение данных о пользовательских сессиях.
  • Очереди задач: Использование Lists или Streams для создания фоновых очередей.
  • Pub/Sub: Реализация системы обмена сообщениями в реальном времени.
  • Счётчики и рейтинги: Атомарные операции (INCR) и упорядоченные множества (Sorted Sets).

Ответ 18+ 🔞

А, так вот ты какой, Redis, ёпта! Ну что за зверь, сейчас разберём, блядь.

Смотри, это не просто какая-то там база, это, сука, in-memory монстр, то есть всё у него в оперативке живёт. Отсюда и скорость — пиздец какая, задержки просто смешные, всё летает. Используют его кто во что горазд: и как базу, и как кэш, и как почтальона для сообщений.

Архитектура, блядь, интересная:

  1. В памяти (In-Memory): Ну тут всё ясно, в ОЗУ всё, поэтому и быстрый, как укус голодной мартышлюшки.
  2. Однопоточный цирк (Single-Threaded Event Loop): Вот это прикол! Весь этот движ обрабатывает команды в ОДИН поток, представляешь? Никаких гонок, никаких блокировок — красота. А чтобы все ядра процессора не простаивали, просто запускают несколько таких инстансов, и всё. Для работы с кучей подключений использует какую-то свою магию (epoll, kqueue), так что тысячи клиентов — не проблема.
  3. Чтобы не потерялось (Персистентность): Ну а если сервер хуяк и перезагрузился? Для этого есть два способа:
    • RDB (Redis Database): Делает, сука, фотку всей базы раз в какое-то время. Для бэкапов — то, что надо.
    • AOF (Append-Only File): А это, блядь, параноик. Каждую операцию, которая меняет данные, пишет в лог. Потерять что-то — надо очень постараться.

А данные-то у него не простые!

Он не только строки туда-сюда гонять умеет. У него там целый зоопарк структур:

  • Strings: Ну, строки, числа, биты.
  • Lists: Списки, как двусторонняя очередь.
  • Hashes: Прямо как map в Go, только в Redis.
  • Sets: Множества, где всё уникальное и без порядка.
  • Sorted Sets (ZSETs): А вот это уже охуенно! Множества, но упорядоченные по какому-то весу (score). Для рейтингов, таблиц лидеров — идеально.
  • Streams: Для логов и очередей сообщений.
  • И ещё куча всякой дичи: HyperLogLogs, Bitmaps, Geospatial indexes — на любой вкус, блядь.

Как с ним в Go общаться (библиотека go-redis):

go-redis/redis — это сейчас стандарт де-факто, все его юзают.

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Где этот красный зверь сидит
        Password: "",               // Пароль, если вдруг запаролен
        DB:       0,                // Номер базы, их там несколько
    })

    // Запихнули значение по ключу
    err := rdb.Set(ctx, "user:1", "Alex", 0).Err()
    if err != nil {
        panic(err)
    }

    // Достали обратно
    val, err := rdb.Get(ctx, "user:1").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("user:1 ->", val)

    // Поработали с Хэшем (как с map)
    rdb.HSet(ctx, "user:2", "name", "Bob", "age", 30)
    name, _ := rdb.HGet(ctx, "user:2", "name").Result()
    age, _ := rdb.HGet(ctx, "user:2", "age").Result()
    fmt.Printf("User 2: Name - %s, Age - %sn", name, age)
}

И где его, блядь, применяют:

  • Кэширование: Чтобы основную базу не ебашить по каждому чиху, результаты сложных запросов скидывают сюда.
  • Сессии пользователей: Быстренько сохранить данные сессии и так же быстро их достать.
  • Очереди задач: На Lists или Streams можно очередь фоновых задач построить.
  • Pub/Sub: Система "издатель-подписчик" для сообщений в реальном времени.
  • Счётчики и рейтинги: Атомарные операции типа INCR и наши любимые Sorted Sets — просто созданы для этого.

Вот такой он, этот Redis. Не база, а мечта, если, конечно, память не жалко, блядь.