Всем привет! Пополняю интернеты “еще одной” статейкой с бенчмарками популярных СУБД. Захотелось выяснить, каков оверхед на протокол, работу с сетью и клиентскими соединениями в самом простом кейсе - когда таблица либо совсем пуста, либо данных так мало, что все они в памяти.

Бенчмарк на Rust. Я попросил написать его Chat-GPT, и он отлично справился. 

Исходник здесь - https://github.com/geneopenminder/rust-db-bench

Сравнивал с официальными scylla-bench и redis-bench - результаты схожи. 

Для теста я специально использовал слабую железку - Orange Pi 3b. Это китайский аналог малинки. Захотелось узнать, на что она способна.

http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-3B.html

Quad-core 64-bit Cortex-A55 processor 1.8GHz
8GB(LPDDR4/4x)
1000Mbps Ethernet (On-board PHY chip: YT8531C-CA )
SD Flash U1: 10 MB/Sec min write speed.

Она в 15-30 раз слабее моего игрового laptop на core i7. Cреда для тестирования получилась идеальной. Генератор нагрузки гораздо мощнее сервера, а гигабитный ethernet всё равно что 400Gbit на реальном кластере. К сожалению, m2 ssd на ней так и не завелся, пришлось тестить на SD карте классом скорости 10MB/Sec. Из за этого цифры записи можно не воспринимать как адекватные. В конце статьи дополнительно приведу результаты, полученные на своем игровом лэптопе.

Для теста я взял Redis, Cassandra, Scylla, MongoDB и Postgres по возрастанию предоставляемых возможностей и сложности архитектуры СУБД. Да, в каком то смысле это сравнение слона, карася и утки. Но правда в том, что какая бы БД не была в вашей системе, вы всё равно прикрутите её дополнительно как key-value к части бизнес логики. Так что, сравнение допустимо. Redis архитектурно работает в режиме одного потока, поэтому я запускал 4 отдельных процесса-шарда - один на ядро. Распределение запросов по шардам происходило на стороне клиента. Остальные же базы активно используют все 4 ядра по умолчанию. Mongo по определенным причинам запускал в докере (и даже не с host network), остальные базы просто устанавливал в систему. Настройки дефолтные, т.к. нас не интересуют размеры кэшей и индексов в данном тесте, они не повлияют ни на что.

Подробнее подсветим несколько основным моментов:

  • диск вообще нигде не используется для тестов на чтение, всё всегда лежит в памяти. запись в конкретном тесте нас в целом не интересует. это данные приведены просто для полноты картины. 

  • в этом бенчмарке я попытался нивелировать ту часть внутренней логики, которая занимается непосредственно самим поиском данных - бегает по индексам, страницам памяти, ходит на диск и тп. меня интересовали все остальные шаги, которые она (база данных) проходит начиная от получения пакета по сети и до момента отправки ответа обратно.  

Из всего набора выделяется только Redis (это вообще не база данных), все же остальные СУБД во многом схожи. Им ровно так же надо получить сообщение, десериализовать его, обогатить prepared statement, провести все необходимые проверки, пройтись по плану запроса, получить данные, дополнить их в ответе метаданными колонок, проделаеть еще какую то тучу работы, отправить ответ. Но каждая делает это по разному. И, казалось бы, супер навороченная Postgres должна была увязнуть в нагромождении логики, а более простая с этой точки зрения Scylla обогнать её в такого рода простых сценариях. Но на деле это оказалось не так. Redis же здесь не с целью показать, что надо выбросить SQL решения и признать их мертвыми, потому что они слишком медленные. Он здесь для контраста и сравнения порядка величин, какова может быть скорость отдачи проиндексированных данных находящихся в памяти в принципе. Так сказать, референсное значение чистого HashMap приклееного к сокету.

Версии ОС и баз данных.

