StarRocks/Doris 深度实践:极速全场景 OLAP 引擎
一、为什么选择 StarRocks/Doris?
1.1 OLAP 引擎选型对比
| 引擎 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| ClickHouse | 单表查询极快 | Join 能力弱、运维复杂 | 日志分析、单表聚合 |
| Presto/Trino | 联邦查询强 | 无索引、延迟高 | 即席查询、跨源查询 |
| Elasticsearch | 全文搜索强 | 聚合性能弱 | 搜索、日志检索 |
| StarRocks/Doris | Join 能力强、运维简单 | 生态相对新 | 多维分析、实时数仓 |
1.2 StarRocks vs Doris
关系: StarRocks fork 自 Doris,两者同源但已分化
| 特性 | Doris | StarRocks |
|---|---|---|
| Join 优化 | Hash Join | CBO + 向量化 Join |
| 物化视图 | 单表聚合 | 多表 Join 物化 |
| 联邦查询 | 支持 | 支持(更强) |
| 写入性能 | 较高 | 更高(向量化) |
| 社区活跃度 | 高 | 更高 |
| 企业采用 | 百度、小米 | 美团、字节、网易 |
选型建议:
- 新项目优先 StarRocks(性能更优、社区更活跃)
- 已有 Doris 集群可继续用(兼容性好)
- 本文以 StarRocks 为主,Doris 差异会标注
1.3 核心优势
StarRocks = MySQL 协议 + MPP 架构 + 向量化引擎
核心优势:
1. 极速查询 - 向量化执行,比 Doris 快 3-10 倍
2. 灵活更新 - 支持 Upsert、部分更新
3. 实时写入 - 微批次提交,秒级可见
4. 联邦查询 - 一张 SQL 查遍 Hive/Iceberg/MySQL/ES
5. 运维简单 - 无外部依赖,FE+BE 即可运行
量化对比(某电商实测):
| 查询类型 | Doris | StarRocks | ClickHouse |
|---|---|---|---|
| 单表聚合 | 1.2s | 0.4s | 0.3s |
| 多表 Join | 5.8s | 1.2s | 8.5s |
| 点查 | 50ms | 30ms | 20ms |
| 并发查询 | 100 QPS | 300 QPS | 150 QPS |
二、架构原理
2.1 整体架构
┌─────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ MySQL │ │ JDBC │ │ Spark │ │ Flink │ │
│ │ Client │ │ Driver │ │ Connector│ │ Connector│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓ (MySQL 协议)
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (FE) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 解析器 │ │ 优化器 │ │ 调度器 │ │
│ │ (Parser) │ │ (CBO) │ │ (Planner)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 元数据管理 (MetaService) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓ (RPC)
┌─────────────────────────────────────────────────────────────────┐
│ Backend (BE) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 查询执行引擎 (Execution Engine) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 向量化 │ │ 管道执行 │ │ 并行计算 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 存储引擎 │ │ 索引管理 │ │ 缓存管理 │ │
│ │ (Column) │ │ (Bitmap 等) │ │ (Page) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 数据模型
三种数据模型:
1. Aggregate 模型(聚合模型)
- 主键相同的行自动聚合
- 适用:预聚合场景(UV、PV、Sum)
示例:
CREATE TABLE ads_uv_daily (
date DATE,
user_id BIGINT,
pv SUM,
uv BITMAP_UNION
)
AGGREGATE KEY(date, user_id);
2. Unique 模型(主键模型)
- 主键唯一,支持 Upsert
- 适用:需要更新删除的场景
示例:
CREATE TABLE dwd_order_detail (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(18,2),
status INT
)
UNIQUE KEY(order_id);
3. Duplicate 模型(明细模型)
- 保留所有明细数据
- 适用:日志、流水等无需去重场景
示例:
CREATE TABLE ods_click_log (
event_time DATETIME,
user_id BIGINT,
page_url STRING
)
DUPLICATE KEY(event_time, user_id);
2.3 分区与分桶
分区(Partition):
- 按范围分区(RANGE)
- 通常按时间(天/月)
- 便于数据生命周期管理
示例:
PARTITION BY RANGE(event_time) (
PARTITION p20260401 VALUES LESS THAN ('2026-04-02'),
PARTITION p20260402 VALUES LESS THAN ('2026-04-03'),
PARTITION p20260403 VALUES LESS THAN ('2026-04-04')
)
分桶(Bucket):
- 按 Hash 分桶
- 决定并行度
- 影响查询性能
分桶数计算:
- 单表数据量 / 1GB = 分桶数
- 或:BE 节点数 * CPU 核数 * (1-3)
示例:
DISTRIBUTED BY HASH(order_id) BUCKETS 32
三、实战操作
3.1 部署配置
FE 配置(fe.conf):
bash
# FE 节点配置
priority_networks = 192.168.1.0/24
edit_log_port = 9010
http_port = 8030
query_port = 9030
rpc_port = 9020
BE 配置(be.conf):
bash
# BE 节点配置
priority_networks = 192.168.1.0/24
be_port = 9060
brpc_port = 8040
webserver_port = 8040
heartbeat_service_port = 9050
# 存储路径
storage_root_path = /data/starrocks/storage
# 内存配置
mem_limit = 80%
# 查询配置
disable_storage_page_cache = false
部署命令:
bash
# 启动 FE
./fe/bin/start_fe.sh --daemon
# 添加 FE 节点(MySQL 客户端)
mysql -h 127.0.0.1 -P 9030 -u root
ALTER SYSTEM ADD FOLLOWER "fe-2:9010";
ALTER SYSTEM ADD FOLLOWER "fe-3:9010";
# 启动 BE
./be/bin/start_be.sh --daemon
# 添加 BE 节点
ALTER SYSTEM ADD BACKEND "be-1:9050";
ALTER SYSTEM ADD BACKEND "be-2:9050";
ALTER SYSTEM ADD BACKEND "be-3:9050";
3.2 表设计
实时数仓表设计:
sql
-- ODS 层(明细数据)
CREATE TABLE IF NOT EXISTS ods_order_info (
order_id BIGINT COMMENT '订单 ID',
user_id BIGINT COMMENT '用户 ID',
amount DECIMAL(18,2) COMMENT '订单金额',
status TINYINT COMMENT '订单状态',
create_time DATETIME COMMENT '创建时间',
update_time DATETIME COMMENT '更新时间'
)
DUPLICATE KEY(order_id, create_time)
PARTITION BY RANGE(create_time) (
PARTITION p20260401 VALUES LESS THAN ('2026-04-02'),
PARTITION p20260402 VALUES LESS THAN ('2026-04-03'),
PARTITION p20260403 VALUES LESS THAN ('2026-04-04')
)
DISTRIBUTED BY HASH(order_id) BUCKETS 32
PROPERTIES (
"replication_num" = "3",
"storage_format" = "DEFAULT",
"light_schema_change" = "true"
);
-- DWD 层(明细事实表)
CREATE TABLE IF NOT EXISTS dwd_order_detail (
order_id BIGINT COMMENT '订单 ID',
user_id BIGINT COMMENT '用户 ID',
amount DECIMAL(18,2) COMMENT '订单金额',
pay_amount DECIMAL(18,2) COMMENT '实付金额',
category_id BIGINT COMMENT '品类 ID',
brand_id BIGINT COMMENT '品牌 ID',
create_time DATETIME COMMENT '创建时间'
)
UNIQUE KEY(order_id)
PARTITION BY RANGE(create_time) (
PARTITION p20260401 VALUES LESS THAN ('2026-04-02'),
PARTITION p20260402 VALUES LESS THAN ('2026-04-03')
)
DISTRIBUTED BY HASH(order_id) BUCKETS 32
PROPERTIES (
"replication_num" = "3",
"enable_mow_light_delete" = "true"
);
-- DWS 层(轻度聚合)
CREATE TABLE IF NOT EXISTS dws_order_daily (
dt DATE COMMENT '日期',
category_id BIGINT COMMENT '品类 ID',
order_count BIGINT SUM COMMENT '订单数',
gmv DECIMAL(20,2) SUM COMMENT '交易额',
buyer_count BIGINT BITMAP_UNION COMMENT '购买人数'
)
AGGREGATE KEY(dt, category_id)
DISTRIBUTED BY HASH(category_id) BUCKETS 16
PROPERTIES (
"replication_num" = "3"
);
-- ADS 层(应用数据)
CREATE TABLE IF NOT EXISTS ads_category_ranking (
dt DATE COMMENT '日期',
category_name STRING COMMENT '品类名称',
gmv DECIMAL(20,2) COMMENT '交易额',
gmv_rank BIGINT COMMENT '排名',
mom_growth DECIMAL(10,4) COMMENT '环比增长'
)
DUPLICATE KEY(dt)
DISTRIBUTED BY HASH(dt) BUCKETS 10
PROPERTIES (
"replication_num" = "3"
);
3.3 数据导入
Stream Load(实时导入):
bash
# 导入 JSON 数据
curl --location-trusted -u user:password \
-T order_data.json \
-H "label:label_20260409_001" \
-H "column_separator: ," \
-H "columns: order_id,user_id,amount,status,create_time" \
http://be-1:8040/api/ods_order_info/_stream_load
# 导入 CSV 数据
curl --location-trusted -u user:password \
-T order_data.csv \
-H "label:label_20260409_002" \
-H "column_separator: ," \
-H "format: csv" \
http://be-1:8040/api/ods_order_info/_stream_load
Routine Load(Kafka 实时导入):
sql
-- 创建 Kafka 导入任务
CREATE ROUTINE LOAD ods_order_info_load ON ods_order_info
COLUMNS TERMINATED BY ","
COLUMNS (order_id, user_id, amount, status, create_time)
WHERE status != -1
PROPERTIES (
"desired_concurrent_number" = "3",
"max_batch_interval" = "10",
"max_batch_rows" = "100000"
)
FROM KAFKA (
"kafka_broker1" = "kafka-1:9092",
"kafka_broker2" = "kafka-2:9092",
"kafka_broker3" = "kafka-3:9092",
"kafka_topic" = "ods.order.events",
"kafka_partitions" = "0,1,2,3",
"property.client.id" = "starrocks_ods_order",
"property.group.id" = "starrocks_ods_order_group",
"property.auto.offset.reset" = "earliest"
);
-- 查看导入状态
SHOW LOAD FROM ods_order_info_load;
-- 暂停/恢复导入
STOP ROUTINE LOAD FOR ods_order_info_load;
RESUME ROUTINE LOAD FOR ods_order_info_load;
Insert Into(ETL 写入):
sql
-- ODS → DWD
INSERT INTO dwd_order_detail
SELECT
order_id,
user_id,
amount,
amount - discount AS pay_amount,
category_id,
brand_id,
create_time
FROM ods_order_info
WHERE dt = '2026-04-08';
-- DWD → DWS(聚合)
INSERT INTO dws_order_daily
SELECT
DATE(create_time) AS dt,
category_id,
COUNT(*) AS order_count,
SUM(amount) AS gmv,
BITMAP_UNION(BITMAP_BUILD(user_id)) AS buyer_count
FROM dwd_order_detail
WHERE DATE(create_time) = '2026-04-08'
GROUP BY DATE(create_time), category_id;
Broker Load(HDFS/S3 导入):
sql
-- 从 HDFS 导入
LOAD LABEL ods_order_info_load_20260409
(
DATA INFILE("hdfs://namenode:8020/data/ods/order_info/*.parquet")
INTO TABLE ods_order_info
FORMAT AS PARQUET
)
WITH BROKER hdfs_broker
(
"username" = "hdfs_user",
"password" = "hdfs_password"
)
PROPERTIES
(
"timeout" = "3600",
"max_filter_ratio" = "0.01"
);
3.4 物化视图
同步物化视图:
sql
-- 创建物化视图(自动刷新)
CREATE MATERIALIZED VIEW mv_category_daily AS
SELECT
DATE(create_time) AS dt,
category_id,
COUNT(*) AS order_count,
SUM(amount) AS gmv,
COUNT(DISTINCT user_id) AS buyer_count
FROM dwd_order_detail
GROUP BY DATE(create_time), category_id;
-- 查询自动路由到物化视图
-- 无需修改 SQL,优化器自动选择
SELECT
DATE(create_time) AS dt,
category_id,
COUNT(*) AS order_count,
SUM(amount) AS gmv
FROM dwd_order_detail
WHERE DATE(create_time) >= '2026-04-01'
GROUP BY DATE(create_time), category_id;
异步物化视图:
sql
-- 创建异步物化视图
CREATE MATERIALIZED VIEW mv_order_summary
REFRESH ASYNC START('2026-04-09 00:00:00') EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"replication_num" = "3"
)
AS
SELECT
DATE_TRUNC('hour', create_time) AS hour,
status,
COUNT(*) AS order_count,
SUM(amount) AS gmv
FROM dwd_order_detail
GROUP BY DATE_TRUNC('hour', create_time), status;
-- 手动刷新
REFRESH MATERIALIZED VIEW mv_order_summary;
-- 查看刷新状态
SHOW ALTER TABLE MATERIALIZED VIEW mv_order_summary;
3.5 联邦查询
创建 External Catalog:
sql
-- Hive Catalog
CREATE EXTERNAL CATALOG hive_catalog
PROPERTIES (
"type" = "hive",
"hive.metastore.uris" = "thrift://hive-metastore:9083"
);
-- Iceberg Catalog
CREATE EXTERNAL CATALOG iceberg_catalog
PROPERTIES (
"type" = "iceberg",
"iceberg.catalog.type" = "hive",
"hive.metastore.uris" = "thrift://hive-metastore:9083"
);
-- MySQL Catalog
CREATE EXTERNAL CATALOG mysql_catalog
PROPERTIES (
"type" = "jdbc",
"jdbc.user" = "root",
"jdbc.password" = "password",
"jdbc.url" = "jdbc:mysql://mysql:3306/test",
"jdbc.driver.url" = "jdbc:mysql://mysql:3306",
"jdbc.driver.class" = "com.mysql.cj.jdbc.Driver"
);
跨源查询:
sql
-- 查询 Hive 表
SELECT * FROM hive_catalog.dw.order_detail LIMIT 100;
-- 查询 Iceberg 表
SELECT * FROM iceberg_catalog.prod.order_fact LIMIT 100;
-- 跨源 Join(StarRocks + Hive + MySQL)
SELECT
s.order_id,
s.amount,
u.user_name,
p.product_name
FROM dwd_order_detail s
JOIN hive_catalog.dim.user_dim u ON s.user_id = u.user_id
JOIN mysql_catalog.product p ON s.product_id = p.id
WHERE s.dt = '2026-04-08';
四、性能优化
4.1 索引优化
前缀索引:
sql
-- 自动创建(Duplicate/Unique 模型的前 N 列)
-- 无需手动创建,默认前 36 字节
ZoneMap 索引:
sql
-- 自动创建,每列都有
-- 记录每列的 Min/Max/Null Count
-- 用于谓词下推
Bitmap 索引:
sql
-- 手动创建(高基数字段)
CREATE INDEX idx_user_id ON dwd_order_detail USING BITMAP(user_id);
-- 适用于:
-- - 等值查询
-- - IN 查询
-- - 去重计数
倒排索引:
sql
-- 手动创建(文本字段)
CREATE INDEX idx_product_name ON dwd_order_detail USING INVERTED(product_name);
-- 适用于:
-- - 模糊查询
-- - 全文检索
4.2 SQL 优化
执行计划分析:
sql
-- 查看执行计划
EXPLAIN SELECT * FROM dwd_order_detail WHERE order_id = 123456;
-- 查看详细执行计划(含成本)
EXPLAIN VERBOSE SELECT * FROM dwd_order_detail WHERE order_id = 123456;
-- 查看 Profile(查询后)
SHOW PROFILE;
常见优化技巧:
sql
-- 1. 避免 SELECT *
SELECT order_id, user_id, amount FROM dwd_order_detail; -- 推荐
SELECT * FROM dwd_order_detail; -- 不推荐
-- 2. 利用分区裁剪
SELECT * FROM dwd_order_detail
WHERE create_time >= '2026-04-08'; -- 会裁剪分区
-- 3. 利用分桶裁剪
SELECT * FROM dwd_order_detail
WHERE order_id = 123456; -- 只查一个分桶
-- 4. 避免函数操作导致索引失效
WHERE DATE(create_time) = '2026-04-08'; -- 索引失效
WHERE create_time >= '2026-04-08' AND create_time < '2026-04-09'; -- 索引生效
-- 5. 大表 Join 小表,小表广播
SELECT /*+ BROADCAST(u) */ *
FROM dwd_order_detail o
JOIN dim_user u ON o.user_id = u.user_id;
4.3 参数调优
查询参数:
sql
-- 设置并发度
SET SESSION parallel_fragment_exec_instance_num = 4;
-- 设置内存限制
SET SESSION exec_mem_limit = 4G;
-- 设置超时时间
SET SESSION query_timeout = 300;
-- 启用 CBO
SET SESSION enable_cbo = true;
-- 启用向量化
SET SESSION enable_vectorized_engine = true;
BE 参数调优:
bash
# be.conf
# 内存限制
mem_limit = 80%
# 查询并发度
be_threads_per_query_per_fragment = 4
# 存储页缓存
disable_storage_page_cache = false
storage_page_cache_limit = 20GB
# 写入并发
write_thread_pool_size = 48
五、生产环境落地案例
5.1 案例背景
公司: 某电商平台
规模: 日订单 300 万 +,日增数据 800GB
团队: 数据团队 45 人
建设前痛点:
- ClickHouse Join 能力弱,多维分析困难
- Presto 查询延迟高(平均 5-10 秒)
- 运维复杂(多套系统)
- 无法支持实时更新
5.2 建设方案
阶段一:实时数仓(2 个月)
- StarRocks 集群部署(3 FE + 6 BE)
- Kafka → StarRocks 实时导入
- 核心表迁移(20 张表)
阶段二:多维分析(2 个月)
- 物化视图建设
- 联邦查询整合 Hive/Iceberg
- 报表迁移
阶段三:优化治理(持续)
- 性能调优
- 资源隔离
- 监控告警
5.3 建设效果
| 指标 | 建设前 | 建设后 | 提升 |
|---|---|---|---|
| 查询延迟 | 5-10s | 0.5-2s | 80% ↓ |
| 并发能力 | 50 QPS | 300 QPS | 6 倍 ↑ |
| 运维成本 | 3 套系统 | 1 套系统 | 67% ↓ |
| 数据可见性 | 分钟级 | 秒级 | 90% ↓ |
六、总结
核心要点
- StarRocks 是全场景 OLAP - 单表、Join、聚合都强
- 数据模型选择关键 - Aggregate/Unique/Duplicate 按需选择
- 分区分桶影响性能 - 合理设计决定查询效率
- 物化视图是利器 - 自动加速查询
- 联邦查询整合数据 - 一张 SQL 查遍所有数据源
最佳实践
yaml
表设计:
- 按时间分区(天/小时)
- 分桶数 = BE 节点数 * CPU * (1-3)
- 高基数字段放前面
数据导入:
- 实时:Routine Load(Kafka)
- 批量:Stream Load / Broker Load
- ETL: INSERT INTO
查询优化:
- 避免 SELECT *
- 利用分区/分桶裁剪
- 物化视图加速
运维管理:
- 监控 BE 内存使用
- 定期 Compaction
- 日志定期清理
附录
A. 版本兼容性
| StarRocks 版本 | MySQL 协议 | Flink Connector | Spark Connector |
|---|---|---|---|
| 2.x | 5.7 | 1.13-1.15 | 3.1-3.2 |
| 3.x | 8.0 | 1.15-1.17 | 3.2-3.4 |
B. 推荐阅读
- StarRocks 官方文档:https://docs.starrocks.io/
- Doris 官方文档:https://doris.apache.org/
- GitHub:https://github.com/StarRocks/starrocks
下一篇: 《流批一体架构落地》
上一篇: 《Iceberg 数据湖实战》
系列目录: 新技术实战系列