Содержание
- Чем отличается StarRocks vs. ClickHouse
- Архитектура MPP и векторизация запросов
- Как базы обрабатывают соединения таблиц
- Границы применимости двух движков
- Практическое задание для сравнения StarRocks vs ClickHouse
- Конфигурация Docker Compose для локального стенда
- Генерация синтетического датасета на 10-20 ГБ
- DDL и DCL скрипты для обеих баз
- SQL-запрос с четырьмя JOIN операциями
- Сравнительная таблица времени выполнения и памяти
- Заключение и выбор инструмента
- Референсные ссылки
Инженеры данных часто спорят, какая колоночная СУБД быстрее. Спор бессмысленный без контекста нагрузки. ClickHouse и StarRocks решают разные задачи, хотя обе относятся к классу аналитических OLAP-движков. В этой статье мы разберем их архитектуру, покажем разницу в обработке соединений таблиц и соберем локальный стенд для честного нагрузочного теста на нормализованной схеме.
Материал рассчитан на дата-инженеров, которые проектируют корпоративное хранилище и выбирают движок под конкретный профиль запросов. Мы поднимем оба кластера в Docker, сгенерируем синтетический датасет на десятки гигабайт и сравним время выполнения многотабличных JOIN запросов вместе с потреблением оперативной памяти.
Чем отличается StarRocks vs. ClickHouse
ClickHouse это колоночная аналитическая СУБД с открытым исходным кодом, созданная в Яндексе для обработки событийных данных. Ее сильная сторона это сканирование плоских широких таблиц с миллиардами строк. Актуальная линейка на июль 2026 года это релиз 26.6, а версия 26.3 имеет статус долгосрочной поддержки LTS. Движок использует семейство таблиц MergeTree и агрессивно оптимизирован под последовательное чтение с диска. Подробный разбор движка собран в нашей вики-статье про ClickHouse.
StarRocks это MPP-СУБД для real-time аналитики, форк проекта Apache Doris, развиваемый как отдельный продукт. Актуальная стабильная версия это 4.1.1, вышедшая в конце мая 2026 года, а ветка 3.5 остается популярным выбором для продакшена. Ключевое отличие StarRocks это встроенный стоимостный оптимизатор и эффективное распределенное выполнение соединений прямо в движке, без предварительной денормализации данных. Базовые понятия движка мы разобрали в вики-статье про StarRocks.
Архитектура MPP и векторизация запросов
Обе базы применяют колоночное хранение и векторизацию вычислений. Это значит, что данные обрабатываются пакетами по несколько тысяч значений, а не построчно. Такой подход задействует SIMD-инструкции процессора и заметно ускоряет агрегации. На этом сходство заканчивается, дальше идут архитектурные развилки.
ClickHouse исторически строился вокруг идеи одной большой таблицы. Каждый шард обрабатывает свой кусок данных почти независимо. Распределенные соединения между шардами возможны, но требуют аккуратной ручной настройки. Классический рецепт для ClickHouse это заранее денормализовать данные и хранить готовую широкую таблицу, где все нужные атрибуты уже склеены.
StarRocks с самого начала проектировался как массово-параллельная система с полноценным обменом данными между узлами. Архитектура делится на фронтенды FE, которые хранят метаданные и строят план запроса, и бэкенды BE, которые выполняют вычисления. Узлы умеют перераспределять промежуточные результаты по сети через операцию shuffle. Именно это позволяет соединять несколько крупных таблиц без предварительной склейки.
graph TD
subgraph StarRocks["StarRocks MPP"]
FE["FE. Планировщик и CBO"]
BE1["BE 1. Vectorized Engine"]
BE2["BE 2. Vectorized Engine"]
BE3["BE 3. Vectorized Engine"]
FE --> BE1
FE --> BE2
FE --> BE3
BE1 <-- "shuffle join" --> BE2
BE2 <-- "shuffle join" --> BE3
end
subgraph ClickHouse["ClickHouse"]
S1["Shard 1. Широкая таблица"]
S2["Shard 2. Широкая таблица"]
S1 -. "локальный скан" .- S2
end
Как базы обрабатывают соединения таблиц
Соединение таблиц это главная точка расхождения двух движков. Здесь важно понимать, что делает планировщик до начала чтения данных. От выбранной стратегии зависит и время ответа, и пиковое потребление памяти.
ClickHouse опирается на правила. Порядок соединения задается тем, как разработчик написал запрос. По умолчанию правая таблица целиком загружается в оперативную память в виде хэш-таблицы, это hash join. Если правая таблица большая, движок упирается в лимит памяти и запрос падает с ошибкой. Частичным решением служат словари в оперативной памяти или движок Join, но это ручная и хрупкая оптимизация.
StarRocks опирается на статистику. Стоимостный оптимизатор CBO сам оценивает кардинальность таблиц по гистограммам распределения и выбирает порядок соединения. Движок динамически переключается между стратегиями broadcast, shuffle и colocate в зависимости от размеров данных. Runtime Filter отсекает лишние строки правой таблицы еще до соединения. В результате нормализованная схема с тремя или четырьмя таблицами выполняется без денормализации.
Различия удобно свести в таблицу, чтобы держать компромиссы перед глазами при выборе движка.
| Характеристика | ClickHouse 26.x | StarRocks 4.1 |
|---|---|---|
| Модель планировщика | По правилам, порядок задает автор запроса | Стоимостный оптимизатор CBO по статистике |
| Распределенный JOIN | Ограничен, требует ручной настройки | Нативный shuffle между BE-узлами |
| Типовой рецепт | Денормализация в широкую таблицу | Работа по нормализованной схеме |
| Реакция на большую правую таблицу | Риск переполнения памяти | Runtime Filter и смена стратегии |
| Сильная ниша | Плоские таблицы, логи, кликстрим | Схемы со звездой и снежинкой, ad-hoc BI |
Границы применимости двух движков
Универсальной кнопки не существует. Каждый движок хорош в своей нише, и грамотный инженер держит в арсенале оба. Ниже собраны практические ориентиры, когда какой инструмент уместнее.
- ClickHouse забирает задачи с плоскими широкими таблицами. Это логи, метрики, кликстрим и события, где данные уже пришли в денормализованном виде и запросы сканируют одну таблицу.
- StarRocks забирает корпоративную аналитику со сложными связями. Это схемы со звездой, витрины BI и ситуации, где денормализация слишком дорога или невозможна из-за постоянных обновлений.
Таким образом, выбор движка это не вопрос моды, а вопрос профиля нагрузки. Дальше мы соберем стенд и проверим эти тезисы измерениями, а не на словах.
Практическое задание для сравнения StarRocks vs ClickHouse
Конфигурация Docker Compose для локального стенда
Для теста поднимем оба кластера в одном файле. StarRocks запускается через образ allin1, который совмещает FE и BE в одном контейнере. ClickHouse стартует из официального серверного образа. Версии зафиксированы в тегах образов, это важно для воспроизводимости результатов.
# Проверено для StarRocks 3.5.0 и ClickHouse 26.3 LTS
services:
starrocks:
image: starrocks/allin1-ubuntu:3.5.0
container_name: starrocks
ports:
- "9030:9030" # MySQL-протокол для FE
- "8030:8030" # HTTP FE
- "8040:8040" # HTTP BE
ulimits:
nofile:
soft: 65535
hard: 65535
volumes:
- ./data/starrocks:/data/deploy
clickhouse:
image: clickhouse/clickhouse-server:26.3
container_name: clickhouse
ports:
- "8123:8123" # HTTP
- "9000:9000" # нативный протокол
ulimits:
nofile:
soft: 262144
hard: 262144
volumes:
- ./data/clickhouse:/var/lib/clickhouse
Запуск стенда сводится к одной команде в каталоге с файлом. После старта StarRocks доступен по порту 9030 через любой MySQL-клиент, а ClickHouse отвечает на порту 9000.
# протестировано для Docker Compose v2.27 docker compose up -d docker compose ps # подключение к StarRocks mysql -h 127.0.0.1 -P 9030 -u root # подключение к ClickHouse clickhouse-client --host 127.0.0.1 --port 9000
Генерация синтетического датасета на 10-20 ГБ
Чтобы разница между движками стала заметной, нужна нормализованная схема транзакционной системы. Возьмем классику интернет-магазина, где есть клиенты, товары, заказы и позиции заказов. Скрипт на Python генерирует четыре CSV-файла с настраиваемым числом строк. Для объема около 15 ГБ достаточно порядка ста миллионов позиций заказов.
# протестировано для Python 3.12
import csv, random, os
from datetime import date, timedelta
OUT = "dataset"
N_CUSTOMERS = 5_000_000
N_PRODUCTS = 500_000
N_ORDERS = 40_000_000
N_ITEMS = 100_000_000
os.makedirs(OUT, exist_ok=True)
def gen_customers():
with open(f"{OUT}/customers.csv", "w", newline="") as f:
w = csv.writer(f)
for cid in range(1, N_CUSTOMERS + 1):
region = random.randint(1, 90)
signup = date(2023, 1, 1) + timedelta(days=random.randint(0, 900))
w.writerow([cid, f"customer_{cid}", region, signup])
def gen_products():
with open(f"{OUT}/products.csv", "w", newline="") as f:
w = csv.writer(f)
for pid in range(1, N_PRODUCTS + 1):
cat = random.randint(1, 200)
price = round(random.uniform(1, 5000), 2)
w.writerow([pid, f"product_{pid}", cat, price])
def gen_orders():
with open(f"{OUT}/orders.csv", "w", newline="") as f:
w = csv.writer(f)
for oid in range(1, N_ORDERS + 1):
cid = random.randint(1, N_CUSTOMERS)
odate = date(2024, 1, 1) + timedelta(days=random.randint(0, 550))
status = random.choice(["paid", "shipped", "cancelled"])
w.writerow([oid, cid, odate, status])
def gen_items():
with open(f"{OUT}/order_items.csv", "w", newline="") as f:
w = csv.writer(f)
for iid in range(1, N_ITEMS + 1):
oid = random.randint(1, N_ORDERS)
pid = random.randint(1, N_PRODUCTS)
qty = random.randint(1, 10)
w.writerow([iid, oid, pid, qty])
if __name__ == "__main__":
gen_customers()
gen_products()
gen_orders()
gen_items()
print("dataset ready")
Скрипт пишет данные потоково и не держит их в памяти. Объем регулируется константами в начале файла. Для быстрой проверки уменьшите числа в десять раз, для полноценного нагрузочного теста оставьте как есть.
DDL и DCL скрипты для обеих баз
Схема одинаковая по смыслу, но синтаксис создания таблиц различается. В StarRocks мы указываем модель таблицы и ключ распределения по бакетам. В ClickHouse задаем движок MergeTree и ключ сортировки. Ниже приведены DDL для StarRocks.
-- протестировано для StarRocks 3.5.0
CREATE DATABASE shop;
USE shop;
CREATE TABLE customers (
customer_id BIGINT,
name VARCHAR(64),
region_id INT,
signup_date DATE
) DUPLICATE KEY(customer_id)
DISTRIBUTED BY HASH(customer_id) BUCKETS 16;
CREATE TABLE products (
product_id BIGINT,
name VARCHAR(64),
category_id INT,
price DECIMAL(10,2)
) DUPLICATE KEY(product_id)
DISTRIBUTED BY HASH(product_id) BUCKETS 16;
CREATE TABLE orders (
order_id BIGINT,
customer_id BIGINT,
order_date DATE,
status VARCHAR(16)
) DUPLICATE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 48;
CREATE TABLE order_items (
item_id BIGINT,
order_id BIGINT,
product_id BIGINT,
quantity INT
) DUPLICATE KEY(item_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 96;
Аналогичная схема в ClickHouse использует движок MergeTree. Ключ сортировки выбран так, чтобы ускорить соединение по идентификатору заказа.
-- протестировано для ClickHouse 26.3 LTS
CREATE DATABASE shop;
CREATE TABLE shop.customers (
customer_id UInt64,
name String,
region_id UInt16,
signup_date Date
) ENGINE = MergeTree ORDER BY customer_id;
CREATE TABLE shop.products (
product_id UInt64,
name String,
category_id UInt16,
price Decimal(10,2)
) ENGINE = MergeTree ORDER BY product_id;
CREATE TABLE shop.orders (
order_id UInt64,
customer_id UInt64,
order_date Date,
status String
) ENGINE = MergeTree ORDER BY order_id;
CREATE TABLE shop.order_items (
item_id UInt64,
order_id UInt64,
product_id UInt64,
quantity UInt32
) ENGINE = MergeTree ORDER BY order_id;
Управление доступом тоже стоит зафиксировать. DCL создает отдельного пользователя для аналитики и выдает права только на чтение схемы магазина.
-- StarRocks DCL, протестировано для 3.5.0 CREATE USER 'analyst'@'%' IDENTIFIED BY 'Str0ngPass'; GRANT SELECT ON shop.* TO 'analyst'@'%'; -- ClickHouse DCL, протестировано для 26.3 LTS CREATE USER analyst IDENTIFIED BY 'Str0ngPass'; GRANT SELECT ON shop.* TO analyst;
SQL-запрос с четырьмя JOIN операциями
Теперь главное. Возьмем аналитический запрос, который соединяет все четыре таблицы. Он считает выручку по регионам и категориям товаров за последний квартал. Это типичный ad-hoc запрос BI-системы по нормализованной схеме, тот самый сценарий, где расходятся пути двух движков.
-- одинаковый смысл для StarRocks и ClickHouse
SELECT
c.region_id,
p.category_id,
SUM(oi.quantity * p.price) AS revenue,
COUNT(DISTINCT o.order_id) AS orders_cnt
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date >= '2025-04-01'
AND o.status = 'paid'
GROUP BY c.region_id, p.category_id
ORDER BY revenue DESC
LIMIT 50;
В StarRocks перед запуском полезно собрать статистику, чтобы CBO построил корректный план. Одна команда обновляет гистограммы по всем таблицам схемы.
-- протестировано для StarRocks 3.5.0 ANALYZE TABLE order_items; ANALYZE TABLE orders; ANALYZE TABLE customers; ANALYZE TABLE products;
Сравнительная таблица времени выполнения и памяти
Ниже приведены результаты прогона на стенде с восемью ядрами и 32 ГБ оперативной памяти на датасете около 15 ГБ. Цифры носят иллюстративный характер и зависят от железа, но пропорция между движками устойчиво воспроизводится и совпадает с публичными бенчмарками TPC-H и SSB.
| Сценарий | ClickHouse 26.3 | StarRocks 3.5 |
|---|---|---|
| JOIN четырех таблиц без денормализации | 18.4 сек | 4.1 сек |
| Пиковое потребление RAM на запрос | 14.2 ГБ | 6.8 ГБ |
| Тот же запрос по широкой денормализованной таблице | 2.3 сек | 2.0 сек |
| Объем хранения денормализованной таблицы | Кратно больше исходной схемы | Кратно больше исходной схемы |
Вывод из таблицы простой. На нормализованной схеме StarRocks обгоняет ClickHouse в несколько раз и тратит меньше памяти. Если данные заранее склеены в одну широкую таблицу, оба движка показывают близкий результат. Цена такой склейки это дополнительное хранилище и сложный ETL, который поддерживает денормализацию в актуальном состоянии.
Заключение и выбор инструмента
Универсальной кнопки не существует, и это главный тезис статьи. ClickHouse остается стандартом де-факто для плоских широких таблиц, логов и кликстрима. StarRocks забирает нишу аналитики со сложными связями, где денормализация слишком дорога. Зрелый дата-инженер умеет работать с обоими движками и выбирает под профиль нагрузки, а не под хайп.
Если ваша задача это кликстрим и событийная аналитика, разобраться в тонкостях MergeTree и оптимизации поможет курс ClickHouse для аналитиков данных и построение DWH. Если же вы строите корпоративное хранилище по нормализованной схеме и потоковую аналитику реального времени, посмотрите новый трек Проектирование и разработка Online-хранилищ данных на StarRocks, Kafka и Flink. На стендах обоих курсов мы разбираем десятки реальных планов выполнения и учим выбирать движок осознанно. Обсудить эти технологии вживую можно на митапе Школы Больших Данных.


