Valkey 9.1.1: 64 байта на короткий SET вместо 102 в 7.2.14

29 сентября 2026 года Percona выложила замер памяти Valkey. Короткий SET в сборке 9.1.1 занимает 64,02 байта на ключ. В 7.2.14 тот же ключ занимал 102,48 байта. Разница 37,5%.

В замере Percona Team от 29 сентября сравнивали пять сборок из исходников, не пакеты дистрибутивов: 7.2.14, 8.0.10, 8.1.9, 9.0.5 и 9.1.1. Аллокатор везде jemalloc 5.3.0, чтобы версия была единственной переменной. Режим standalone, не кластер. У процесса maxmemory 0, пустой save и appendonly no: ни вытеснения, ни снимка, ни AOF.

Нагрузка описана так: valkey-benchmark, тип set, миллион запросов, -r тоже миллион, значение 16 байт. Из-за коллизий ключей уникальными остаются около 63,2%. В таблице это примерно 632 тысячи. Память пустого процесса вычитают и считают, сколько байт добавляет каждый ключ. По словам Percona, так устроен официальный метод бенчмарка Valkey.

По словам Percona, 37,5% актуальны там, где много коротких строковых ключей: кэш, сессии, авторизация, мелкие realtime-ключи. Для крупных значений такой экономии никто не обещает. Самый большой шаг в ряду, по их же таблице, не 9.1, а переход 7.2 к 8.1: минус 29,8%.

В начале поста есть набросок «было 1 ТБ на 7.2»: 1000 ГБ, потом около 800 ГБ на 8.0, 640 ГБ на 8.1 и 630 ГБ на 9.1. По таблице замера цифры другие: 800 ГБ от 1000 это минус 20%, а у 8.0.10 в замере минус 7,8%. 640 ГБ это около минус 36%, а у 8.1 к базе минус 29,8%. Для 9.1 цифры близки: 630 ГБ это почти те же 37%. RAM под апгрейд лучше считать по байтам на ключ из таблицы, а не по круглым цифрам из вступления.

7.2.14, 8.0.10, 8.1.9, 9.0.5, 9.1.1

Одинаковые условия, разное число уникальных ключей из-за случайных коллизий. Цифры из таблицы Percona:

  • 7.2.14: 632 181 ключ, 61,78 МБ, 102,48 байта. База.
  • 8.0.10: 632 565 ключей, 57,00 МБ, 94,49 байта. Минус 7,8% к 7.2.14.
  • 8.1.9: 632 573 ключа, 43,43 МБ, 71,98 байта. Минус 23,8% к предыдущей сборке и минус 29,8% к 7.2.14.
  • 9.0.5: 631 672 ключа, 43,36 МБ, 71,99 байта. К 8.1 ноль. К 7.2.14 те же минус 29,8%.
  • 9.1.1: 631 985 ключей, 38,58 МБ, 64,02 байта. Минус 11,1% к 9.0.5 и минус 37,5% к 7.2.14.

На том же железе таких мелких ключей влезает примерно на 60% больше: 102,48 разделить на 64,02. Почти весь путь сделала 8.1. Ветка 9.0 тут ничего не дала: 71,98 и 71,99 байта. Percona пишет, что в том релизе память не оптимизировали.

dictEntry в 8.0 и кэш-линия в 8.1

С 7.2 на 8.0 ключ перестали хранить отдельным куском. В 7.2 dictEntry это три указателя, 24 байта: key, val, next. Сам ключ лежит в отдельном выделении памяти, SDS, и за ним приходится ходить отдельно. В 8.0 появляется embeddedDictEntry: байты ключа лежат сразу за структурой. Остаются указатели val и next плюс ключ на месте. 16 байт плюс длина ключа, одно выделение вместо двух. Итог в посте: минус 8 байт на ключ, на одно обращение к памяти меньше, конфиг менять не нужно. 102,48 минус 94,49 как раз около восьми байт.

С 8.0 на 8.1 переписали словарь. В 8.0 обычная цепочка: бакет, запись, ключ, значение. Четыре обращения к памяти и ещё по два на каждую коллизию. В 8.1 один бакет ровно 64 байта, длина кэш-линии. Ключ и значение лежат прямо в нём. В восьмибайтовом заголовке бакета упакованы бит «есть дочерний бакет», семь бит занятости и семь байт вторичного хеша. Неподходящий кандидат часто отсеивается без лишнего чтения. dictEntry как структура пропадает. Путь короче: бакет и serverObject. При коллизии данные чаще остаются в той же кэш-линии.

Оценка Percona для этого шага: около минус 20 байт на пару ключ-значение и около минус 30 байт, если у ключа есть TTL. В таблице 94,49 к 71,98, это 22,5 байта. Это и есть минус 23,8% к предыдущей сборке, самый крупный одиночный скачок 7.2-9.1.

