Нагрузочное тестирование LWT: Sirin vs Apache Cassandra

Отчёт сравнивает производительность механизма Lightweight Transactions (LWT) в Sirin 1.2.0 и Apache Cassandra 5.0.6.

Окружение

ПараметрЗначение
Sirin1.2.0 на Picodata 26.1.2
Apache Cassandra5.0.6
Нагрузчикlatte
Конфигурация стенда16 ядер / 32ГБ RAM / 1 ТБ SSD

Кластер Sirin

Узлы запущены как сервис systemd. Размер кластера зависит от replication factor: при RF=1 — 8 узлов без репликации, при RF=3 — 24 узла с тройной репликацией данных.

Кластер Cassandra

Три узла Apache Cassandra 5.0.6, запущены через Docker.

Схема

Создание keyspace

Фактор репликации задаётся параметром replication_factor. Тесты проводились при replication_factor=1 и replication_factor=3.

CREATE KEYSPACE IF NOT EXISTS latte
    WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor': <replication_factor> };

Таблица для одиночных LWT-запросов (write_lwt.rn)

CREATE TABLE latte.lwt (
    partition_key  bigint,
    clustering_key bigint,
    data           blob,
    PRIMARY KEY ((partition_key), clustering_key)
);

Таблица для батчевых LWT-запросов (write_batch_lwt.rn)

CREATE TABLE latte.lwt_batch (
    partition_key  bigint,
    clustering_key bigint,
    data           blob,
    PRIMARY KEY ((partition_key), clustering_key)
);

Тесты

Все тесты проводятся с partitions=1: все запросы направляются в один партишен, что максимизирует нагрузку на механизм LWT.

Одиночные LWT-запросы (write_lwt.rn)

Каждая итерация выполняет один INSERT … IF NOT EXISTS. Clustering key уникален для каждой итерации.

INSERT INTO latte.lwt (partition_key, clustering_key, data)
    VALUES (?, ?, ?) IF NOT EXISTS;

Параметры запуска:

latte run write_lwt.rn -P partitions=1 --threads 5 -p 50 --connections 5 --duration 15m

Батчевые LWT-запросы (write_batch_lwt.rn)

Каждая итерация отправляет один BATCH из batch_size записей в один партишен. LWT-операции внутри батча не могут пересекать границу партишена — это ограничение Cassandra и Sirin.

BEGIN BATCH
  INSERT INTO latte.lwt_batch (partition_key, clustering_key, data)
      VALUES (?, ?, ?) IF NOT EXISTS;
  -- … ещё (batch_size - 1) строк в тот же partition_key
APPLY BATCH;

Параметры запуска:

latte run write_batch_lwt.rn -P batch_size=50   --threads 5 -p 10 --connections 1 --duration 15m
latte run write_batch_lwt.rn -P batch_size=1000 --threads 5 -p 10 --connections 1 --duration 15m

Результаты

Replication factor = 1

Параметр запуска: -P replication_factor=1 (передаётся в latte schema). Кластер Sirin: 8 узлов, репликация отключена — каждая партиция хранится на одном узле.

Одиночные LWT-запросы

СистемаRPSP50P95P99CPU, %
Sirin642393.6566.8168.569190
Cassandra263349.578301.203500.433155

Батчевые LWT-запросы, batch_size=50

СистемаRPSP50P95P99CPU, %
Sirin159846.563173.539246.284150
Cassandra79532.309520.618858.784180

Батчевые LWT-запросы, batch_size=1000

СистемаRPSP50P95P99CPU, %
Sirin74273.154771.7521085.276180
Cassandra115519.7045679.0885737.808800

Replication factor = 3

Параметр запуска: -P replication_factor=3 (передаётся в latte schema). Кластер Sirin: 24 узла, каждая партиция хранится на трёх независимых узлах — данные реплицируются с тройной избыточностью.

Одиночные LWT-запросы

СистемаRPSP50P95P99CPU, %
Sirin259954.77234.47251.446600
Cassandra465302.5141923.0883445.621570

Батчевые LWT-запросы, batch_size=50

СистемаRPSP50P95P99CPU, %
Sirin152024.51086.770104.006800
Cassandra318162.5291203.7652065.695500

