StarRocks: Архитектура, Практика и место в современном Data Stack
StarRocks — это аналитическая MPP-база данных нового поколения.
Если коротко, она пытается решить трилемму аналитики: объединить скорость ClickHouse (за счет векторизации и C++), гибкость Trino (поддержка сложных JOIN-ов) и простоту использования MySQL (совместимый протокол).
Это короткое руководство проведет вас от понимания архитектуры до построения простого конвейера загрузки данных (ETL) в домашнем продакшене.
Часть 1. Архитектура: FE и BE
В отличие от PostgreSQL (монолит) или ClickHouse (где узлы часто одноранговые), StarRocks имеет четкое разделение ролей. Это критически важно для понимания масштабирования и эксплуатации.
1. FE (Frontend) — “Мозг”
Написан на Java.
- Роль: Управляющий слой.
- Функции:
- Принимает подключения клиентов (по протоколу MySQL).
- Хранит метаданные (схемы таблиц, права доступа).
- Парсит SQL и строит план выполнения запроса (Query Plan).
- Управляет транзакциями загрузки данных.
- Масштабирование: Обычно запускают 1 или 3 узла для обеспечения высокой доступности (HA).
- Важно: Клиенты (DBeaver, BI, сurl) подключаются только к FE.
2. BE (Backend) — “Мускулы”
Написан на C++ (использует SIMD-инструкции процессора).
- Роль: Слой хранения и вычислений.
- Функции:
- Физически хранит данные (в колоночном формате).
- Выполняет “тяжелую” работу: фильтрацию, агрегацию, JOIN-ы.
- Управляет репликацией данных.
- Масштабирование: Можно добавлять узлы линейно. Чем больше BE, тем быстрее выполняются запросы и тем больше данных можно хранить.
В Docker All-in-One: Оба компонента упакованы в один контейнер для удобства, но слушают разные порты:
- `9030`: FE (SQL интерфейс, сюда идет DBeaver).
- `8030`: FE (HTTP API для загрузки Stream Load, сюда идет curl).
- `8040`: BE (HTTP API метрик и логов).
Часть 2. Быстрый старт (Docker Compose)
Мы поднимем стек StarRocks и MinIO (S3-совместимое хранилище), используя bridge-сеть для связности.
Файл `docker-compose.yml` (Полностью рабочий пример):
version: "3.9"
networks:
starrocks-stack-network:
driver: bridge
services:
starrocks:
image: starrocks/allin1-ubuntu:4.0-latest
container_name: starrocks
hostname: starrocks.local.com
platform: "linux/amd64"
restart: unless-stopped
ports:
- "9030:9030" # MySQL Protocol (SQL клиенты)
- "8030:8030" # FE HTTP (Stream Load)
- "8040:8040" # BE HTTP (Logs/Metrics)
environment:
- TZ=UTC
networks:
starrocks-stack-network:
volumes:
# Персистентность данных (чтобы данные не исчезли после рестарта)
- ${HOME}/dv/starrocks/be/storage:/data/deploy/starrocks/be/storage
- ${HOME}/dv/starrocks/be/log:/data/deploy/starrocks/be/log
- ${HOME}/dv/starrocks/fe/meta:/data/deploy/starrocks/fe/meta
- ${HOME}/dv/starrocks/fe/log:/data/deploy/starrocks/fe/log
minio:
image: quay.io/minio/minio
container_name: minio
platform: "linux/amd64"
hostname: minio.local.com
restart: unless-stopped
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web UI
networks:
starrocks-stack-network:
environment:
MINIO_ROOT_USER: root
MINIO_ROOT_PASSWORD: rootroot
volumes:
- ${HOME}/dv/minio/data:/data
command: server /data --console-address ":9001"Запуск:
`docker-compose up -d`
Часть 3. Моделирование данных (Table Design)
В StarRocks нельзя просто “создать таблицу”. Нужно выбрать тип ключа (Key Model), который определит, как база будет хранить и обновлять данные.
Подключение (DBeaver): `localhost:9030`, User: `root`, Password: (пусто).
CREATE DATABASE IF NOT EXISTS demo_db;
USE demo_db;1. Primary Key Model (Для изменяемых данных)
Это “флагманская” возможность StarRocks. Она поддерживает быстрые Upsert (вставка новых или обновление старых записей по ID) в реальном времени.
CREATE TABLE IF NOT EXISTS users (
user_id INT NOT NULL,
username VARCHAR(50),
email VARCHAR(100),
register_date DATE,
city VARCHAR(50)
)
PRIMARY KEY (user_id) -- Уникальный ключ
DISTRIBUTED BY HASH(user_id) -- Распределение данных
PROPERTIES (
"replication_num" = "1" -- Для локального теста ставим 1 реплику
);2. Aggregate Key Model (Для витрин данных)
База автоматически агрегирует данные при вставке. Если вы вставите новую продажу с *существующими* датой и категорией, StarRocks не создаст новую строку, а прибавит суммы к уже существующей строке. Это экономит место и ускоряет `GROUP BY`.
CREATE TABLE IF NOT EXISTS daily_sales (
report_date DATE NOT NULL,
category VARCHAR(50) NOT NULL,
-- Метрики с функцией агрегации:
total_amount BIGINT SUM DEFAULT "0",
items_sold INT SUM DEFAULT "0"
)
AGGREGATE KEY (report_date, category)
DISTRIBUTED BY HASH(report_date) BUCKETS 3
PROPERTIES (
"replication_num" = "1"
);Часть 4. загрузка данных users (Stream Load)
Для загрузки данных в продакшене мы используем Service Account (Техническую учетную запись). Это стандарт безопасности: мы не используем `root` и не используем токены в конфигах (так как они требуют перезагрузки кластера для смены).
Шаг 1. Создание сервисного пользователя (SQL)
Выполнять под `root`:
-- 1. Создаем пользователя-бота
CREATE USER IF NOT EXISTS 'etl_loader'@'%' IDENTIFIED BY 'SecretPass123!';
-- 2. Даем права ТОЛЬКО на вставку и чтение в базе demo_db
GRANT INSERT, SELECT ON demo_db.* TO 'etl_loader'@'%';
-- Права применяются мгновенно.Шаг 2. Загрузка сложного JSON через CURL
Stream Load — это самый быстрый способ загрузки (до 100 МБ/сек на узел). Он поддерживает транзакционность (ACID).
Пример файла `users.json`:
{
"users": [
{"user_id": 101, "username": "alex", "email": "a@test.com", "city": "NY"},
{"user_id": 102, "username": "bob", "email": "b@test.com", "city": "LA"}
]
}Команда загрузки (Terminal):
curl --location-trusted \
-u etl_loader:SecretPass123! \
-H "Expect: 100-continue" \
-H "format: json" \
-H "strip_outer_array: true" \
-H "json_root: $.users" \
-H "jsonpaths: [\"$.user_id\", \"$.username\", \"$.email\", \"$.city\"]" \
-H "columns: user_id, username, email, city" \
-T "users.json" \
-XPUT http://localhost:8030/api/demo_db/users/_stream_loadОтвет
{
"TxnId": 9596,
"Label": "a9a37ab6-3678-4c08-95b7-2fd8b6ae973e",
"Db": "demo_db",
"Table": "users",
"Status": "Success",
"Message": "OK",
"NumberTotalRows": 2,
"NumberLoadedRows": 2,
"NumberFilteredRows": 0,
"NumberUnselectedRows": 0,
"LoadBytes": 177,
"LoadTimeMs": 153,
"BeginTxnTimeMs": 2,
"StreamLoadPlanTimeMs": 2,
"ReadDataTimeMs": 0,
"WriteDataTimeMs": 26,
"CommitAndPublishTimeMs": 121
}%Шаг 3. Загрузка в Aggregate Table (Example)
Давайте “дольем” данные в таблицу продаж. Агрегация произойдет на лету.
Файл sales.json (простой список):
[
{"dt": "2023-11-01", "cat": "Electronics", "amt": 100, "qty": 1},
{"dt": "2023-11-01", "cat": "Electronics", "amt": 50, "qty": 1}
]
curl --location-trusted \
-u etl_loader:SecretPass123! \
-H "format: json" \
-H "Expect: 100-continue" \
-H "strip_outer_array: true" \
-H "jsonpaths: [\"$.dt\", \"$.cat\", \"$.amt\", \"$.qty\"]" \
-H "columns: report_date, category, total_amount, items_sold" \
-T "sales.json" \
-XPUT http://localhost:8030/api/demo_db/daily_sales/_stream_loadОтвет:
{
"TxnId": 9613,
"Label": "bce0721a-dc2d-4927-be93-e0979a57873d",
"Db": "demo_db",
"Table": "daily_sales",
"Status": "Success",
"Message": "OK",
"NumberTotalRows": 2,
"NumberLoadedRows": 2,
"NumberFilteredRows": 0,
"NumberUnselectedRows": 0,
"LoadBytes": 143,
"LoadTimeMs": 52,
"BeginTxnTimeMs": 3,
"StreamLoadPlanTimeMs": 2,
"ReadDataTimeMs": 0,
"WriteDataTimeMs": 24,
"CommitAndPublishTimeMs": 20
}%Разбор заголовков:
- `-u ...`: Авторизация сервисным пользователем.
- `Expect: 100-continue`: Критически важно для надежности передачи больших файлов.
- `json_root: $.users`: Указывает базе, что данные лежат внутри ключа `users`.
- `strip_outer_array: true`: Говорит базе, что внутри лежит массив `[...]` и его нужно “развернуть” в отдельные строки.
Часть 5. Совместимость и Trino Dialect
Одна из сильных сторон StarRocks — способность “притворяться” другими базами данных для облегчения миграции.
Если у вас есть дашборды, написанные на диалекте Trino (Presto), вам не нужно переписывать все SQL-запросы.
Пример трансляции функций:
-- Функция Trino, которой нет в StarRocks
SELECT doy(date '2022-03-06');
-- Ошибка: No matching function...
-- Проверяем, как StarRocks переведет этот запрос
TRANSLATE TRINO select doy(date '2022-03-06');
-- Результат: SELECT dayofyear('2022-03-06')
-- Включаем режим автоматической трансляции в сессии
SET sql_dialect = 'trino';
-- Теперь запрос выполняется корректно, но это не правда. а вот так SELECT dayofyear('2022-03-06') работает. Может бага или у меня версия не та.
SELECT doy(date '2022-03-06');
-- Возвращаем нативный режим
SET sql_dialect = 'starrocks';*(Примечание: Поддержка диалекта постоянно расширяется, но некоторые специфические функции могут требовать ручной замены).*
Итог: Сравнение и Выбор решения ( грубо )
| Характеристика | StarRocks | ClickHouse | Trino (Presto) |
| Основной сценарий | OLAP-витрины с JOIN-ами и обновлениями данных | Сбор логов, событий, метрик (Append-only) | Федерация данных (запрос к S3 + Postgres + Kafka одновременно) |
| JOIN производительность | ⭐⭐⭐ (Excellent, CBO оптимизатор) | ⭐ (Слабо, требует денормализации) | ⭐⭐⭐ (Excellent) |
| Обновление (UPDATE) | ⭐⭐⭐ (Работает как в OLTP, Primary Key) | ⭐ (Тяжелые асинхронные ALTER) | ❌ (Обычно только полная перезапись партиций), iceberg не в счёт :) |
| Язык Engine | C++ (SIMD Vectorized) | C++ (SIMD Vectorized) | Java (JVM) |
| Место в стеке | Serving Layer (Быстрый доступ для BI) | Storage Layer (Хранение логов) | Query Engine (Ad-hoc запросы к Data Lake) |
Выбирайте StarRocks, если:
- Вам нужна “витрина” для BI (Superset/Tableau), где данные должны быть всегда свежими (Real-time updates).
- Ваш бизнес требует сложных аналитических запросов с множеством JOIN-ов, и ClickHouse не справляется/падает по памяти.
- Вы хотите использовать стандартный протокол MySQL без установки проприетарных драйверов.