В 9.0 по памяти изменений нет. С 9.0 на 9.1 переделали хранение коротких строк. Раньше embstr всё равно держал восьмибайтовый указатель на данные, которые лежали следом за заголовком объекта. В 9.1 для короткой строки эти же восемь байт заняты самими байтами строки. Отдельного указателя нет, отдельного выделения под значение тоже. В посте обещают до 20% экономии на строках короче 128 байт. Значения бенчмарка по 16 байт в этом диапазоне. Весь ключ при этом с 71,99 до 64,02, минус 11,1%, не минус 20. Двадцать процентов относятся к хранению самой короткой строки, а не ко всему used_memory процесса.

Это три отдельные правки, а не один переключатель. В 8.0 ключ встроили в запись словаря. В 8.1 цепочку заменили бакетом размером с кэш-линию и убрали dictEntry. В 9.1 у строк короче 128 байт убрали указатель. Отдельной директивы в конфиге под это нет.

220 МБ четырьмя SET и 100 МБ одним HSET

Второй замер Percona: миллион записей, два способа хранить одни и те же поля. Версию Valkey для этого прогона Percona не называет, так что к 9.1.1 его не привязать.

Первый способ: четыре строковых ключа на пользователя. SET user:1:name и ещё три ключа на возраст, город и счёт. В примере значения Alice, 30, Toronto, 100. Второй способ: один хеш, HSET user:1 с теми же четырьмя полями.

Четыре строки заняли 220 МБ. Один хеш занял 100 МБ. Минус 54%. Накладные расходы на ключ платятся четыре раза вместо одного. Для кэша, где у одной сущности есть имя, мета и флаг, выбор схемы хранения часто важнее, чем шаг с 9.0.5 на 9.1.1.

Хеш выигрывает не всегда. По памяти на больших объёмах лучше хеш. TTL на отдельное поле проще со строками: у строки родной EXPIRE, а у хеша TTL на поле до 7.4 не было. Несколько полей за один запрос удобнее в хеше, через HSET или HMGET, строкам на то же нужны отдельные запросы. В кластере хеш и так один ключ и один слот, а строки надо вручную склеивать хештегом. На одном VPS без кластера последний пункт ничего не меняет.

used_memory на VPS и лабораторные 16 байт

На обычном сервере с сайтом рядом с nginx, php-fpm и MariaDB часто крутится отдельный процесс кэша. Объектный кэш WordPress и сессии PHP живут в нём. Файловый fastcgi_cache и proxy_cache nginx к этой таблице не относятся: они не станут легче на 37,5% от нового Valkey.

64,02 байта сняты с пустого инстанса, куда бенчмарк записал короткие SET по 16 байт, минус память пустого процесса. Объектный кэш сайта хранит сериализованные данные, часто гораздо длиннее. Доля накладных расходов на ключ там меньше, и 37,5% к своему RSS применять нельзя. Если ключей много, а значения короткие, профиль ближе к таблице. Сессия одним ключом ближе к схеме «одна сущность, один ключ», а не к четырём SET на пользователя.

Замер сделан на своих сборках с одним jemalloc. Пакет из репозитория, старый пакет с именем redis и контейнер со своим тегом могут быть другой сборкой. Если у вас не 9.1.1, нижняя строка таблицы к вам не относится. Если 7.2, ваш расход ближе к верхней строке. Переход с 8.1 на 9.0 память не экономит: в замере разницы нет.

Новое хранение ключей из 8.0, по словам Percona, включается вместе с обновлением, правка конфига не нужна. Что обновление пакета сразу переложит уже лежащий дамп и RSS упадёт до лабораторной цифры, из замера не следует: байты считали на ключах, которые бенчмарк записал в этот процесс.

Флаги из теста в unit боевого кэша копировать не надо. maxmemory 0, пустой save и appendonly no выключили вытеснение, снимок и AOF, чтобы в цифру не попала персистентность. На кэше с сессиями это уже другой режим, не «как в статье, чтобы было меньше RAM».

Смотреть версию и объём можно без записи. Если клиент называется redis-cli, тот же INFO читается им. В server строка версии, в memory поле used_memory. Это не те 64 байта на ключ из замера: в эту цифру входят пустой процесс, фрагментация, клиенты и сами значения. Смотреть стоит на порядок и динамику, а не на совпадение с таблицей до сотых. Если клиент просит пароль, это requirepass вашего конфига.

valkey-cli INFO server
valkey-cli INFO memory

Копию конфига имеет смысл сделать тогда, когда после замера вы реально правите unit или конфиг кэша. Сам INFO файлы не трогает. nginx -t здесь ни при чём, пока вы не меняете прокси к этому процессу. Репозиторий, из которого ставили бинарник, и имя пакета (valkey или ещё redis) путают чаще, чем флаги бенчмарка: в INFO server будет та версия, которая реально слушает порт, а не та, что в заголовке поста.

Для коротких строк замер показывает заметную разницу между 7.2.14 и 9.1.1, и почти вся она приходится на 8.1. Ветка 9.0 память не уменьшила. Переход с четырёх ключей на один хеш во втором примере дал 54%, версию Valkey для этого прогона Percona не называет. На VPS сначала смотрите версию процесса и то, как устроены ключи, а уже потом считайте, хватит ли RAM.

Источники и ссылки


Комментарии загружаются…