Привет, Хабр! Меня зовут Роман Ананьев из команды DBA в Авито. В этой статье я расскажу о поиске альтернативы для многошардовых инсталляций MongoDB. Основная цель исследования — найти базу данных с поддержкой автошардирования, которая упростит эксплуатацию и лучше утилизирует ресурсы.
Когда проект вырастает из уютных нескольких шардов MongoDB и превращается в огромную систему на сотни узлов, стандартные подходы к масштабированию начинают пожирать железо и время инженеров. Здесь продуктовый инженер упирается в ресурсы, и у него начинается головная боль, как перелить данные из одних шардов в другие.
Это текст не про то, что MongoDB плохая, она — прекрасный стандарт рынка, в топ-5 движков БД. Я расскажу про то, что происходит, когда у стандартной технологии заканчивается запас прочности на нужном масштабе, и про то, как мы в Авито перебрали множество NoSQL и NewSQL кандидатов, чтобы найти одного подходящего.
В статье я разберу результаты технического исследования, проведённого командой DBA. Мы сравнили производительность, утилизацию ресурсов и архитектурные грабли Cassandra 5, FoundationDB и других БД. Также объясню, почему погоня за низкой latency в случае с FDB обернулась трёхкратным перерасходом дискового пространства.
В этой статье:

Возникновение проблемы. Почему мы решили уйти от MongoDB
В Авито MongoDB — это база, которую используют многие продуктовые команды. Она крутится на платформе DBaaS K8s, поддерживает классический self-sharding и в продакшене этих инсталляций — сотни, а если считать по шардам, то счёт идёт на тысячи отдельных MongoDB-инстансов. Есть и одношардовые инсталляции для небольших сервисов, и многосотшардовые монструозные хранилища на терабайты данных для высоконагруженных сервисов вроде Messenger.
Технология рабочая, обкатанная, с огромным сообществом. Но у self-sharding модели MongoDB есть фундаментальная архитектурная особенность, которая с ростом числа шардов превращается из мелкой неприятности в системную проблему.
В классическом сетапе MongoDB шард — это отдельный реплика-сет: три ноды с данными плюс четвёртая hidden-нода специально для бэкапов, чтобы снять нагрузку с боевых реплик. Получается, что на каждый шард приходится четыре физических хоста ради данных, которые логически занимают объём одного шарда. По факту это прямой множитель на x4 к инфраструктурным расходам.
Дальше есть сложность самого шардирования. В MongoDB автоматика на стороне DBaaS позволяет добавлять новые шарды в хранилище автоматически, и здесь всё неплохо: K8s-платформа поднимает новый реплика-сет, конфигурирует его, подключает к топологии. Но есть нюанс, который не зря называется self-sharding. Каждый шард MongoDB — это изолированный кластер, который ничего не знает о данных в других шардах.
Вся логика распределения данных между шардами лежит не на стороне СУБД, а на стороне сервиса.
Так, если добавил новый шард, то сервис должен сам пересчитать и перераспределить данные с учётом нового шарда. А если удаляешь шард, то перед этим сервис должен вытащить данные из удаляемого шарда и раскинуть их по оставшимся. Вся бизнес-логика ребалансировки лежит в коде продуктовой команды, а не в движке БД. Для сервисов с одним-двумя шардами это терпимо. Для сервисов с сотнями шардов это означает, что у команды должна быть отдельная нетривиальная подсистема ребалансировки данных, то есть это должен быть фактически собственный шардинг-менеджер, написанный и поддерживаемый продуктовой командой. Именно эту боль мы и хотели закрыть, найдя технологию с честным автошардированием на уровне самого движка.
Мы также знаем, что в MongoDB есть функционал автомасштабирования — Mongos. Мы протестировали его и убедились, что скорость его работы нас не тоже устраивает.
Прежде, чем смотреть на рынок, мы выписали требования, без которых любое сравнение СУБД превращается в вкусовщину. Получился список из шести пунктов.
Простота в эксплуатации. Удобство и прозрачность создания кластера, подготовка инфраструктуры без танцев с бубном.
Простота в обслуживании. Наличие зрелого инструментария администрирования, в идеале с поддержкой плавного масштабирования вверх и вниз без даунтайма.
Простота автошардирования. Встроенный в движок механизм перераспределения данных при изменении состава кластера. Не сервис сам разберётся, а база сама разберётся.
Однокомпонентный кластер. Нам нужна однокомпонентность без отдельных ролей типа арбитров, как в MongoDB, или зоопарка вспомогательных сервисов, как Zookeeper и BookKeeper в Pulsar. В идеале нужна masterless или multi-master архитектура, где все ноды равноправны и могут исполнять любую роль.
Доступная открытая лицензия. Только open source или другая открыто лицензированная технология, никаких community-версий платных продуктов. Этот пункт казался формальностью на старте, но по факту стал одним из решающих фильтров (именно на нём срезалась ScyllaDB).
Ресурсная эффективность. Учитывая x4 по данным в MongoDB, нам хотелось технологию, которая позволяет уживаться на меньшем числе серверов при сопоставимой производительности, а также поддерживает плавную горизонтальную масштабируемость и умеет жить в multi-DC окружениях, то есть работать в нескольких дата-центрах одновременно, как и MongoDB.
С таким чек-листом мы и пошли осмотреться. Нашлось много кандидатов, но большинство отвалилось ещё до полноценного тестирования либо из-за лицензии, либо из-за архитектурной специфики, которая делала технологию нерелевантной задаче.