Батчевые LWT-запросы, batch_size=1000

СистемаRPSP50P95P99CPU, %
Sirin70481.2961836.0572116.026810
Cassandra134773.1185637.1455712.6421000

Выводы

Почему LWT в Cassandra значительно медленнее

LWT в Cassandra реализован поверх протокола Paxos. Для выполнения одного INSERT … IF NOT EXISTS координатор проводит до четырёх фаз:

  1. Prepare — координатор рассылает prepare-запросы репликам;
  2. Promise — реплики отвечают, возвращая последнее принятое значение;
  3. Accept — координатор предлагает значение, реплики соглашаются;
  4. Commit — значение фиксируется.

При RF=3 кворум требует ответа минимум от двух из трёх реплик на каждой фазе. Одна LWT-операция — от двух до четырёх сетевых round-trip’ов.

Sirin реализует LWT через механизмы транзакций Picodata внутри конкретной партиции без использования Paxos, что устраняет эти накладные расходы.

Replication factor = 1

Одиночные запросы. Разрыв в пропускной способности составляет 24× (64 239 vs 2 633 rps) при сопоставимой загрузке CPU — 190% против 155%. CPU у Cassandra уходит на Paxos, а не на запись. Медиана латентности — 49 мс против 3.6 мс у Sirin (14×), P99 — 500 мс против 8.6 мс (58×).

Батч из 50 записей. Cassandra выполняет один Paxos-раунд на весь BATCH, независимо от числа строк в нём, поэтому стоимость Paxos на строку в 50 раз ниже, чем при одиночных запросах — отсюда и меньший разрыв в RPS: (1 598 vs 795). Тем не менее хвостовая латентность P99 у Cassandra в 3.5 раза выше (858 мс vs 246 мс).

Батч из 1000 записей. Cassandra уходит в CPU-насыщение: 800% при 16 доступных ядрах. Несмотря на это, пропускная способность — лишь 11 rps против 74 у Sirin (6.7×), P99 достигает 5 737 мс против 1 085 мс (5.3×).

Replication factor = 3

При включённой репликации стоимость Paxos в Cassandra возрастает: каждая из четырёх фаз теперь требует кворума от реальных реплик, что добавляет сетевые round-trip’ы.

Одиночные запросы. Разрыв вырастает до 56× по RPS (25 995 vs 465). P50 у Cassandra — 302 мс против 4.8 мс у Sirin (63×); P99 — 3 445 мс против 51 мс (67×). Sirin задействует больше CPU (600%) на обслуживание трёх реплик, однако пропускная способность снижается лишь в 2.5× относительно RF=1. Cassandra при сопоставимой загрузке CPU (570%) даёт в 5.7× меньше RPS, чем при RF=1.

Батч из 50 записей. Разрыв по RPS — 4.8× (1 520 vs 318), по P99 — 20× (2 065 мс vs 104 мс). Sirin при RF=3 сохраняет почти ту же пропускную способность, что при RF=1 (1 520 vs 1 598 rps) — просадка в пределах погрешности. Cassandra деградирует в 2.5× относительно RF=1.

Батч из 1000 записей. Cassandra упирается в потолок: 1 000% CPU — более 60% вычислительной мощности машины, — и при этом выдаёт лишь 13 rps. Sirin при 810% CPU даёт 70 rps — разрыв 5.4×. P99 у Cassandra — 5 712 мс против 2 116 мс у Sirin.

Сводка

СценарийRFSirin, rpsCassandra, rpsSirin / Cassandra
Одиночные запросы164 2392 63324×
Батч, 50 строк11 598795
Батч, 1000 строк174116.7×
Одиночные запросы325 99546556×
Батч, 50 строк31 5203184.8×
Батч, 1000 строк370135.4×

Заключение

Для LWT-нагрузки Sirin — очевидный выбор. Cassandra при каждой условной записи прогоняет Paxos между узлами-репликами; Sirin этого не делает, и сетевые round-trip’ы просто выпадают из критического пути. При переходе с RF=1 на RF=3 разрыв по RPS на одиночных запросах вырастает с 24× до 56×: Cassandra платит за кворум на каждой из четырёх Paxos-фаз, Sirin — нет.