关于 doris-best-practices
doris-best-practices 是指导 Apache Doris 表设计、查询排查和集群 sizing 的技能。它按工作负载整理规则与模板,并提供 DDL 限制及基于证据的运行时诊断指引。
主要功能
- 01按工作负载分类的表设计模板
- 02CREATE TABLE 前检查清单
- 03DDL 限制与常见问题
- 04基于证据的查询诊断指引
- 05FE、BE 与存储 sizing 指南
适用场景
- 01为日志、CDC 或仪表盘设计表
- 02选择键、分区、分桶和索引
- 03使用 profile 和 EXPLAIN 排查慢查询
- 04估算集群与存储需求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ apache/ doris-skills --skill doris-best-practices安装后在 Claude Code 中输入/doris-best-practices确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/cli-investigation.mdschema-bucket-cloud-mandatory-hash.mdschema-bucket-composite-for-skew.mdschema-bucket-hash-vs-random.mdschema-bucket-high-cardinality-key.mdschema-bucket-target-size.mdschema-cache-file-cache.mdschema-cache-query-partition.mdschema-ddl-gotchas.mdschema-index-bitmap.mdschema-index-bloomfilter.mdschema-index-inverted.mdschema-index-ngram-for-like.mdschema-index-text-search.mdschema-index-vector.mdschema-keys-avoid-float.mdschema-keys-cluster-key-for-mow.mdschema-keys-fixed-length-types.mdschema-keys-prefix-index-limits.mdschema-keys-selectivity-first.mdschema-model-avoid-agg-for-updates.mdschema-model-choose-for-workload.mdschema-model-prefer-mow.mdschema-model-sequence-col-for-cdc.mdschema-mv-async-join.mdschema-mv-async-limits.mdschema-mv-sync-rollup.mdschema-partition-auto-on-demand.mdschema-partition-dynamic-ttl.mdschema-partition-range-for-timeseries.md详细介绍
Apache Doris 最佳实践
面向问题的 Apache Doris 表设计指南。 37 条规则、7 个用例模板、4 份容量规划指南。 所有详细信息都在
references/目录中。
1 ▸ 面向问题的路由
我需要构建……
| 问题 | 模板 | 关键规则 |
|---|---|---|
| 实时日志/事件分析 | usecase-log-event | DUPLICATE、RANGE 分区、动态 TTL、ZSTD |
| CDC/将 MySQL 同步到 Doris | usecase-cdc-sync | UNIQUE MoW、sequence_col、HASH 分桶 |
| 使用预聚合指标的仪表板 | usecase-dashboard-metrics | AGGREGATE、BITMAP_UNION、同步 MV |
| 面向用户、低延迟点查询的 API | usecase-point-query | UNIQUE MoW、store_row_column、BloomFilter |
| JOIN 密集型分析的星型模式 | usecase-star-schema-join | Colocation、相同的分桶键/数量 |
| 小型维度表/查找表 | usecase-dimension-lookup | DUPLICATE、RANDOM 分桶、3 个桶 |
| 可观测性(日志 + 追踪 + 指标) | usecase-observability | 3 张表:DUP 日志、DUP 追踪、AGG 指标 |
| 车辆/车队追踪 | usecase-log-event + usecase-point-query | 时序 + 点查询混合模式 |
| 电商订单分析 | usecase-star-schema-join + usecase-dashboard-metrics | 星型模式 + AGG 汇总 |
| 全文搜索/内容搜索 | schema-index-text-search | 倒排索引、MATCH、BM25 |
| 用户行为/漏斗分析 | schema-types-bitmap-count-distinct | BITMAP_UNION、bitmap_intersect |
| 半结构化 JSON 数据 | schema-types-variant-json | VARIANT 类型、schema_template |
有证据表明我的查询较慢……
对于实时慢查询或运行时诊断,不要首先使用此表。请先阅读 references/cli-investigation.md,并收集或尝试获取证据(profile get、profile list、profile history、tablet、EXPLAIN 或 auth status)。仅在证据指向相应症状后才使用此表。
| 症状 | 检查这些规则 | 快速修复 |
|---|---|---|
| WHERE 条件导致全表扫描 | schema-keys-selectivity-first | 将过滤列移至排序键第 1 位 |
| JOIN 较慢/发生数据 Shuffle | usecase-star-schema-join | 小型维度表(<1GB):广播 + 运行时过滤。大型维度表:Colocation |
| COUNT DISTINCT 较慢 | schema-types-bitmap-count-distinct | 切换为 BITMAP_UNION 聚合 |
| LIKE '%keyword%' 较慢 | schema-index-ngram-for-like | 添加 NGram BloomFilter 索引 |
| 点查询延迟过高 | usecase-point-query | 启用 store_row_column + Prepared Statement |
| 存储增长过快 | schema-partition-auto-on-demand + schema-props-compression | AUTO PARTITION + ZSTD 压缩 + 定时 DROP PARTITION |
| 未使用同步 MV | schema-mv-sync-rollup | 在 MV GROUP BY 中使用原始列(而不是 date_trunc);使用唯一别名 |
| 异步 MV 改写失败 | schema-mv-async-join + schema-mv-async-limits | 检查 State/RefreshState;若谓词不匹配,则直接查询 MV |
| 数据倾斜/热点 tablet | schema-bucket-composite-for-skew | 使用复合分桶键或 RANDOM |
| 导入失败/数据版本错误 | schema-mv-async-limits | 检查并发 MV 刷新上限(最多 3 个) |
| 键中的 VARCHAR 导致性能下降 | schema-keys-fixed-length-types | 将 VARCHAR 移至定长类型之后 |
| UNIQUE 表写入较慢 | schema-model-prefer-mow | 确保启用 MoW(而不是 MoR) |
2 ▸ 预检清单(执行任何 CREATE TABLE 之前)
按顺序逐项检查。每个步骤都引用了相关规则:
- 数据模型 — UNIQUE(更新?)还是 DUPLICATE(追加?)还是 AGGREGATE(仅预聚合?)→
schema-model-choose-for-workload - 分区策略 — 时序数据?优先使用 AUTO PARTITION。小表?跳过分区。不要将 AUTO 与 dynamic_partition 结合使用。→
schema-partition-* - 分桶键 + 数量 — 对 JOIN 键使用 HASH。明确计算数量:
daily_GB / target_tablet_GB。如果数据量未知,使用明确的备用数量:小型维度表用 3,中型表用 8,大型日事实表用 16-32。→schema-bucket-* - 排序键顺序 — 高选择性列优先,定长列排在 VARCHAR 之前 →
schema-keys-* - 数据类型 — 使用原生类型,而非 STRING。使用 DECIMAL,而非 FLOAT。→
schema-types-* - 索引 — 等值查询使用 BloomFilter,文本查询使用 Inverted,LIKE 查询使用 NGram →
schema-index-* - 属性 — 是否启用 MoW?压缩?云模式下 replication_num=1?→
schema-props-* - DDL 硬性约束(违反任意一项时 Apache Doris 都会拒绝 DDL):
- UNIQUE KEY + PARTITION BY RANGE → 分区列必须包含在 UNIQUE KEY 中:
UNIQUE KEY(id, dt) PARTITION BY RANGE(dt) - 键列必须是架构中的前 N 列,且顺序相同——将键列放在前面,非键列放在后面。例如:
UNIQUE KEY(account_id, symbol)表示架构必须以account_id, symbol, ...开头——切勿在键列之间放置非键列 store_row_column = "true"适用于 UNIQUE MoW 和 DUPLICATE,不适用于 AGGREGATE(Doris 会拒绝 AGG:“Aggregate table can't support row column”)。已在 4.x 上验证;旧版本仅支持 UNIQUE- AUTO PARTITION 要求使用
date_trunc()且括号不能为空:AUTO PARTITION BY RANGE(date_trunc(col, 'day')) ()——仅写列名会失败,缺少()也会失败 - 动态分区要求在 DDL 中显式包含
PARTITION BY RANGE(col) ()子句——仅设置属性不够 - 不要设置
dynamic_partition.buckets;只在DISTRIBUTED BY HASH(col) BUCKETS N中填写数值 compaction_policy = "time_series"仅适用于 DUPLICATE 表——在 UNIQUE 表上会失败- 异步 MV 刷新:使用
REFRESH AUTO ON SCHEDULE EVERY 10 MINUTE或REFRESH COMPLETE ON SCHEDULE EVERY 10 MINUTE——不要使用REFRESH SCHEDULE EVERY,也不要使用REFRESH ASYNC EVERY(INTERVAL ...)。最短间隔:1 MINUTE - 使用
NOW()/CURDATE()的 MV:添加PROPERTIES ("enable_nondeterministic_function" = "true") - BOOLEAN 默认值必须加引号:使用
DEFAULT "true",不要使用DEFAULT TRUE - BloomFilter 索引:使用
PROPERTIES ("bloom_filter_columns" = "col1,col2")——不要使用内联的INDEX ... USING BLOOM FILTER - AGGREGATE 列语法:聚合函数必须放在默认值之前:
col BIGINT SUM DEFAULT "0"——不要使用col BIGINT DEFAULT "0" SUM - AGGREGATE 的
DEFAULT "null"仅适用于 VARCHAR——用于 INT、DATE、DECIMAL、BIGINT 时会失败。对非字符串类型的 REPLACE_IF_NOT_NULL,应完全省略 DEFAULT:vip_level INT REPLACE_IF_NOT_NULL(不要使用DEFAULT "null") enable_unique_key_partial_update是会话变量,不是表属性- 完整详情:
schema-ddl-gotchas
- UNIQUE KEY + PARTITION BY RANGE → 分区列必须包含在 UNIQUE KEY 中:
2b ▸ DDL 模板(复制最接近的模板,并自定义列)
对于每个 CREATE TABLE,从下方选择最接近的模板。自定义列名、类型、分桶数量和分区设置。不要从头编写 DDL。
T1:仅追加事件/日志(DUPLICATE)
CREATE TABLE events (
entity_id VARCHAR(64) NOT NULL,
event_time DATETIME NOT NULL,
event_type VARCHAR(50) NOT NULL,
payload VARIANT
) DUPLICATE KEY(entity_id, event_time, event_type)
PARTITION BY RANGE(event_time) ()
DISTRIBUTED BY HASH(entity_id) BUCKETS 10
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-90",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"compression" = "zstd",
"compaction_policy" = "time_series",
"replication_num" = "1"
);
T2:带分区的可更新表(UNIQUE MoW + CDC)
CREATE TABLE orders (
order_id BIGINT NOT NULL,
order_time DATETIME NOT NULL,
update_time DATETIME NOT NULL,
status VARCHAR(20),
amount DECIMAL(18,2)
) UNIQUE KEY(order_id, order_time)
PARTITION BY RANGE(order_time) ()
DISTRIBUTED BY HASH(order_id) BUCKETS 5
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"function_column.sequence_col" = "update_time",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-365",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"replication_num" = "1"
);
T3:小型维度表/查找表(UNIQUE,无分区)
CREATE TABLE dim_product (
product_id INT NOT NULL,
name VARCHAR(200),
category VARCHAR(50)
) UNIQUE KEY(product_id)
DISTRIBUTED BY HASH(product_id) BUCKETS 3
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"replication_num" = "1"
);
T4:预聚合 KPI(AGGREGATE)
CREATE TABLE daily_kpi (
stat_date DATE NOT NULL,
dimension VARCHAR(50) NOT NULL,
metric_sum BIGINT SUM DEFAULT "0",
metric_max DOUBLE MAX DEFAULT "0",
unique_users BITMAP BITMAP_UNION
) AGGREGATE KEY(stat_date, dimension)
PARTITION BY RANGE(stat_date) ()
DISTRIBUTED BY HASH(dimension) BUCKETS 3
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "MONTH",
"dynamic_partition.start" = "-12",
"dynamic_partition.end" = "1",
"dynamic_partition.prefix" = "p",
"replication_num" = "1"
);
T5:点查询/API 服务(UNIQUE MoW + 行存储)
CREATE TABLE user_profiles (
user_id BIGINT NOT NULL,
update_time DATETIME NOT NULL,
name VARCHAR(100),
data VARIANT
) UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 5
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"function_column.sequence_col" = "update_time",
"store_row_column" = "true",
"light_schema_change" = "true",
"replication_num" = "1"
);
3 ▸ 连接与 CLI
Apache Doris 使用 MySQL 协议,因此始终可用的连接方式是使用任意兼容 MySQL 的客户端(mysql),并通过 SQL 和 FE HTTP REST API 操作。某些发行版还提供可选的管理 CLI(本文称为 doriscli),增加了便捷的性能分析和诊断命令——如果你的发行版提供了它,请使用;否则,请使用原生方式。
检测可选 CLI
运行任何查询之前,请先检测 CLI 二进制文件是否可用:
- 检查
DORIS_CLI_PATH环境变量——如果已设置,则使用该二进制文件路径 - 执行
command -v doriscli——使用 PATH 中的版本 - 如果均不可用:回退到
mysql客户端(参见references/start-*.md)
如果 doriscli 可用,所有操作优先使用它:
| 任务 | doriscli 命令 |
|---|---|
| 运行 SQL | doriscli sql "SELECT ..." |
| 检查 DDL | doriscli sql "SHOW CREATE TABLE db.t" |
| 表/tablet 健康状态 | doriscli tablet db.t(概览)或 doriscli tablet db.t --detail |
| 分析慢查询 | doriscli sql "SELECT ..." --profile → 获取 query_id |
| 获取查询性能分析 | doriscli profile get <qid>,或使用 --full 进行完整诊断 |
| 比较快查询与慢查询 | doriscli profile diff <slow_qid> <fast_qid> |
| 性能趋势 | doriscli profile history <sql_pattern> --days 7 |
| 测试连接 | doriscli auth status |
| 切换环境 | doriscli use <name> |
运行时查询排查
对于慢查询或运行时性能问题,请阅读 references/cli-investigation.md。
- 必须先收集证据:在形成假设之前,先收集或尝试收集性能分析、tablet、DDL、统计信息、EXPLAIN、历史记录、活动查询或连接方面的证据。如果无法在本地收集证据,请说明这一点并提供要运行的确切命令
- 优先使用现有性能分析:重新执行 SQL 之前,先使用
profile get <query_id>、profile list或profile history - 主动发现:对于描述模糊的慢查询问题,先运行
auth status、profile list --active和近期的profile list,再向用户询问更多背景信息 - 安全检查:使用
--profile运行用户 SQL 之前,请检查其安全性(不包含 DDL、不执行数据变更、不进行无界扫描)。对于未知、高峰时段或开销较大的 SQL,请先运行doriscli sql "EXPLAIN <query>" --format json,然后征求确认或请求现有的 query_id - 提供假设,而非定论:诊断对应关系是启发式的。请说明证据、可能的原因、下一步需要检查的内容,以及结论可能不成立的情况
- 如果 doriscli 不可用,请回退到参考文档中列出的 SQL 命令
- 结构化且便于 Agent 读取的输出一律使用
--format json
快速入门指南
references/start-self-hosted.md— 自托管/BYOC/本地部署- 云模式(存算分离)的连接方式仅 HTTP 端口不同(8080 与 8030)——同样适用此指南
4 ▸ 集群容量规划
容量规划指南见:
references/sizing-fe.md— FE 节点容量规划references/sizing-be-integrated.md— BE 容量规划(集成存储)references/sizing-be-cloud.md— BE 容量规划(云端/存算分离)references/sizing-storage-formula.md— 存储计算公式
5 ▸ 按类别划分的规则索引
数据模型 — 关键(4 条规则)
schema-model-choose-for-workload— DUP、UNIQUE 与 AGG 的决策树schema-model-prefer-mow— UNIQUE 表始终使用 MoWschema-model-avoid-agg-for-updates— AGG 不支持 UPDATE/DELETEschema-model-sequence-col-for-cdc— 为乱序 CDC 使用 Sequence 列
分区策略 — 关键(4 条规则)
schema-partition-range-for-timeseries— 时间序列数据使用 RANGEschema-partition-dynamic-ttl— 使用动态分区实现自动 TTLschema-partition-auto-on-demand— 零散数据使用 AUTOschema-partition-skip-for-small— 小于 1 GB 时跳过分区
分桶策略 — 严重(5 条规则)
schema-bucket-hash-vs-random— 使用 HASH 进行裁剪,仅对 DUP 使用 RANDOMschema-bucket-high-cardinality-key— 选择高基数列schema-bucket-composite-for-skew— 使用复合键解决数据倾斜schema-bucket-target-size— 每个 tablet 的目标大小为 1-10 GBschema-bucket-cloud-mandatory-hash— Cloud MoW 必须使用 HASH
排序键 — 严重(5 条规则)
schema-keys-selectivity-first— 优先选择高选择性列schema-keys-fixed-length-types— 定长类型排在 VARCHAR 前schema-keys-prefix-index-limits— 最多 36 字节,遇到 VARCHAR 时终止schema-keys-cluster-key-for-mow— UNIQUE 表使用聚簇键schema-keys-avoid-float— 排序键中不要使用 FLOAT/DOUBLE
数据类型 — 高(5 条规则)
schema-types-native-vs-string— 使用原生类型,而非 STRINGschema-types-zonemap-limitations— JSON/ARRAY 会禁用 ZoneMapschema-types-variant-json— 使用 VARIANT 存储半结构化 JSONschema-types-bitmap-count-distinct— 使用 BITMAP_UNION 进行精确去重计数schema-types-doris-specifics— DATETIME 精度、VARCHAR 与 STRING 的区别
索引 — 高(7 条规则)
schema-index-bloomfilter— 对等值查询使用 BloomFilterschema-index-inverted— 对文本/范围查询使用倒排索引schema-index-ngram-for-like— 对 LIKE %pattern% 使用 NGramschema-index-bitmap— 对中等基数使用 Bitmapschema-index-vector— 对 ANN 搜索使用 HNSW/IVFschema-index-text-search— 使用全文 MATCH + BM25
查询加速 — 高(3 条规则)
schema-mv-sync-rollup— 对单表聚合使用同步 MVschema-mv-async-join— 对多表 JOIN 使用异步 MVschema-mv-async-limits— 运行限制(50M 行,3 个并发)
表属性 — 高/中(2 条规则)
schema-props-cloud-forced— Cloud 模式强制属性schema-props-compression— LZ4 与 ZSTD 压缩
缓存 — 中(2 条规则)
schema-cache-file-cache— Cloud 模式使用文件缓存schema-cache-query-partition— 查询缓存和分区缓存