Кандидаты на решение проблемы
До стадии нагрузочных тестов добрались шесть кандидатов: CassandraDB, FoundationDB, ArcadeDB, CouchDB, Yandex TSaurusDB и ScyllaDB. Мы тестировали их одинаково, проверяя пять параметров:
Нагрузка. RPM, сопоставимый с продом сотне шардовых MongoDB и latency на запись/чтение
Отказоустойчивость. Как кластер переживает потерю одной или нескольких нод, а также целого дата-центра, и как он восстанавливается при их возвращении.
Масштабируемость. Изменение состава кластера вверх и вниз без боли.
Утилизация ресурсов. Что происходит с CPU, RAM и сетью после расширения кластера.
Автошардируемость. Собственно, ради чего весь сыр-бор: перераспределение данных внутри кластера при изменении его состава.
ArcadeDB — комбайн с подвохом
ArcadeDB — это мультимодельный комбайн, который пытается закрыть несколько API сразу: частичная поддержка Redis API и частичная поддержка MongoDB API, но не полная. У неё два режима работы: in-memory и persistent. В режиме in-memory она показывает великолепный RPS, а в режиме persistent производительность заметно проседает.
Решающим фактором отсева стала не производительность, а отказоустойчивость, а точнее её отсутствие. Отказ одной ноды приводил к отказу всего кластера. Для базы, которую планируется использовать как замену multi-shard MongoDB с требованием выдерживать потерю отдельных нод и целых ДЦ, это попросту неприемлемо. Плюс технология на момент тестов была совсем молодой — около года жизни, что для продовой инфраструктуры такого масштаба категория риска сама по себе.
CouchDB — технически зрелая, но числа не те
CouchDB — технология, которая действительно поддерживает автошардирование и горизонтальное масштабирование из коробки. Но на сравнимом железе и в сравнимых конфигурациях её RPM на чтение и запись оказался гораздо ниже, чем у MongoDB, при этом latency — заметно выше. Технология рабочая, но просто не дотягивала по перформансу до того базлайна, который мы хотели улучшить, а не ухудшить.
Yandex TSaurusDB и архитектурная неожиданность
Кандидат не просто не дотягивает по цифрам, у TSaurusDB есть конкретная архитектурная мина. Её особенность в том, что добавление нового мастера в кластер приводит к простою всего кластера целиком. Причём проблема не только в плановом добавлении ноды. Когда старый мастер отъезжает вместе с дата-центром и нужно поднять замену, то здесь та же история: для этого нужно выключать весь кластер.
Для технологии, которую планируется использовать как более надёжную замену существующему решению, такая отказоустойчивость не подходит концептуально. Она хуже, чем то, что мы пытаемся заменить. Добавим сюда многокомпонентность: конфигурация кластера и rack awareness идут через Cypress — специально разработанную под эту БД файловую систему. Через её конфиг как раз задаются все настройки. На момент тестов этого нигде не было задокументировано. Выяснилось это только методом проб и code-reading. Документация на тестируемую версию была откровенно скудной.
ScyllaDB победила по железу, но проиграла по лицензии
ScyllaDB — отдельная история и по-своему показательная. Она тестировалась первой и прошла все нагрузочные тесты лучше остальных кандидатов. Технически она сильный кандидат: совместимость с протоколом Cassandra, написана на C++ с шардированием по ядрам CPU (shard-per-core архитектура), низкие latency.
К сожалению, ScyllaDB отвалилась из-за лицензии.
ScyllaDB в какой-то момент взяла и зарезала наличие комьюнити-версии. Она сказала: «Всё, вот эта версия последняя комьюнити, и можете сидеть до скончания веков на ней. Но все новые версии будут только энтерпрайз и только с ограничениями в бесплатном использовании. А если не хотите ограничений — платите».
Именно этот кейс заставил нас впервые серьёзно отнестись к юридическому аспекту выбора.
Так, из шести кандидатов в финал вышли два: Cassandra 5 и FoundationDB. Их тестировали значительно глубже и детальнее остальных. Далее идёт их технический разбор.

