Все ключи Redis лежат в одном глобальном словаре. Понимание его устройства объясняет, почему Redis не «проседает» ступенькой при росте данных и почему память иногда не освобождается сразу.
Один плоский словарь
В Redis нет баз, схем и таблиц в привычном смысле. Есть одно пространство ключей:
словарь (hash table) «строка ключа → объект значения». Поиск по ключу — хеш-функция (SipHash) +
индексация в бакете: O(1) в среднем. Даже «числовой» ключ — строка: SET 123 x хранит
ключ «123», а не число.
Логическое разделение данных — целиком ваша забота, через соглашение об именах:
# Рекомендуемый формат: объект:идентификатор[:поле]
user:1001:profile
session:abc123
cache:product:sku-777
lock:report-generator
💡 Префиксы — это и «пространство имён», и основа диагностики: по префиксу легко считать статистику ключей черезSCANсMATCHи чистить устаревшие данные одним итератором.
Две хеш-таблицы и прогрессивный рехешинг
Словарь Redis — структура из двух таблиц: активной и «строящейся». При росте количества записей (load factor > 1, либо принудительно при BGSAVE-расширении) Redis начинает рехешинг: аллоцирует новую таблицу вдвое больше и постепенно переносит бакеты. Условный «индекс» переноса называется rehashidx.
- Каждое добавление/удаление/чтение словаря заодно переносит несколько бакетов;
- чтение ключа ищет сначала в старой таблице, затем в новой;
- новые записи всегда идут в новую таблицу;
- когда старая опустела — она освобождается.
Так миллиард ключей переезжает в новую таблицу незаметно для клиентов — сотни микросекунд за операцию вместо одной секундной паузы. Сжатие (load factor < 0.1) работает так же.
⚠️ Побочный эффект: после массового удаления ключей
used_memory может не упасть сразу — таблица сжимается прогрессивно, а аллокатор
(jemalloc) держит освобождённые страницы в своих пулах. Это нормально: память вернётся
по мере работы или уйдёт на новые записи.
Служебный словарь expires
Ключам с TTL соответствует второй словарь «ключ → время истечения». Именно поэтому
TTL и истечение почти бесплатны, а EXPIRE на несуществующий ключ просто
возвращает 0. Полезные команды вокруг TTL — в модуле 2, здесь важно знать, что TTL — это отдельная
запись в отдельной таблице, а не «время внутри значения».
Лимиты и здравый смысл
- Максимальный размер ключа и строкового значения — 512 МБ. Это анти-паттерн: такие значения медленно ходят по сети, тормозят репликацию и миграции. Практический потолок — десятки килобайт для строки.
- Ключи бинарно-безопасны: можно даже пустую строку
SET "" x, но не надо. - Короткие ключи экономят память, но не в ущерб читаемости:
u:1001:pхужеuser:1001:profile, если вы не гугл.
Логические базы: SELECT 0..15
Standalone Redis поддерживает 16 логических баз (SELECT 3). Это один
словарь с префиксной изоляцией, не отдельные области памяти. В Redis Cluster логических баз
нет — только DB 0. Поэтому индустрия давно ушла от использования SELECT в пользу
префиксов и/или отдельных инстансов: перенос кода в кластер не требует переделки ключей.
Заглянуть внутрь своими руками
DBSIZE # число ключей
RANDOMKEY # случайный ключ
EXISTS user:1001:profile # 1/0
TYPE user:1001:profile # hash/string/...
MEMORY USAGE user:1001:profile # байт на объект
SCAN 0 MATCH user:* COUNT 1000 # итератор по префиксу (подробно в 1.4)
Практика
- Залейте 1 млн ключей (
redis-benchmark -n 1000000 -t set -d 8 -q), затем удалите 90% из них скриптом через SCAN+UNLINK и понаблюдайтеINFO memory | grep used_memoryпару минут. - Сравните:
SET "ключ с пробелами" v,SET "" v, бинарный ключ черезSET "\x00\x01" v— убедитесь, что всё работает, и подумайте, стоит ли так делать. - Создайте ключ с TTL и найдите его след в обоих словарях через
DEBUG SLEEP 0(не получится — поэтому просто изучитеINFO keyspace: колонка «expires» показывает число ключей с TTL).
Мини-квиз
1. Почему Redis не блокируется на рехешинге при росте БД? — Рехешинг прогрессивный: бакеты переносятся по чуть-чуть при каждой операции.
2. Чем SELECT 5 отличается от префикса tenant5:? — Ничем физически:
та же память, тот же словарь. Но SELECT недоступен в кластере, а префикс работает везде.
3. Нужен ли «индекс» по ключам, чтобы быстро искать по префиксу? — Словарь индексирует только точные совпадения. Поиск по префиксу — это всегда обход (SCAN с MATCH), т.е. O(N) по всем ключам, поэтому MATCH — фильтр вывода, а не оптимизация.