Redis 7.0.15
Cassanda 5.0.5
Scylla 2025.3.0-0.20250827
Mongo 8.0.13
Postgres 15.04
Сервер -  armbian x64 - Linux 6.1.115-vendor-rk35xx
Генератор нагрузки - Ubuntu 25.04 i7-13620H DDR5-4800 32 GB

Соединены через гигабитный ethernet по кабелю.

Везде использовал prepared statements (далее PS), где это возможно. Для каждого клиентского потока свой connection.

Табличка c payload 108 байт, что вполне сравнимо с реальными сущностями в продакшн системах. 

CREATE TABLE vectors (
LONG primary key,
INT[16] vector,
TEXT // 36 bytes utf-8 
);

Погнали!

Сперва посмотрим перфоманс в режиме пустой таблицы. Интересно же, какой оверхед на протокол. Не понимаю, почему никто это не делает. 

Внизу каждой диаграмм указано количество клиентских потоков/сессий/соединений.

Казалось бы, что сложного отправить ответ с пустым rows, да еще и когда таблица без записей. Но разница получилась значительная. Redis ожидаемо в топе. Scylla чуть быстрее Cassandra. От монги я многого не ждал, там нет аналога PS. Postgres меня удивил, если честно. 

Теперь читаем в табличку с заполненными данными. Датасет небольшой - 1млн записей. Здесь главное, что на каждое чтение возвращается ответ в 1 row с payload размером 108 байт. Селектим рандомными id из массива всех вставленных в таблицу ключей. Всё, что здесь происходит - lightweight парсинг препарашки, поиск и сериализация. Помимо данных в ответе всегда присутствуют имена колонок. Везде, кроме Redis. Там просто blob. 

C Redis всё понятно, Cassandra просела еще сильнее. Почему она стала медленнее Mongo, для меня загадка. При селекте из пустой таблицы она её обгоняла. Забавно, что Postgres всё равно быстрее Scylla.

Я попутно смотрел disk io - Postgres очень много пишет. Как я упоминал выше, на железке SD карта, поэтому результаты не стоит воспринимать слишком всерьез. Но разброс цифр заметный, и это заставляет задуматься. 5к RPS это почти 500 млн записей в сутки. На этом то калькуляторе!

Теперь посмотрим на latency. Привел цифры только select single row. Для столбцов с одним клиентом я указал min latency, для остальных (2-64) - average latency. Кассандру я долго прогревал перед каждый тестом, наблюдая как RPS постепенно возрастает примерно в 7-8 раз от значений после холодного старта.

С Cassandra и Mongo ничего неожиданного. Как и с Redis. Scylla снова оказалась медленнее Postgres. Здесь действительно видно, насколько слаба эта железка. 500 микросекунд на запрос это ж целая вечность.

Теперь самое интересное. Напомню, для Scylla, Cassandra и Postgres я использовал prepared statements. В Redis это не нужно. В Mongo прямого аналога нет. Посмотрим, сколько байт ходит по сети на 1 запрос. Оверхэд сетевого стека в моём случае 66 байт - это фрейм Ethernet, заголовок TCP + доп параметры протокола. Соответственно, от каждого столбца можно отнять эту цифру, чтобы понять чистый payload. Проверял через tcpdump. Empty - это select из пустой таблицы, read full - select с ответом в 1 row. Ну и insert.

Что тут интересного? Redis не отправляет ничего лишнего. У Cassandra и Scylla одинаковый протокол, использовал один и тот же драйвер и даже код бенчмарка, но размер сообщений местами разный. Я так и не выяснил, почему. Монга из за того, что там нет полноценных PS, посылает в запросе очень много данных. Redis не отправляет имена колонок в ответе. А у Cassandra какой то жирный протокол, что даже Postgres местами лучше!

В конце обещанный бенч на ноуте. Север и генератор нагрузки разделены через cpuset по разным cpu core. Redis запускал как 4 отдельных инстанса.

Select из пустой таблицы

Вот вам и 500k RPS. А Postgres - 248k.

Select single row.