Основные тесты финалистов
FoundationDB: красивая теория и болезненная практика
FoundationDB заслуживает отдельного уважения хотя бы потому, что это одна из немногих NoSQL-баз, которая честно предоставляет строгие ACID-гарантии с serializable изоляцией на распределённом кластере. Технологию в своё время выкупил Apple, и, судя по архитектурным решениям и масштабу внутреннего использования, значительная часть инфраструктуры компании держится именно на ней. Звучит как идеальная рекомендация. На практике всё немного сложнее.
Архитектура FoundationDB: разделение ролей как фича и как источник боли
В основе FoundationDB лежит разделение узлов кластера на классы. Каждый процесс в системе может выполнять определённую роль: storage отвечает за хранение данных, coordinator — за координацию консенсуса, transaction-related роли занимаются обработкой транзакций (resolver, proxy, sequencer в разных версиях получают разные названия, но суть не меняется — это конвейер обработки транзакций, разнесённый по специализированным процессам). Разработчики FoundationDB рекомендуют разносить эти роли по разным физическим нодам и конфигурировать каждую под конкретную задачу.
Специализация процессов под нагрузку звучит как разумная инженерная практика. Но мы протестировали все разумные комбинации: универсальную ноду, способную брать на себя любую роль в любой момент, и жёстко закреплённые роли за конкретными нодами. Ни одна из конфигураций не дала положительного эффекта на производительность на нашем железе. Это не означает, что такой подход в целом провальный. Возможно, на других профилях нагрузки и других топологиях разница появляется. Но для нашего сценария вся эта тонкая настройка ролей превратилась в дополнительную операционную сложность без видимой отдачи.
Критическая зависимость от дисковой подсистемы
Чтобы выжать из FoundationDB максимум, нужно буквально пробрасывать в систему физический SSD-диск как отдельное блочное устройство и натравливать на него ровно один процесс с инструкцией писать строго в этот диск. Двадцать дисков — двадцать процессов. При этом каждый процесс лимитирован использованием одного ядра CPU, поэтому идеальная конфигурация сервера для FDB — это многоядерная машина с большим количеством отдельных физических дисков, а не RAID-массивом.
Здесь у нас сломался привычный паттерн мышления. В индустрии RAID 0 — стандартный способ получить больше IOPS, агрегируя несколько дисков в один логический том. Для FoundationDB — наоборот. Даже RAID 0 из двух дисков работает заметно хуже, чем запись напрямую на один физический диск. А если несколько процессов начинают писать на один и тот же диск, то производительность резко проседает.
Классическая оптимизация хранилища — агрегация дисков, выравнивание нагрузки между процессами — для FDB не просто бесполезна, а вредна. Нужна нестандартная для большинства инженеров топология: один диск — один процесс. Без исключений.
Conflict rate — ахиллесова пята оптимистичных транзакций
FoundationDB реализует транзакционность через оптимистичную конкурентность (optimistic concurrency control) с MVCC. Транзакция выполняется локально, а на коммите проверяется конфликт с другими параллельными транзакциями через resolver-слой. Если конфликт обнаружен, то транзакция откатывается и клиент должен повторить попытку.
В синтетических тестах с равномерными по размеру сообщениями и предсказуемым паттерном доступа conflict rate был нулевым: красивые цифры, всё работает как в документации. Но как только мы начали туда гнать реальные продовые данные с разными по размеру и характеру сообщениями, такие же как в MongoDB, conflict rate перестал быть нулём, и производительность немедленно ушла вниз. Это критично важный момент для оценки любой технологии: синтетический benchmark на равномерной нагрузке систематически переоценивает производительность optimistic-concurrency систем, потому что реальный продовый трафик почти никогда не бывает настолько предсказуемым.
Лимит на размер значения и отсутствие сжатия
У FoundationDB есть два болезненных ограничения: жёсткий лимит на размер value в 100 КБ и отсутствие сжатия данных на диске.
Для key-value хранилища общего назначения лимит на размер в 100 КБ это очень скромный потолок. Для нашего сценария с messenger-нагрузкой, где сообщения сильно варьируются по размеру, это критичное ограничение архитектуры.
Отсутствие сжатия данных на диске бьёт по ресурсной эффективности. По итогам тестов мы получили не экономию, а проигрыш, ведь рост занимаемого места на диске превысил 2,7 раза по сравнению с MongoDB. Учитывая, что один из мотивов всего этого исследования, это избавиться от x4 по ресурсам в текущей модели MongoDB, получить вместо этого 2.7x по диску от кандидата — довольно плохой итог.
Клиентская сложность: CGO как отдельный круг ада
Последний и, возможно, самый болезненный для продуктовых команд пункт — это порог входа со стороны клиента. FoundationDB предоставляет C API, и большинство клиентских биндингов для других языков, включая нужный нам Go, реализованы через CGO-обёртки над этим C API. На практике это означает регулярные проблемы при сериализации и десериализации tuple-ключей — внутреннего формата FDB для составных ключей — на границе между Go-кодом и C-библиотекой.
По итогу, с точки зрения разработчика архитектура FoundationDB — это распределённая упорядоченная хэш-мапа (ordered key-value store) с serializable-транзакциями, поверх которой layers-слоем можно построить что угодно: от документной модели до SQL-подобного API. Но эта абстракция изначально находится дальше от привычной модели работы продуктовых команд, чем CQL у Cassandra. Уровень входа для разработчика существенно выше: нужно самостоятельно реализовывать retry-логику при конфликтах транзакций на стороне клиента, разбираться с tuple-кодированием ключей, проектировать схему хранения с нуля — никакой query language тебе для этого не даст подсказок.
Всё, с чем мы сталкивались в процессе тестирования, по большей части приходилось проходить методом проб и ошибок — и не всегда успешно: даже у продуктовых команд, которые параллельно пилотировали эту технологию, далеко не всё получалось настроить с первого раза, хотя в итоге решение и заработало. Такой стабильности и предсказуемости, как у клиента Cassandra, здесь и близко нет.
Чем удивила Cassandra 5
Стоит сразу оговориться, что мы тестировали именно пятую версию Cassandra, а не четвёртую. Это не формальность, ведь для новой версии движок существенно переписан не только в части compaction-стратегий, а значительно шире. В Cassandra 5 появилась новая структура хранения SSTable BTI (BigTableIndex) вместо привычного Big–формата, что меняет характеристики чтения индекса и итерации по диску.
Нетребовательность к железу при требовательности к ОС
Первое, что приятно удивило, Cassandra оказалась практически нетребовательна к типу дисковой подсистемы. Производительность была плюс-минус одинаковой на SATA SSD, на SAS SSD, на RAID 0, RAID 5 и RAID 6 при равном объёме CPU и памяти. Это разительно отличало её от FoundationDB, для которой тип и топология дисков — определяющий фактор производительности.
Однако, Cassandra оказалась требовательна к настройкам операционки. Сетевые буферы, лимиты на число процессов, параметры файловой системы — всё это нужно тюнить, и без OS-level тюнинга движок не покажет заявленный потенциал. Требовательность сместилась с физического слоя на программный, что для DBA команды куда более управляемая проблема, чем заказ конкретной модели NVMe.
Производительность: 600K RPS против 50K RPS
Теперь посмотрим на цифры, ради которых всё затевалось. На кластере из 18 нод Cassandra выдала 600K RPS при P99 latency на запись в 60мс.

