Хеши: объекты и поля
Хеш — это «объект» внутри Redis: ключ с набором полей. Идеально для профилей, настроек и любых структур «поле — значение».
Зачем хранить профиль пользователя как одну JSON-строку, если можно хранить его как хеш и менять отдельные поля, не перезаписывая всё?
Хеш (Hash) — это отображение «поле → значение» внутри одного ключа. По сути, вложенный словарь. Если строка — это одна ячейка, то хеш — это целая запись с колонками. Это естественный способ хранить объекты.
Основные команды
127.0.0.1:6379> HSET user:42 name "Анна" age 30 city "Москва"
(integer) 3
127.0.0.1:6379> HGET user:42 name
"Анна"
127.0.0.1:6379> HGETALL user:42
1) "name"
2) "Анна"
3) "age"
4) "30"
5) "city"
6) "Москва"
127.0.0.1:6379> HINCRBY user:42 age 1
(integer) 31
127.0.0.1:6379> HDEL user:42 city
(integer) 1
127.0.0.1:6379> HEXISTS user:42 name
(integer) 1
HSET задаёт поля, HGET читает одно, HGETALL — все, HMGET — несколько, HINCRBY атомарно увеличивает числовое поле, HDEL удаляет поле, HEXISTS проверяет наличие.
Хеш vs JSON-строка
JSON-строка: Хеш:
SET user:42 '{"name":"Анна", HSET user:42 name "Анна"
"age":30}' age 30
Изменить age: Изменить age:
1. GET всю строку HINCRBY user:42 age 1
2. распарсить JSON (одна атомарная команда,
3. изменить не трогает другие поля)
4. SET всю строку обратно
С хешем вы меняете отдельное поле атомарно, не читая и не переписывая весь объект. Это и быстрее, и безопаснее при конкурентном доступе.
Демонстрация: хеш как словарь объекта на Python
# Хеш Redis — это вложенный словарь "поле -> значение"
db = {} # всё хранилище Redis
# HSET user:42 ...
db["user:42"] = {"name": "Анна", "age": 30, "city": "Москва"}
print("HSET user:42 ->", db["user:42"])
# HGET user:42 name
print("HGET user:42 name ->", db["user:42"]["name"])
# HINCRBY user:42 age 1 — атомарно меняем ОДНО поле
db["user:42"]["age"] += 1
print("HINCRBY user:42 age 1 ->", db["user:42"]["age"])
# HDEL user:42 city
del db["user:42"]["city"]
print("HDEL user:42 city ->", db["user:42"])
# HGETALL user:42
print("HGETALL user:42 ->", db["user:42"])
print("\nКлюч remained цел, мы меняли отдельные поля без перезаписи всего объекта.")
Каждое поле меняется независимо. Это ключевое преимущество хеша перед хранением объекта одной строкой.
Как работает под капотом
Маленькие хеши (мало полей, короткие значения) Redis хранит как listpack — компактный плоский массив пар «поле-значение». Это очень экономно по памяти: тысячи мелких хешей занимают в разы меньше, чем тысячи отдельных строковых ключей. Когда хеш превышает пороги (hash-max-listpack-entries, hash-max-listpack-value), он конвертируется в полноценную хеш-таблицу с доступом за O(1). Кстати, в новых версиях у полей хеша может быть собственный TTL.
Частые ошибки
- HGETALL на хеше с тысячами полей. Вернёт всё разом; используйте
HSCANилиHMGETдля нужных полей. - Хранить объект как JSON-строку, когда нужны частичные обновления. Это вынуждает читать и переписывать весь объект.
- Считать, что у хеша есть встроенный TTL на весь ключ — да, но не на отдельные поля (кроме новых версий с field TTL).
Best practices
- Храните объекты (профили, настройки) как хеши — это экономит память и даёт частичные обновления.
- Группируйте мелкие сущности в хеши вместо россыпи строковых ключей.
- Для числовых полей используйте
HINCRBYвместо чтения-записи.
Итог: Хеш — это объект с полями внутри одного ключа. Он экономит память, позволяет менять отдельные поля атомарно и идеален для профилей и настроек. Внутри — компактный listpack для мелких хешей и хеш-таблица для крупных.
Хеши как способ сэкономить память
Есть неочевидный, но мощный приём: группировать множество мелких значений в хеши вместо россыпи отдельных ключей. Дело в том, что у каждого ключа Redis есть накладные расходы (метаданные, место в словаре ключей). Тысячи мелких строковых ключей расходуют память заметно щедрее, чем те же данные, упакованные в хеши.
# Расточительно: ключ на каждое поле
SET user:42:name "Анна"
SET user:42:age 30
SET user:42:city "Москва"
# Экономно: один хеш на объект (хранится компактным listpack)
HSET user:42 name "Анна" age 30 city "Москва"
Пока хеш маленький, Redis держит его как компактный listpack — плоский массив без накладных расходов на каждый элемент. Этот же приём иногда применяют для шардирования огромного набора счётчиков: вместо миллиона ключей counter:N делают сотни хешей по тысяче полей в каждом, экономя память в разы.
Команды HSETNX (записать поле, только если его ещё нет) и HRANDFIELD (случайное поле) дополняют арсенал. А HSCAN позволяет безопасно обойти поля большого хеша порциями, не вытягивая всё разом через HGETALL.