Распаковывая Valkey или миллион RPS на BSD-клоне Redis
В этот статье мы поделимся некоторыми результатами тестирования производительности Valkey (BSD-клон Redis).
В этом году Redis, «кеш с персистентностью», мимикрирующий под СУБД и даже умеющий работать в режиме распределенного кластера — фантастически успешный проект, да и просто пример отличного продукта — сменил лицензию. Подробное рассмотрение лицензионной политики, включающее разбор лицензий Open Source и Source Available — выходит за рамки этой статьи. Скажем лишь, что это всё продолжение битвы «против облачных провайдеров», которые «пользуются» результатами «настоящих» open source проектов, предоставляя услуги management service.
Итак, Redis cменил лицензию, и почти сразу же сотрудники AWS (Amazon Web Services, крупнейшего облачного провайдера) в сотрудничестве с другими облаками объявили о выпуске форка, Valkey: https://valkey.io/. Лицензия BSD, контроль проекта у Linux Foundation, участвуют многие контрибьюторы Redis. Major contributor проекта Valkey — Madelyn Olson (AWS Engineer and Valkey project maintainer, https://www.linkedin.com/in/madelyn-olson-valkey).
DragonFly, один из наиболее успешных коммерческих «клонов» Redis, выпустил достаточно подробный обзор отличий: https://www.dragonflydb.io/guides/valkey-vs-redis. Однако в секции про производительность там сказано странное:
▪️ Valkey: 1.19M requests per second with new I/O threading
▪️ Redis: Known for high performance but varies based on version
Цитата про миллион запросов в секунду — действительно звучала от контрибьюторов Valkey в комментарии к этим патчам: https://github.com/valkey-io/valkey/pull/861. Утверждение «known for high performance but varies based on version» кажется черезчур оценочным и малоинформативным, но мы не будет судить маркетинг DragonFly строго, он уже удивлял нас некоторыми опусами (https://habr.com/ru/articles/747814/). Так что же там с производительностью?
Disclaimer: Автор этой статьи никаким образом не связан ни с одним из упомянутых ниже проектов, не получал финансирование и не преследует никакие интересы упомянутых организаций или их конкурентов — вендоров софта либо cloud-компаний. Данный материал является краткой версией туториала образовательного облака для бекендеров — DevHands. Если вы вдруг хотите научиться самостоятельно управлять своим linux-сервером — возможно, вам будет интересен этот курс: https://devhands.ru/linux. Основная цель — дать бекендерам «альтернативый опыт управления облачными ресурсами», который часто скрыт за интерфейсами облачных провайдеров.
Налицо два мифа. Первый: Redis не скейлится по ядрам. Второй: Valkey скейлится, или по крайней мере — скейлится лучше Redis. Постараемся разобраться, так ли это.
Поскольку Valkey — проект новый, то его пока нет ни в каких репозиториях пакетов, поэтому скорее всего придется его собрать руками. Ниже приводится пример установки на сервер с Убунтой.
Релиз-кандидат 8.0.0-rc2 лежит тут: https://github.com/valkey-io/valkey/releases. Качаем его и собираем в отдельной папке, чтобы не путаться ни с каким другими (я предпочитаю /local):
fisher@t1:/local/src$ wget https://github.com/valkey-io/valkey/archive/refs/tags/8.0.0-rc2.tar.gz
Valkey, так же как и Redis, собирается и ставится буквально несколькими командами:
fisher@t1:/local/src$ tar zxvf valkey-8.0.0-rc2.tar.gz
...
fisher@t1:/local/src$ cd valkey-8.0.0-rc2
fisher@t1:/local/src/valkey-8.0.0-rc2$ PREFIX=/local/valkey-8.0.0-rc2 make install
Если у вас что-то не соберется — не беда, вероятнее всего, вам не хватает чего-то из сборочного тулчейна. Начните с главного пакета консольных make-джедаев build-essential:
fisher@t1:/local/src/valkey-8.0.0-rc2$ sudo apt-get install build-essential -y
Если вдруг этого будет недостаточно, по названию ошибки обычно можно понять, что не найдено, поискать через apt-cache search и поставить соответствующий пакет.
Итак, наш valkey собран, по умполчанию он создает только бинари и redis-симлинки для полной совместимости:
fisher@t1:/local$ ls -la /local/valkey/bin/
total 30572
drwxrwxr-x 2 fisher fisher 4096 Sep 7 06:33 .
drwxrwxr-x 3 fisher fisher 4096 Sep 7 06:33 ..
lrwxrwxrwx 1 fisher fisher 16 Sep 7 06:33 redis-benchmark -> valkey-benchmark
lrwxrwxrwx 1 fisher fisher 16 Sep 7 06:33 redis-check-aof -> valkey-check-aof
lrwxrwxrwx 1 fisher fisher 16 Sep 7 06:33 redis-check-rdb -> valkey-check-rdb
lrwxrwxrwx 1 fisher fisher 10 Sep 7 06:33 redis-cli -> valkey-cli
lrwxrwxrwx 1 fisher fisher 15 Sep 7 06:33 redis-sentinel -> valkey-sentinel
lrwxrwxrwx 1 fisher fisher 13 Sep 7 06:33 redis-server -> valkey-server
-rwxr-xr-x 1 fisher fisher 6584176 Sep 7 06:33 valkey-benchmark
lrwxrwxrwx 1 fisher fisher 13 Sep 7 06:33 valkey-check-aof -> valkey-server
lrwxrwxrwx 1 fisher fisher 13 Sep 7 06:33 valkey-check-rdb -> valkey-server
-rwxr-xr-x 1 fisher fisher 7494488 Sep 7 06:33 valkey-cli
lrwxrwxrwx 1 fisher fisher 13 Sep 7 06:33 valkey-sentinel -> valkey-server
-rwxr-xr-x 1 fisher fisher 17214392 Sep 7 06:33 valkey-server
Я предпочитаю такую структуру redis и valkey, в которой абсолютно все «артефакты» — от конфигурации и data-файлов до pid-файла и логов лежат вместе:
fisher@t1:/local$ ls -la valkey/
total 28
drwxrwxr-x 7 fisher fisher 4096 Aug 23 11:37 .
drwxr-xr-x 6 fisher root 4096 Sep 7 06:34 ..
drwxrwxr-x 2 fisher fisher 4096 Sep 7 06:34 bin
drwxrwxr-x 2 fisher fisher 4096 Sep 7 06:34 conf
drwxrwxr-x 26 fisher fisher 4096 Sep 7 06:34 lib
drwxrwxr-x 2 fisher fisher 4096 Sep 7 06:34 log
drwxr-xr-x 2 root root 4096 Sep 7 06:34 run
Если будете делать так же — убедитесь, что поправили конфигурационный файл redis. Он не копируется при make install, но остается лежать в корне директории с сорцами:
fisher@t1:/local$ ls -la /local/src/valkey-8.0.0-rc2/valkey.conf
-rw-rw-r-- 1 fisher fisher 112839 Sep 3 16:00 /local/src/valkey-8.0.0-rc2/valkey.conf
Давайте запустим valkey и убедимся что он работает:
sudo /local/valkey/bin/redis-server /local/redis/conf/valkey-7023.conf
Я намеренно для простоты запускаю его под рутом на тестовой машине, в обычном окружении вам конечно нужно сделать отдельного юзера valkey/redis, сделать его owner’ом всех файлов, куда может вестись запись (данные, логи, pid-файл), и завести systemd unit-файл.
Проверяем через INFO:
fisher@t1:/local/valkey$ ./bin/valkey-cli -p 7023
127.0.0.1:7023> INFO keyspace
# Keyspace
db0:keys=0,expires=0,avg_ttl=0
Всё работает, можно приступать в тестированию. Здесь нужно сделать несколько важных замечаний.
▪️ Чтобы не перегружать читателя, мы намеренно не приводим такие данные с тестируемой машины, как CPU usage, load average и т.д. Valkey, насколько это известно, не делал большого количества оптимизаций, поэтому никакой принципиальной разницы по потреблению ресурсов в тестах замечено не было. В общем случае, или при сравнении, например с альтернативными решениями типа DragonFly или KeyDB — учёт использования ресурсов процессора, конечно, необходим.
▪️ Чтобы не тестировать дисковую подсистему, во всех тестируемых кейсах была полностью выключена персистентность: ни Redis, ни Valkey никакие данные не писали на диск — ни в режиме снэпшотирования, ни в режиме aof-лога.
▪️ Стрельбы проводились локально. Машина, на которой проводилось тестирование, железный сервер Xeon Gold 24/48 vCPU и 128G памяти — его «за глаза» хватало при том количестве как тредов стрелялки, так и тредов
Внутри у Redis есть утилита для нагрузочного тестирования, redis-benchmark, которая очень много чего умеет: открыть много соединений, пошерить их между несколькими тредами. Чего она не умеет — это ограничивать поток RPS, поэтому построить максимально информативные latency-throughput диаграммы мы не сможем, но сейчас нам это и не требуется.
Сначала запустим «стрелялку» на однопоточный valkey:
/local/redis/bin/redis-benchmark -r 10000 -c 64 --threads 8 -p 7023 -t set -n 10000000 -d 64
(...)
Summary:
throughput summary: 181785.14 requests per second
latency summary (msec):
avg min p50 p95 p99 max
0.332 0.016 0.303 0.559 0.631 2.191
В этом тесте мы запускаем 8 тредов «стрелялки», которые шеряд тежду собой 64 соединения с valkey (порт 7023). Выполняется 10 миллионов операций set с 10000 ключей, каждый из которых имеет размер 64 байта. Результирущая пропускная способность составляет 181785 запросов в секунду, с 95-м перцентилем менше 1й милисекунды (0.559 msec).
Включим мульти-тредовый режим. Для этого в конфигурационном файле нужно поставить какое-то осмысленное значение для параметра io-threads, мы поставим 8:
io-threads 8
Перезапустим valkey и снова «натравим» benchmark:
fisher@t1:/local/redis$ /local/redis/bin/redis-benchmark -r 10000 -c 64 --threads 8 -p 7023 -t set -n 10000000 -d 64
(...)
Summary:
throughput summary: 850267.88 requests per second
latency summary (msec):
avg min p50 p95 p99 max
0.056 0.008 0.055 0.071 0.103 1.183
Почти миллион set в секунду, вполне достойно!
На get-операциях получаем вожделеннный миллион RPS:
Summary:
throughput summary: 1024590.12 requests per second
latency summary (msec):
avg min p50 p95 p99 max
0.050 0.008 0.055 0.063 0.079 1.911
Отличный результат. А что же Redis? Тут важно сказать, что io-threads и поддержка мульти-тредового io появилась как раз у Redis, команда Valkey её усовершенсововала. Вам может показаться это странным, но Redis скейлится по ядрам! Покажем это.
Redis на чтение в режиме одного ядра:
/local/redis/bin/redis-benchmark -r 10000 -c 64 --threads 8 -p 7022 -t get -n 10000000 -d 64
Summary:
throughput summary: 190839.69 requests per second
latency summary (msec):
avg min p50 p95 p99 max
0.317 0.016 0.287 0.551 0.599 2.175
Обратите внимание, в однопоточном режиме redis не valkey, даже наоборот. Потестим мульти-тредовый io (те же 8 ядер):
/local/redis/bin/redis-benchmark -r 10000 -c 64 --threads 8 -p 7022 -t set -n 10000000 -d 64
Summary:
throughput summary: 408096.66 requests per second
latency summary (msec):
avg min p50 p95 p99 max
0.130 0.016 0.135 0.167 0.207 1.911
Как видите, скейлится, но заметно хуже!
Итак, какие выводы можно сделать из этого небольшого исследования?
▪️ Первое, Redis таки скелится по ядрам, пусть и не очень хорошо. Примерно вдвое производительность Redis можно увеличить, подбирая оптимальный io-threads.
▪️ Второе, Valkey действительно усовершенствовали много-тредовый режим Redis, и мы увидели двойной прирост RPS при одном и том же количестве тредов (8).
Здесь нужно сказать, что вообще сильное увеличение параметра io-threads ни у Valkey, ни у Redis, не только не приводит к увеличению пропускной способности, но наоборот значительно её снижает. Связано это с тем, что треды начинают излишне конкурировать между собой, и оверхэд на мульти-тредовый режим оказывается значительно сильнее. Ну и напомним, что вообще мульти-тредовый режим в Redis/Valkey потому и называется io-threading поскольку не распаралелливает всю цепочку выполнения запросов, так что в действие вступает закон Амдала.
Напоследок можно порассуждать о кластерном режиме Redis. Действительно, у Redis есть отличный кластерный режим, в котором можно поднять несколько экземпляров на одном хосте. Кластер без реплик заводится и скейлится по ядрам процессора лучше, чем в мутильтитредовом режим — это мы проверяли. Похоже, для пользователей Redis это пока единственный способ утилизировать ядра по-настоящему мощных машин максимальным образом. Но кластер делает много чего. Поэтому сравнение с «настоящим» in-memory кешом (без персистентности) можно было бы сделать, например, поставив число реплик в 0, и направив aof в /dev/null (я кстати не проверял — сработает ли последний такой трюк, в противном случае set будет приводить к значительной дисковой активности, и честного теста не получится).Подробное тестирование производительности кластера мы делаем на практических занятиях и оно выходит за рамки этой статьи.
Комментарии и замечания к этой статье и к методике тестирования, и по идеям для дальнейшего тестирования — горячо приветствуются. ТГ-канал автора: https://t.me/rybakalexey.