У FoundationDB на той же топологии из 18 нод максимум, который удалось выжать, всего 50K RPS на чтение и запись. Это на порядок ниже.

При этом у FDB latency оказалась объективно лучше: порядка 20мс P99 (с пиками до 25). Это её стабильный показатель почти на любой нагрузке вплоть до потолка в 50K RPS. FoundationDB — это система, которая держит низкую и предсказуемую latency, но за счёт сильно ограниченного пропускного потолка. У Cassandra latency невысокая, сопоставимая с MongoDB, но пропускная способность системы заметно выше, чем у MongoDB и FoundationDB. Это прямо то, что нужно для high-throughput сценария.
Архитектурная причина такой разницы в том, что FoundationDB тратит существенную часть производительности на сериализуемые транзакции с честным разрешением конфликтов через централизованный resolver-слой. Cassandra работает в модели tunable consistency без serializable-транзакций по умолчанию, что позволяет линейно размазывать нагрузку по нодам без необходимости глобальной координации на каждой записи.
Утилизация ресурсов: диск против CPU
У Cassandra и FoundationDB ресурсный профиль практически зеркальный. У Cassandra утилизация диска сэкономила 30% места относительно эквивалентной инсталляции MongoDB. Однако, ресурс CPU она кушает с аппетитом, тратя силы на compaction в фоне, работу с LSM-структурой, сериализацию/десериализацию на каждый запрос.

