Сервис хранит в Redis миллионы коротких флагов, счётчиков и идентификаторов. Полезное значение каждого элемента занимает совсем немного места, поэтому бюджет строят по сумме значений и добавляют умеренный запас. Но Redis достигает maxmemory заметно раньше прогноза, начинает удалять ключи либо возвращать ошибки в зависимости от выбранной политики. Причина не обязательно в утечке или неверном общем учёте памяти. Каждый самостоятельный ключ — это не только payload, то есть полезное значение. Redis должен сохранить имя, объект значения, запись в пространстве ключей и служебные связи. Если у ключа есть срок жизни, требуется учёт истечения. Allocator — механизм выдачи памяти процессу — округляет запросы до доступных классов размеров, поэтому короткое выделение не обязано занимать ровно столько байтов, сколько видно приложению.

На миллионах объектов служебная цена иногда оказывается больше суммы значений. Но нельзя вывести вечное число байтов на ключ: результат меняют версия Redis, архитектура, allocator, длины, типы и encodings.

Короткое значение хранится не отдельно

Представим флаг с полезным смыслом «да» или «нет». Приложение видит короткое значение, но Redis ещё должен найти его по имени и поддерживать объект в общей структуре базы. Имя ключа может быть длиннее самого флага, а запись верхнего уровня повторяется для каждого отдельного элемента. TTL, или срок жизни, добавляет ещё одно измерение. Для отдельного ключа Redis должен помнить, когда его удалить. Не каждый ключ имеет TTL и накладной расход не одинаков для всех случаев, но миллион независимо истекающих элементов — иная модель, чем один контейнер с общим сроком жизни.

Служебная цена зависит от формы. Короткие имена, длинные имена, строки, hashes и другие типы дают разные результаты. Разрядность платформы, версия Redis, выбранный allocator и история создания или удаления объектов тоже влияют. Поэтому пример, измеренный на ноутбуке или в чужом проекте, нельзя превращать в тариф bytes per key для production.

Два измерения отвечают на разные вопросы

MEMORY USAGE оценивает, сколько выделений требуют конкретный ключ, его значение и связанный административный overhead. Это полезнее длины исходной строки: команда смотрит на представление внутри Redis, а не только на payload приложения. Для вложенных типов результат может строиться по выборке внутренних элементов и средней оценке. Однородный маленький hash и структура с редкими крупными полями поэтому требуют разной осторожности. Даже точное измерение одного объекта остаётся ответом про этот объект, версию и конфигурацию.

OBJECT ENCODING показывает, какое внутреннее представление Redis выбрал для значения. Название encoding объясняет способ хранения, но не сообщает число байтов. Два объекта с одинаковым encoding могут иметь разную длину полей, разную заполненность и разный MEMORY USAGE. Общий used_memory нужен как контроль масштаба: после загрузки реального распределения объектов он показывает все выделения Redis, а не только отобранные ключи. Но статья №19 уже разбирает уровни общего учёта; здесь важен более узкий вывод. Разница между суммой MEMORY USAGE выбранных объектов и общим used_memory не должна автоматически приписываться цене каждого ключа.

Группировка распределяет повторяющийся overhead

Если множество элементов логически относится к одному владельцу или разделу, их можно представить полями подходящей агрегатной структуры, например hash. Тогда имя верхнеуровневого ключа и часть служебных структур повторяются не для каждого бизнес-элемента. Небольшие агрегаты Redis также могут получать компактное внутреннее представление. Экономический смысл прост. Миллион самостоятельных счётчиков несёт миллион верхнеуровневых имён и записей keyspace. При допустимой группировке часть этой цены распределяется между полями. Полезные числа не исчезают, но доля повторяющегося overhead может снизиться.

Это не означает, что любой набор нужно собрать в один огромный hash. Выгода зависит от длины полей, размеров значений, количества элементов в группе, версии и encoding. По мере роста агрегат может перейти из компактного представления в общее, после чего расход и профиль CPU изменятся. Группировка оправдана, когда бизнес-элементы действительно образуют устойчивую единицу управления. Тогда общая структура отражает предметную модель, а меньший overhead становится полезным следствием. Если связь придумана только ради RAM, скрытая стоимость проявится в TTL, распределении нагрузки и эксплуатации.

Экономия памяти меняет семантику сервиса