Собственно, те же +-500k RPS. Загрузка сети (на loopback) доходила до гигабита на чтение. И чем вас не устраивает Postgres как mem cache? )

Для 1 клиента указано минимальное latency, для остальных столбцов среднее.

Что мы видим? Цифры в районе 10-30 микросекунд. И это просто лэптопный процессор, а не серверный Xeon. На хорошем железе они могут быть и меньше 10, уверен. Да, это через loopback, и тем не менее.

Выводы.

Postgres великолепна. Scylla действительно быстрее Cassandra. И дело не в Java. Видимо, кассандру пришло время рефакторить. Я ожидал, что Scylla должна была быть никак не медленнее Postgres в такого рода тестах, но реальность другая. До сих пор не покидает сомнение, что я протестировал её как то не так, уж очень низкие цифры.

И, честно говоря, не понимаю такую большую разницу в latency.  

Я готов теперь поверить в то, что RPS на современном железе для in-memory mode - 50-100k на ядро. А кластер запросто может держать больше миллиона. Например, на той же Scylla. Latency для внешнего кэша на подобных Redis решениях (или на своем велосипеде - ведь у всех же руки чешутся запилить своей кэшик) хотелось бы видеть в пределах 10-20 мкс. 

По итогу вопросов для меня больше чем ответов, может в комментах подскажут, почему такие разные цифры.

  1. Почему Scylla настолько медленнее Postgres?

  2. Почему Redis настолько быстрее даже в режиме чтения пустой таблицы?

  3. Почему Cassandra так сливает Scylla? 

Спасибо, что дочитали до конца!