В FoundationDB по факту отсутствует сжатие данных на диске.рост занимаемого на диске места в 2,7 раза относительно эквивалентной по нагрузке инсталляции MongoDB. А CPU практически не нагружен.

Это следствие разных архитектурных решений на уровне storage engine. Cassandra построена на классическом Log-Structured Merge-tree (LSM-tree): запись сначала уходит в commit log и in-memory Memtable, а затем периодически сбрасывается на диск в виде иммутабельных SSTable, которые затем фоново мержатся через compaction. Вся фоновая работа идёт нагрузкой на CPU, причём процессор использовался хорошо вне зависимости от того, было ли задействовано 20 ядер или 100.
FoundationDB построена вокруг redwood/SQLite-подобного storage-движка с упором на прямую работу с диском через storage-процессы, отсюда и зеркальная картина утилизации.
Линейность роста: масштабирование кратно replication factor
Один из выводов касается того, как именно нужно расширять кластер Cassandra, чтобы получить положительный эффект. Тестовый график автошардирования показал характерную ступенчатую картину. RPS заметно рос только в те моменты, когда в кластер добавлялось число нод, кратное replication factor. Если же добавлять ноды по одной, то линейного прироста производительности не будет.

Логика здесь упирается в механизм консистентного хеширования и распределения токенов в кольце Cassandra (token ring). Каждый ключ распределяется по N репликам согласно replication factor, и партиционирование данных идёт через виртуальные узлы (vnodes), привязанные к токен-диапазонам. Если добавить ноды не кратно RF, получившееся распределение временно нарушает равномерность нагрузки между владельцами токен-диапазонов: часть нод оказывается перегруженной относительно других, и эффект от расширения замаскирован этим перекосом. Добавление кратно RF восстанавливает симметрию топологии относительно существующих наборов реплик, и тогда прирост производительности проявляется в полную силу.
По итогу получается, что расширение нужно планировать заранее пакетами, кратными RF, а не «докинем парочку нод по мере роста нагрузки», ведь иначе можно не увидеть ожидаемого эффекта от масштабирования и решить, что технология не оправдывает ожиданий.
Чувствительность к топологии клиентских подключений
Ещё одна неожиданность, которую тестирование выявило эмпирически.
Производительность и RPS у Cassandra растут при увеличении числа клиентов и при уменьшении числа подключений от каждого из них. То есть много клиентов с небольшим числом коннектов на каждого лучше, чем мало клиентов с агрессивным пулингом большого числа подключений.
Объяснение лежит в том, как драйвер Cassandra распределяет запросы по token-aware routing: координатор запроса выбирается на основе того, какая нода владеет нужным токен-диапазоном, и при равномерном распределении клиентов драйвер эффективнее балансирует нагрузку между координаторами без избыточной конкуренции за одни и те же connection slots. Здесь же отдельно стоит сказать про батчинг: Go-драйвер для Cassandra крайне чувствителен к настройкам батчинга запросов — в некоторых случаях правильная настройка batch size меняет перформанс буквально в два раза. Это тот случай, когда дефолтные настройки стороннего драйвера могут не отражать реальный потенциал технологии, и DBA-команде, тестирующей новую СУБД через сторонний драйвер, обязательно нужно прогонять профилирование настроек самого драйвера отдельно от настроек кластера.
Поведение при добавлении и удалении нод
Ввод каждой новой ноды в кластер Cassandra происходит строго последовательно, нельзя одновременным батчем закинуть пачку из пяти нод и получить мгновенное равномерное перераспределение. Когда одна нода добавлена, и данные на неё реплицировались, только тогда можно добавлять следующую.
При этом распределение клиентских подключений происходит равномерно. Как только новая нода насыщена данными, к ней без ручного вмешательства начинают подключаться клиенты. Это именно то автошардирование, ради которого всё затевалось. Движок сам разруливает топологию, сервису не нужно ничего знать о внутренней структуре кластера.
Отказоустойчивость и masterless-архитектура
В отказоустойчивости Cassandra показала, пожалуй, самое элегантное поведение из всех протестированных кандидатов. Её технология основана на masterless/multimaster топологии. Все ноды равноправны, нет выделенной роли. Это прямое следствие архитектуры на основе модифицированного протокола Dynamo и gossip-протокола для распространения состояния кластера между нодами.
Если сравнить это с архитектурой MongoDB, то у Cassandra узкой точки отказа структурно не существует: отъезжающие ноды быстро покидают кольцо, а возвращающиеся — быстро возвращаются. Никаких проблем с мониторингом отказа дата-центра при тестировании не зафиксировано — оставшиеся ноды продолжали честно сообщать о состоянии кластера через стандартный стек.
Сравнительные показатели финалистов
Производительность | Экономия диска по сравнению с MongoDB | Масштабируемость | Особенности | |
Cassandra 5 | 600K RPS | Экономия 30% | Горизонтальная. Добавление нод в кластер пропорционально фактору репликации. Оно увеличивает объём и пропускную способность кластера. | Зрелое комьюнити, простота адаптации клиентов и быстрая сходимость при отказе дата-центра. |
FoundationDB | 50K RPS | Прирост 300% | Горизонтальная. Добавление каждой ноды в кластер незначительно увеличивает объём и пропускную способность. При одинаковом добавлении числа нод эффект у Cassandra будет кратно выше. | Очень низкая latency 15-20 мс в персистентном режиме, но нужен прямой проброс дисков, а не RAID, также высокий порог входа для разработчиков и небольшой рынок специалистов. |
По итогам всех тестов выбор сделан в пользу Cassandra 5. Однако, она не абсолютный победитель по всем параметрам, а в контексте конкретной задачи. Мы искали технологию, которая закроет потребности многосотшардовых, тяжёлых MongoDB-инсталляций, где self-sharding руками сервиса превратился в операционную и инфраструктурную боль.
Cassandra не полная замена MongoDB, обычная малошардовая MongoDB продолжит обслуживать большинство продуктовых сервисов Авито без изменений.
Cassandra выбрана решением для тяжёлого хвоста инсталляций, где счёт идёт на сотни шардов и терабайты данных.
Что не попало в тесты:
LWT-транзакции. Если в продовом сценарии Cassandra появится потребность в LWT, эту характеристику нужно тестировать отдельно и заранее.
Датамоделинг. Поскольку датамоделинг в Cassandra строится не от сущностей, а от паттернов чтения (Chebotko diagrams), поэтому схема, выбранная один раз, практически не подлежит пересмотру. В этом смысле Cassandra жёстче, чем базы у MongoDB, где гибкость схемы прощает многие ошибки проектирования постфактум. Команды с многосотшардовыми инсталляциями имеют сильно отличающиеся паттерны работы с данными, поэтому этот процесс надо вести индивидуально.
Geo-distribution. Все дата-центры физически находились в одном регионе. Теста на сценарий с ДЦ на разных континентах и латентностью в десятки-сотни миллисекунд не проводилось.
ACID. Всё исследование строилось вокруг конкретного сценария — нагрузки высоконагруженного сервиса Messenger с очень большой MongoDB-инсталляцией, разнородными по размеру сообщениями и понятной key-value моделью с явными partition key и clustering key. Строгие ACID-гарантии и уровни изоляции не были критичны, и этому аспекту не уделялось приоритетного внимания.
K8s и потери на виртуализации. Чтобы зафиксировать максимальный потенциал технологии, все тесты проводились на голом железе. А тестирование Cassandra внутри Kubernetes на тех же серверах с теми же конфигурациями — отдельный этап, который ещё не был завершён. Это важная оговорка, потому что переход в K8s почти никогда не бесплатен.