Отдельные ключи независимо адресуются, удаляются и получают срок жизни. После группировки приложение обращается к полю внутри общего объекта. Возможности и семантика срока жизни отдельных полей зависят от версии Redis и поддерживаемых операций, поэтому прежнее поведение key-level TTL не сохраняется автоматически. Меняется и атомарность. Операции над полями одного объекта могут давать удобную общую границу, но логика, раньше построенная на независимых ключах, требует другого набора команд и обработки ошибок. Миграция — это изменение модели данных и клиентского кода, а не только упаковка тех же байтов.

Большой объект способен стать горячей точкой, а его обработка — более крупной единицей CPU и сети. Чтение поля не обязано передавать весь hash, но массовые операции имеют другой профиль. В Redis Cluster верхнеуровневый ключ задаёт hash slot, поэтому поля нельзя независимо разнести по узлам как отдельные ключи. Растёт и зона последствий: ошибка удаления, миграции или тяжёлая операция затрагивает группу элементов. Экономию DRAM сравнивают со стоимостью восстановления, сложностью перехода и возможностью остановить больше бизнес-объектов одной ошибкой.

Компактное представление не является обещанием

Redis автоматически выбирает encodings для разных типов и может заменить специальное компактное представление на общее, когда операция, размер объекта или его элементов больше не позволяют сохранить прежнюю форму. Сам переход является штатным поведением, а не поломкой. OBJECT ENCODING позволяет увидеть выбранную форму, а MEMORY USAGE — связанный размер. Но encoding нельзя читать как гарантию экономии, а единичное измерение — как прогноз будущего роста. Общий used_memory показывает итог для всей смеси, однако не объясняет, какой класс объектов перевёл систему через границу.

Бизнес-решение поэтому относится не к названию encoding, а к распределению размеров. Если большинство групп компактно, но небольшая доля становится очень крупной и горячей, средняя цифра скрывает риск. Иногда разумнее разделить такие группы по устойчивому бизнес-признаку, а иногда — сохранить отдельные ключи и принять их overhead ради независимости. Нельзя считать внутреннюю структуру Redis неизменным ABI — контрактом, который версия обязана сохранять навсегда. Названия encodings, пороги и детали allocator меняются. Стабильным остаётся требование проверять выбранную модель на той версии и архитектуре, где она будет работать.

Память покупают вместе с нужной семантикой

Правильный вопрос звучит не «сколько байтов стоит ключ», а «какая модель хранит обязательное число элементов, сохраняя TTL, адресацию, распределение и качество сервиса». Для части флагов и счётчиков подходящая группировка снижает повторяющийся overhead без вреда бизнес-логике. Для других независимость ключей важнее сэкономленной RAM. Сначала устраняют заведомо неэффективную модель данных. Прозрачный страничный уровень класса gigaRAM не меняет encoding, TTL или семантику Redis и не отменяет overhead миллионов объектов. Он может снижать потребность в физической DRAM только для реально менее активных страниц, оставляя горячие в DRAM; большие общие структуры и случайный доступ способны оставаться горячими, а NVMe добавляет задержку. Публичное позиционирование не подтверждает фиксированный результат или совместимость для любого Redis.

Иногда более дорогая модель с отдельными ключами выигрывает в совокупной стоимости: проще TTL, равномернее cluster placement, меньше зона последствий и легче миграция. Иногда группировка даёт важную экономию и одновременно лучше отражает сущность бизнеса. Выбор должен сохранять не максимальную плотность любой ценой, а полезную ёмкость при прежнем поведении приложения. Главный вывод: у миллионов коротких значений повторяется не только payload, но и инфраструктура каждого самостоятельного объекта. Redis даёт инструменты увидеть цену и представление, однако не универсальный коэффициент. Экономия появляется там, где общую структуру можно разделить между логически связанными элементами без потери нужной семантики.

Источники. MEMORY USAGE определяет оценку выделений ключа, значения и административного overhead, включая выборку вложенных типов.

OBJECT ENCODING показывает внутреннее представление и его возможную автоматическую смену.

MEMORY STATS и INFO используются только для общего контроля выделений.

Memory optimization описывает компактные агрегаты, группировку в hashes и её компромиссы.

Продуктовая страница используется только для позиционирования страничного уровня.