如何完成 Apache Doris POC 验证?
本文档汇总了新用户常见的问题,旨在加速 POC 进程。内容按照 POC 的典型流程组织:
- 建表设计 — 选择数据模型、排序键、分区与分桶策略。
- 数据导入 — 选择合适的导入方式,避免常见陷阱。
- 查询调优 — 排查慢查询,优化分桶与索引配置。
- 数据湖查询 — 针对 Lakehouse 场景的额外优化要点。
5 分钟完成基础检查:
- 建表是否使用合适的数据模型(Duplicate/Unique/Aggregate Key)?
- 分桶数是否为 BE 数量的整数倍?
- 时间查询是否包含分区列?
- 是否开启 Data Cache(湖上查询场景)?
建表设计
在 Doris 中建表涉及四个影响导入和查询性能的决策:数据模型、排序键、分区和分桶。
数据模型
根据数据写入方式选择合适的模型:
| 数据特征 | 推荐模型 | 原因 |
|---|---|---|
| 仅追加(日志、事件、事实表) | Duplicate Key(默认) | 保留所有行,查询性能最好 |
| 按主键更新(CDC、Upsert) | Unique Key | 新行按相同 Key 替换旧行 |
| 预聚合指标(PV、UV、汇总) | Aggregate Key | 写入时按 SUM/MAX/MIN 合并行 |
Duplicate Key 适用于大多数场景。详见数据模型概述。
Sort Key(排序键)
Doris 在排序键的前 36 字节上构建前缀索引,设置排序键时注意以下原则:
- 高频过滤列优先:将最常用于 WHERE 条件的列放在最前面。
- 定长类型优先:INT、BIGINT、DATE 等定长类型放在 VARCHAR 之前,因为遇到 VARCHAR 时前缀索引会立即截断。
- 补充倒排索引:前缀索引覆盖不到的列,可添加倒排索引加速过滤。
分区
如果有时间列,使用 AUTO PARTITION BY RANGE(date_trunc(time_col, 'day')) 启用分区裁剪。Doris 会自动跳过无关分区。
分桶
默认是 Random 分桶(推荐用于 Duplicate Key 表)。如果频繁按某列过滤或 JOIN,使用 DISTRIBUTED BY HASH(该列)。详见数据分桶。
如何选择分桶数:
| 原则 | 说明 |
|---|---|
| 设为 BE 数量的整数倍 | 确保数据均匀分布。后续扩容 BE 时,查询通常涉及多个分区,性能不会受影响 |
| 尽可能少 | 避免产生小文件 |
| 每个分桶压缩后数据 ≤ 20 GB | Unique Key 表 ≤ 10 GB。可通过 SHOW TABLETS FROM your_table 查看 |
| 每个分区不超过 128 个分桶 | 需要更多时优先考虑增加分区。极端情况下上限为 1024,但生产环境中很少需要 |
建表模板
日志 / 事件分析
适用场景: 日志、事件、传感器数据等仅追加场景
前置要求: 无特殊要求
CREATE TABLE app_logs
(
log_time DATETIME NOT NULL,
log_level VARCHAR(10),
service_name VARCHAR(50),
trace_id VARCHAR(64),
message STRING,
INDEX idx_message (message) USING INVERTED PROPERTIES("parser" = "unicode")
)
AUTO PARTITION BY RANGE(date_trunc(`log_time`, 'day'))
()
DISTRIBUTED BY RANDOM BUCKETS 10;
验证步骤:
-- 1. 验证分区是否自动创建
SHOW PARTITIONS FROM app_logs;
-- 2. 验证数据分布是否均匀
SHOW TABLETS FROM app_logs;
实时看板与 Upsert(CDC)
适用场景: 用户信息、订单流水等需要按主键更新的场景
前置要求: 需要明确主键列
CREATE TABLE user_profiles
(
user_id BIGINT NOT NULL,
username VARCHAR(50),
email VARCHAR(100),
status TINYINT,
updated_at DATETIME
)
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
验证步骤:
-- 1. 验证主键唯一性(同一 user_id 只有一条最新数据)
SELECT user_id, count(*) as cnt FROM user_profiles GROUP BY user_id HAVING cnt > 1;
-- 2. 验证数据分布
SHOW TABLETS FROM user_profiles;
指标聚合
适用场景: 流量统计、业务报表等需要预聚合的场景
前置要求: 明确聚合维度列和指标列
CREATE TABLE site_metrics
(
dt DATE NOT NULL,
site_id INT NOT NULL,
pv BIGINT SUM DEFAULT '0',
uv BIGINT MAX DEFAULT '0'
)
AGGREGATE KEY(dt, site_id)
AUTO PARTITION BY RANGE(date_trunc(`dt`, 'day'))
()
DISTRIBUTED BY HASH(site_id) BUCKETS 10;
验证步骤:
-- 1. 验证聚合是否生效(相同 dt+site_id 的指标是否合并)
SELECT dt, site_id, pv, uv FROM site_metrics ORDER BY dt DESC LIMIT 10;
-- 2. 验证分区裁剪是否生效
EXPLAIN SELECT * FROM site_metrics WHERE dt = '2024-01-01';
数据导入
选择合适的导入方式并遵循以下最佳实践,可以有效避免常见的性能问题:
- 批量数据不要用
INSERT INTO VALUES。请使用 Stream Load 或 Broker Load。详见导入概述。 - 优先在客户端合并写入。高频小批次导入导致版本堆积。如不可行,使用 Group Commit。
- 将大型导入拆分为小批次。长时间运行的导入失败后必须从头重试。使用 INSERT INTO SELECT 配合 S3 TVF 实现增量导入。
- Random 分桶的 Duplicate Key 表启用
load_to_single_tablet,减少写放大。
快速验证:
-- 查看导入任务状态
SHOW LOAD WHERE label = 'your_label';
-- 检查版本堆积(Version Count 过高说明导入过于频繁)
SHOW TABLETS FROM your_table;
详见导入最佳实践。
查询调优
分桶相关
分桶数直接影响查询并行度和调度开销,需要在两者之间取得平衡:
- 不要分桶过多。过多的小 tablet 会产生调度开销,查询性能最多可下降 50%。
- 不要分桶过少。过少的 tablet 会限制 CPU 并行度。
- 避免数据倾斜。通过
SHOW TABLETS检查 tablet 大小。差异明显时切换为 Random 分桶或选择基数更高的分桶列。
诊断命令:
-- 检查 tablet 大小分布(用于判断数据倾斜)
SHOW TABLETS FROM your_table\G
-- 查看 Tablet 数量和大小,判断是否需要调整分桶数
参见分桶了解分桶数选择。
索引相关
- 正确设置排序键。与 PostgreSQL 等系统不同,Doris 仅对排序键的前 36 字节建立索引,且遇到 VARCHAR 会立即截断。超出前缀范围的列无法从排序键受益,需添加倒排索引。参见 Sort Key(排序键)。
验证排序键是否生效:
EXPLAIN SELECT * FROM your_table WHERE filter_column = 'xxx';
-- 查看是否走 Sort Key 索引
诊断工具
诊断慢查询请使用 Query Profile。
快速上手:
-- 1. 执行查询并获取 query_id
SET enable_profile = true;
SELECT ...;
-- 2. 查看 Query Profile
SHOW PROFILELIST;
SHOW PROFILE WHERE query_id = 'xxx';
数据湖查询
如果 POC 涉及通过 Doris 查询 Hive、Iceberg、Paimon 等湖上数据(即 Lakehouse 场景),以下几点对测试结果影响最大。
确保分区裁剪生效
湖上表往往有海量数据,查询时务必在 WHERE 条件中包含分区列,使 Doris 只扫描必要的分区。可通过 EXPLAIN <SQL> 查看 partition 字段确认裁剪是否生效:
0:VPAIMON_SCAN_NODE(88)
partition=203/0 -- 203 个分区被裁剪,实际扫描 0 个
如果分区数远大于预期,检查查询的 WHERE 条件是否正确匹配分区列。
开启 Data Cache
远端存储(HDFS/对象存储)的 IO 延迟比本地磁盘高出数倍。Data Cache 将最近访问的远端数据缓存到 BE 本地磁盘,重复查询同一批数据时可获得接近内表的查询性能。
- 缓存默认关闭,请参阅 数据缓存 文档进行配置。
- 自 4.0.2 版本起支持缓存预热,可在 POC 测试前主动加载热数据。
POC 中建议先执行一次查询完成缓存加载,再以第二次查询的延迟作为基准。这样可以更准确地评估生产环境的常态性能。
治理小文件
湖上数据常存在大量小文件。小文件会被拆分为大量 Split,导致 FE 内存压力增大甚至 OOM,查询规划开销上升。
- 从源头治理(推荐):在 Hive/Spark 侧定期合并小文件,使每个文件保持在 128 MB 以上。
- Doris 侧兜底:通过
SET max_file_split_num = 50000;(4.0.4 起支持)限制每次扫描的最大 Split 数量,防止 OOM。
使用 Query Profile 诊断
湖上查询的瓶颈通常在 IO 而非计算。Query Profile 可以定位慢查询根因,重点关注:
- Split 数量和数据量:判断是否扫描了过多数据。
- MergeIO 指标:若
MergedBytes远大于RequestBytes,说明读放大严重,可通过调小merge_io_read_slice_size_bytes(默认 8 MB)来缓解。 - Cache 命中率:确认 Data Cache 是否在有效工作。
更多优化手段请参阅数据湖查询调优。
常见报错与解决方案
建表报错 "Tablet count should be greater than 0"
原因: 分桶数设置为 0 或未设置分桶。
解决: 检查 DDL 是否包含 DISTRIBUTED BY HASH(xxx) BUCKETS n,确保 BUCKETS 后跟正整数。
-- 正确示例
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
查询慢,怀疑未走索引
诊断步骤:
- 执行
EXPLAIN SQL查看查询计划,确认是否使用 Sort Key 索引 - 执行
SHOW TABLETS FROM table_name检查 tablet 大小是否均匀 - 查看 Query Profile 定位瓶颈
-- 检查是否走索引(看 output_id 是否有 Sort Key 列)
EXPLAIN SELECT * FROM table_name WHERE key_col = 'xxx';
-- 检查 tablet 大小(判断数据倾斜)
SHOW TABLETS FROM table_name;
湖上查询 OOM
原因: 小文件过多导致 Split 数量爆炸。
解决:
- 在数据源侧合并小文件(每个文件 > 128 MB)
- Doris 侧限制 Split 数量:
SET max_file_split_num = 50000;
导入版本堆积,查询变慢
原因: 频繁小批次导入导致版本过多。
解决:
- 合并写入批次,减少导入频率
- 启用 Group Commit:
SET group_commit_mode = 'async_mode';
常见问题
Q: POC 验证需要多久?
一般 1-2 天完成基础功能验证(建表、导入、简单查询),详细性能调优需要 3-5 天。
Q: 建表时如何选择分桶数?
优先设为 BE 节点数的整数倍,确保数据均匀分布。每个分桶压缩后数据建议 ≤ 20 GB(Unique Key 表 ≤ 10 GB)。
Q: 查询比预期慢怎么办?
- 执行
EXPLAIN检查是否走索引 - 执行
SHOW TABLETS检查数据是否倾斜 - 查看 Query Profile 定位瓶颈
Q: Data Cache 是否需要开启?
如果涉及湖上查询(Hive/Iceberg/Paimon),建议开启。首次查询会自动缓存数据,重复查询可获得接近内表的性能。