Комментарии (0)


  1. valera_efremov
    18.09.2025 18:25

    Коннект через сокет или TCP?


    1. Openminder Автор
      18.09.2025 18:25

      В каком смысле?


      1. Openminder Автор
        18.09.2025 18:25

        Всё, понял вопрос. TCP.


  1. baldr
    18.09.2025 18:25

    Но правда в том, что какая бы БД не была в вашей системе, вы всё равно прикрутите её дополнительно как key-value к части бизнес логики. Так что, сравнение допустимо. Redis работает в режиме одного потока, поэтому я запускал по 4 инстанса - один на ядро. Остальные же базы активно используют все 4 ядра. Mongo по определенным причинам запускал в докере, остальные базы просто устанавливал в систему. Настройки дефолтные, т.к. нас не интересуют размеры кэшей и индексов в данном тесте.

    Вот этот абзац точно написан через ChatGPT, потому что каждое предложение очень нелогично.

    То есть вы сравниваете in-memory KV-хранилище с транзакционной базой данных? И удивляетесь что почему-то на диск пишет?

    Настройки дефолтные, т.к. нас не интересуют размеры кэшей и индексов в данном тесте.

    В смысле не интересуют? Из-коробки любая база сконфигурирована так, чтобы просто запуститься на минимальном железе, но производительность никто не обещал.

    Redis работает в режиме одного потока, поэтому я запускал по 4 инстанса - один на ядро.

    То есть в кластере? Или по отдельности? А тесты шли на каждый отдельно или только на один? Что за ерунда тут происходит? В linux можно прилепить процесс к одному ядру процессора и он только его и будет использовать, если вы этого хотели добиться. Зачем 4 инстанса-то?


    1. Openminder Автор
      18.09.2025 18:25

      1. Почему бы и не сравнить в режиме чтения? В данном случае это всё лежит в памяти. И где вы увидели, что я удивлялся записи на диск? Redis тоже может записывать, если уж на то пошло. Они все записывают, и у всех можно тюнить wal лог и fsync. Но исследование записи меня не интересовало, там вообще всё будет сильно по разному, что и получилось.

      2. Когда у вас записей на 100 мегабайт в памяти, все дефолтные настройки как раз годятся. Можете почитать официальные доки, как запускать бенч на Scylla и той же Postgres. Они интересуют, когда у вас режим работы базы совсем другой. В данном случае влияние настроек будет ничтожно.

      3. Redis работает в одном процессе (и потоке), так он спроектирован. Можно запустить несколько шардов на одной системе прибив их каждый к своему cpu, что я и сделал.
        Конечно же, если вместо 1го инстанса запустить 4 шарда, перфоманс не вырастет в 4 раза, потому что сеть у вас разгребает всё тот же один поток ядра.
        Выигрышь получается процентов 20-30. Я не стал выкладывать результаты с 1м процессом Redis, так как мог использовать 4. И написал об этом. С точки зрения максимального использования ресурсов системы это выглядит логичным.

      PS: поправил в статье про Redis. Мне показалось, что и так будет понятно про несколько шардов, но, увы, нет.


      1. baldr
        18.09.2025 18:25

        Почему бы и не сравнить в режиме чтения? В данном случае это всё лежит в памяти.

        Или не в памяти. Postgres по-умолчанию: shared_buffers=128MB , work_mem=4MB Ну ещё max_connections=100, но вы могли в них и не упереться. Если вы рандомно читаете из таблицы, то будете с диска грузить постоянно новые страницы. Кстати, версия 15 немного старовата - уже 18я вышла.

        С остальными базами не могу нормально прокомментировать, но опираться на дефолт и мерять скорость - не очень объективно.

        Опять же - Redis тут вообще ни к месту сравнивать. У него доступ O(1), а постгрес сначала посчитает номер закоммиченной транзакции чтобы вам строку отдать.


        1. Openminder Автор
          18.09.2025 18:25

          То, что оно было в памяти, это 100%. Я еще раз повторюсь, что датасет ничтожный. К тому же, я проверял сериализацию даже когда у меня каждый запрос брал запись в одним и тем же ключом. А уж момент чтения пустой таблицы какие настройки могут затронуть кроме количества соединения? Шифрование, сжатие? Сжатия нет, шифрование отключеною. Количество коннектов бы вылезло во время теста однозначно, если бы их не хватало. Но замечание засчитано, спасибо. Я посмотрю, какие дефолтные параметры выставились у Postgres, попробую их поднять, если вызовут подозрения и перепроверю. Про то, как Postgres ищет запись, я представляю. Конечно там не O(1), как у Redis. Я дополнил вступление к статье и написал, что Redis я использовал для референса. Уверен, мало кто представляет себе, какова реальная разница в скорости получения данных из памяти для Redis и Postgres. Тут я это подсветил, в том числе. Меня больше удивило то, как быстра Postgres.


          1. kirik
            18.09.2025 18:25

            То, что оно было в памяти, это 100%.

            чёйт? давайте вы добавите хотя бы в составной индекс все поля, тогда хоть какая-то уверенность будет что всё в памяти, и то не факт.

            Монга туда же, там снаппи запаковано всё, кроме индексов, которые частично или полностью в памяти будут. Соответственно чтение - это поиск по индексу и чтение с диска. Хорошо если данные в дисковый кэш попадут, а то и не факт. Очень опрометчивое заявление про 100%. Если бы это было правдой, то вы бы получили сравнимые результаты доступа к данным, не так ли? ;)


  1. Iselston
    18.09.2025 18:25

    Хранит ли Редис у Вас данные на диске или всё в памяти?

    Раз уж тут сравнения теплого с мягким, то попробуйте провести тест с insert миллиона строк в каждую из баз, а потом их удалением - удивитесь тому как редиска просядет.


    1. Openminder Автор
      18.09.2025 18:25

      Какой диск вообще, там миллион записей всего. Естественно, там всё в памяти. Меня не интересовал инсерт в данном случае. Только как база читает из памяти. Инсерт миллиона строк в базу происходит за 10-20 секунд, как и удаляется. Что там может просесть, я не понимаю. Наверное, вы имели ввиду миллиард строк?


  1. oookkdjjjdjdj
    18.09.2025 18:25

    Вывод общий, в боевых условиях скорее упрёшься в сеть, логику приложения или диски, чем в саму базу