跳到主要内容

优化表 Schema 设计:表模型、分桶列、Key 列与字段类型调优

表 Schema 设计是 Doris 性能调优的关键环节,直接影响数据分布、查询并行度与排序效率。

不合理的 Schema 设计常导致以下问题:

  • 数据倾斜,查询并行度无法充分利用
  • 排序特性失效,等值/范围查询变慢
  • 字段类型选择不当,计算开销升高

更详细的设计原则可参考 数据表设计 章节。本章从实际案例出发,展示典型 Schema 设计问题及调优建议。

调优 Checklist

在设计或排查表 Schema 时,建议依次检查以下项:

  • 是否选择了与业务匹配的表模型(Duplicate / Unique / Aggregate)?
  • 分桶列是否散列均匀,无 null 或固定值倾斜?
  • 高频等值/范围查询列是否定义为 Key 列?
  • 字段类型是否遵循「定长优先、低精优先」原则?

案例 1:表模型选择

Doris 提供 Duplicate、Unique(MOR/MOW)、Aggregate 三种表模型,查询性能与功能特性各不相同。

三种表模型对比

表模型查询性能是否支持更新典型场景
Duplicate最高不支持日志、明细数据的高性能查询
Unique(MOW)较高支持需主键去重、对查询性能要求较高
Unique(MOR)一般支持需主键去重,写入频繁
Aggregate一般聚合更新预聚合报表、指标汇总

性能排序:Duplicate > MOW > MOR ≈ Aggregate

优化建议

业务无数据更新需求且对查询性能要求高时,优先使用 Duplicate 表

案例 2:分桶列选择

一句话定义:分桶列决定数据在 Bucket 间的分布,选择不当会引发数据倾斜,进而导致查询性能瓶颈。

合理的分桶列设计能:

  • 防止数据倾斜,充分利用并行能力
  • 最大化 Colocate Join、Bucket Shuffle Join 的效果

反例:c2 列存在大量 null

下例将分桶列设为 c2,但导入数据中 c2 全为 null,导致 64 个分桶中只有 1 个分桶承载全部数据:

CREATE TABLE `t1` (
`c1` INT NULL,
`c2` INT NULL
) ENGINE=OLAP
DUPLICATE KEY(`c1`)
DISTRIBUTED BY HASH(`c2`) BUCKETS 64
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
insert into t1 select number, null from numbers ('number'='10000000');

优化方案:改用散列度高的列

将分桶列从 c2 改为 c1,使数据均匀分布到各 Bucket,提升并行处理能力。

数据倾斜排查命令

目的:确认分桶字段是否倾斜 命令

select c2, count(*) cnt from t1 group by c2 order by cnt desc limit 10;

说明:如果 Top 值的 cnt 远大于其他值,说明该列存在严重倾斜,不适合作为分桶列。

分桶列选择原则

  • 避免使用业务上易出现 null 或固定值的列
  • 优先选择业务含义上散列度高的字段,如用户 ID、订单 ID
  • 建表前预估字段值分布,必要时抽样验证
优化建议

检查分桶列是否存在数据倾斜,如有,更换为散列度高的字段作为分桶列。事前设计可显著降低事后定位与修正成本。

案例 3:Key 列优化

一句话定义:Doris 在存储层按 Key 列排序,将高频查询列定义为 Key 列,可显著加速等值与范围查询。

业务查询示例

select * from t1 where t1.c1 = 1;
select * from t1 where t1.c1 > 1 and t1.c1 < 10;
select * from t1 where t1.c1 in (1, 2, 3);

优化方案:将 c1 设为 Key 列

CREATE TABLE `t1` (
`c1` INT NULL,
`c2` INT NULL
) ENGINE=OLAP
DUPLICATE KEY(`c1`)
DISTRIBUTED BY HASH(`c2`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
优化建议

将业务查询中频繁使用的等值或范围查询列定义为 Key 列,以加速查询过程。

案例 4:字段类型优化

一句话定义:字段类型直接影响计算复杂度,定长类型与低精类型在处理上比变长类型与高精类型更高效。

类型选择原则

原则推荐避免
定长优先INT、BIGINT、DATE、DATETIMEVARCHAR、STRING
低精优先INT、BIGINT、FLOATDECIMAL(高精度场景)

常见替换场景

  • 用 BIGINT 替代用于存储数值的 VARCHAR / STRING 字段
  • 用 FLOAT / INT / BIGINT 替代非必要的 DECIMAL 字段
  • 用 DATETIME 替代字符串形式的时间字段
优化建议

定义 Schema 类型时遵循「定长优先、低精优先」原则,提升计算效率和系统性能。

常见问题

Q1:建表后发现分桶列不合理,如何调整?

分桶列在建表后无法直接修改,需要新建表并重新导入数据,或使用 ALTER TABLE 创建新的 Rollup / 分区方案。建议建表前充分评估字段散列度。

Q2:Key 列越多越好吗?

并非如此。Key 列过多会增加存储排序开销和写入成本。仅将真正高频用于等值或范围过滤的列设为 Key 列。

Q3:什么时候必须使用 Unique 或 Aggregate 模型?

  • 需要按主键去重或更新数据 → Unique
  • 需要预聚合(SUM、MAX、MIN 等)→ Aggregate
  • 仅追加明细且追求极致查询性能 → Duplicate

Q4:如何判断当前表是否存在数据倾斜?

执行以下 SQL 排查分桶列分布:

select <bucket_col>, count(*) cnt from <table> group by <bucket_col> order by cnt desc limit 10;

若 Top 值数量远超其他值,则存在倾斜。

总结

精心设计的 Schema 能最大化利用 Doris 特性,显著提升查询性能;反之则可能导致数据倾斜等全局性问题。

调优要点:

  • 优先选择 Duplicate 表模型(无更新需求场景)
  • 分桶列选择散列度高的字段,避免 null 或固定值
  • 将高频查询列定义为 Key 列
  • 字段类型遵循「定长优先、低精优先」原则

事前设计永远比事后调优成本更低,建议在 Schema 设计阶段严格执行上述原则。