Строки: основной тип данных Redis

Строка — самый простой и самый используемый тип в Redis. Но «строка» здесь значит куда больше, чем текст.

В Redis строка — это просто последовательность байтов длиной до 512 МБ. Текст, число, картинка, сериализованный объект — всё это «строки».

Когда вы делаете SET key value, вы создаёте строку. Это базовый кирпич Redis. Но не дайте слову «строка» себя обмануть: внутри может лежать что угодно — текст, число, JSON, даже бинарные данные вроде картинки.

Базовые команды

127.0.0.1:6379> SET name "Анна"
OK
127.0.0.1:6379> GET name
"Анна"
127.0.0.1:6379> STRLEN name
(integer) 8
127.0.0.1:6379> APPEND name " Иванова"
(integer) 24
127.0.0.1:6379> GET name
"Анна Иванова"
127.0.0.1:6379> SET counter 10
OK
127.0.0.1:6379> GETSET counter 20
"10"

STRLEN — длина строки в байтах (кириллица в UTF-8 занимает 2 байта на символ). APPEND — дописать в конец. GETSET — атомарно вернуть старое значение и записать новое.

Опции SET

Команда SET гибкая и часто заменяет несколько команд сразу:

SET key val EX 60       # с TTL 60 секунд
SET key val NX          # только если ключа НЕ было
SET key val XX          # только если ключ УЖЕ был
SET key val KEEPTTL     # сохранить существующий TTL
SET key val GET         # вернуть старое значение

Особенно важна опция NX — «установить, только если не существует». Это атомарная операция, на которой строятся распределённые блокировки. Об этом будет урок в разделе про кэширование.

Хранение нескольких значений

127.0.0.1:6379> MSET k1 "a" k2 "b" k3 "c"
OK
127.0.0.1:6379> MGET k1 k2 k3
1) "a"
2) "b"
3) "c"

MSET и MGET работают с несколькими ключами за один запрос — это экономит сетевые круги (round-trips), что критично для производительности.

   Один MGET вместо трёх GET

   Плохо:  App -> GET k1 -> ответ
           App -> GET k2 -> ответ      3 круга по сети
           App -> GET k3 -> ответ

   Хорошо: App -> MGET k1 k2 k3 -> ответ   1 круг

Как работает под капотом

Redis хранит строки в структуре SDS (Simple Dynamic String). В отличие от си-строк, SDS хранит длину явно — поэтому STRLEN работает за O(1), а не сканирует строку. SDS также позволяет хранить бинарные данные с нулевыми байтами внутри. Короткие строки, похожие на числа, Redis хранит как целые числа (int encoding), экономя память и ускоряя инкременты.

Частые ошибки

  • Хранить гигантские значения. Строка до 512 МБ, но огромные значения замедляют сеть и блокируют сервер при чтении/записи.
  • Делать много отдельных GET вместо MGET. Сетевая задержка на каждый запрос убивает производительность.
  • Путать APPEND с перезаписью. APPEND дописывает, не заменяет.

Best practices

  • Группируйте операции через MSET/MGET и пайплайнинг, чтобы экономить круги по сети.
  • Для значений-флагов и блокировок используйте SET key val NX EX.
  • Держите значения компактными; для структурированных данных рассмотрите хеши вместо одной большой JSON-строки.

Итог: Строка — универсальный тип Redis для байтов любого вида. SET с опциями заменяет множество команд. MSET/MGET экономят сеть. Внутри строки лежат в эффективной структуре SDS, а числовые строки оптимизируются отдельно.

Битовые операции и подстроки

Строка в Redis — не монолит: к ней есть команды для работы с частями и даже с отдельными битами. Это расширяет применение базового типа далеко за «положить текст»:

SETRANGE key 5 "XYZ"    # переписать часть строки с позиции 5
GETRANGE key 0 4        # прочитать подстроку (байты 0..4)
SETBIT flags 7 1        # установить 7-й бит в 1
GETBIT flags 7          # прочитать бит
BITCOUNT flags          # сколько единичных битов

Битовые операции превращают строку в компактный массив флагов: миллион булевых значений «да/нет» помещается всего в ~125 КБ. Это основа для bitmap-аналитики (кто заходил сегодня, кто прочитал уведомление) — подробнее в разделе про продакшен.

Ещё одна частая операция — атомарная замена с возвратом старого значения через SET key new GET (современная замена устаревшего GETSET). Она полезна, когда нужно «забрать и обнулить» счётчик или флаг за одну команду, не опасаясь, что между чтением и записью кто-то вклинится.

Строки как ячейки кэша

На практике строка чаще всего выступает именно как единица кэша: в неё кладут сериализованный объект (JSON, MessagePack) и снабжают его TTL. Это самый прямой способ закэшировать результат дорогого запроса:

SET cache:product:1001 "{...сериализованный товар...}" EX 600
GET cache:product:1001

Здесь работают все опции SET сразу: EX задаёт срок жизни кэша, а NX пригодится, если нужно «закэшировать только если ещё не закэшировано». Если же объект меняется по частям и важны частичные обновления — лучше хранить его не одной строкой, а хешем, о котором будет отдельный урок. Выбор между «строка с JSON» и «хеш» сводится к тому, читаете ли вы объект целиком (тогда строка проще и быстрее) или часто меняете отдельные поля (тогда хеш экономнее).

Проверьте себя
1. Какой максимальный размер строкового значения в Redis?
A1 КБ
B64 КБ
C512 МБ
DНеограничен
2. В чём преимущество MGET перед несколькими отдельными командами GET?
AMGET шифрует данные при передаче
BMGET выполняет всё за один сетевой запрос вместо нескольких кругов по сети
CMGET автоматически кэширует результат
DMGET работает с большими значениями, а GET нет