Выводы по тестам
Если резюмировать ключевые архитектурные выводы по итогам всего цикла тестирования:
Технологии, которые умеют масштабироваться вверх и вниз без боли на стороне сервиса, существуют. Это не обязательно Cassandra.
Технологии, которые утилизируют ресурсы лучше MongoDB, тоже реально существуют: 30% экономии по диску у Cassandra на сопоставимой инсталляции это не теоретическая цифра, а измеренный результат.
Честное автошардирование данных на уровне движка работает. Основная гипотеза всего исследования подтвердилась измерениями.
Производительность Cassandra растёт нелинейно относительно наивных ожиданий. Линейный прирост требует расширения кластера кратно replication factor, и об этом нужно знать заранее, а не выяснять методом проб и ошибок в проде.
Чувствительность к топологии клиентских подключений — реальный фактор, который нужно учитывать при проектировании connection pooling на стороне сервисов, использующих Cassandra.
На полный цикл тестирования ушло заметное количество времени, железа и бессонных ночей, но именно это позволило получить результат, который можно обосновать цифрами, а не интуицией.
Куда движемся дальше
Следующий логичный шаг — платформизация Cassandra на DBaaS K8s по аналогии с тем, как сегодня в Авито устроена MongoDB. Это не тривиальная задача. Нужно повторить тесты производительности уже внутри Kubernetes, чтобы оценить реальную просадку от контейнеризации и виртуализации сетевого стека, продумать механизм rack awareness и распределения нод по зонам доступности на уровне платформы, а не вручную. После надо выстроить процесс безопасного onboarding для продуктовых команд, которые захотят мигрировать свои тяжёлые MongoDB-кластеры.
Команда совместно с DBA-юнитом опишет методику переноса данных, спроектирует новую схему хранения с учётом партицирования по паттернам чтения, и запланирует переход на горизонт от одного до двух кварталов в зависимости от сложности сервиса. Дешёвым и быстрым этот процесс не будет.
Миграция модели данных под другую парадигму хранения почти никогда не бывает дешёвой, и это нормально: цена входа окупается на горизонте, на котором self-sharding руками команды перестаёт быть рентабельным решением вообще.
Если смотреть шире, история выбора хорошо иллюстрирует общий тренд последних лет. Рынок распределённых СУБД движется не к одному универсальному решению, а к специализации движков под конкретный профиль нагрузки.
Кстати, если вам интересна работа в бигтехе — Хабр совместно с ЭКОПСИ проводит большое исследование IT-брендов работодателей. В прошлом году в нём поучаствовали 34 000 специалистов. Если у вас есть опыт — он точно будет учтён.

