当前位置: 首页 > backend >正文

【Doris入门】Doris数据表模型使用指南:核心注意事项与实践

目录

引言

1 建表规范与实践

1.1 列类型选择建议

1.2 建表示例

2 聚合模型的局限性及解决方案

2.1 聚合模型的核心局限性

2.2 聚合模型局限性示例

2.3 count(*)查询的性能问题

2.4 解决方案

2.4.1 方案一:增加SUM类型的count列

2.4.2 方案二:使用REPLACE类型的count列

3 Unique模型的写时合并实现优势

3.1 性能优势对比

3.2 实际应用示例

4 Key列在不同模型中的不同意义

4.1 Key列的含义对比

4.2 各模型Key列特点

4.3 实际应用中的影响

5 数据模型选择建议

5.1 模型选择决策流程

5.2 各模型适用场景

5.2.1 Aggregate模型适用场景

5.2.2 Unique模型适用场景

5.2.3 Duplicate模型适用场景

5.3 模型选择决策表

6 总结


引言

Apache Doris作为一款高性能的分布式分析型数据库,提供了三种主要的数据模型:
  • 明细模型(Duplicate Key Model)
  • 聚合模型(Aggregate Key Model)
  • 主键模型(Unique Key Model)
每种数据模型都有其特定的适用场景和局限性,正确选择和使用数据模型对于系统性能和业务需求满足至关重要。

1 建表规范与实践

1.1 列类型选择建议

  • 在建表时,列类型的选择直接影响查询性能和存储效率,需要特别注意以下几点:
具体建议:
  • Key列必须在所有Value列之前
    • 这是Doris的硬性要求,违反会导致建表失败
    • Key列用于数据排序和分组,Value列用于存储实际业务数据
    • 合理的列顺序可以优化存储结构和查询性能
  • 尽量选择整型类型
    • 整型类型的计算和查找效率远高于字符串类型
    • 常用整型类型:TINYINT、SMALLINT、INT、BIGINT、LARGEINT
    • 整型类型在内存占用和CPU计算方面都有优势
  • 整型类型遵循够用原则
    • 根据数据范围选择合适的整型长度
    • 例如年龄字段用TINYINT(0-255)足够,无需使用INT
    • 避免过度使用大类型造成存储浪费
  • VARCHAR和STRING类型长度控制
    • 遵循"够用即可"原则,避免定义过大的长度
    • 例如用户名定义VARCHAR(50)而非VARCHAR(1000)
    • 合理的长度定义可以显著提升压缩率和查询性能

1.2 建表示例

-- 推荐的建表示例
CREATE TABLE IF NOT EXISTS ods_db.optimized_user_profile(user_id BIGINT NOT NULL COMMENT "用户ID",register_date DATE NOT NULL COMMENT "注册日期",user_age TINYINT COMMENT "年龄",city_code SMALLINT COMMENT "城市编码",user_level TINYINT COMMENT "用户等级",last_login_time DATETIME COMMENT "最后登录时间",total_consumption DECIMAL(10,2) COMMENT "总消费金额",login_count INT COMMENT "登录次数",is_active TINYINT COMMENT "是否活跃"
)DUPLICATE KEY(user_id, register_date)
DISTRIBUTED BY HASH(user_id) BUCKETS 1
PROPERTIES ("replication_allocation" = "tag.location.default: 1","compression" = "zstd"
);

2 聚合模型的局限性及解决方案

2.1 聚合模型的核心局限性

  • 聚合模型虽然通过预聚合机制提升了查询性能,但也存在一些重要的局限性:
主要局限性:
  • 数据一致性保证机制
    • 聚合模型需要保证不同导入批次的数据最终聚合结果的一致性
    • 在查询引擎中加入聚合算子来保证数据对外的一致性
    • 这种一致性保证在某些查询中会极大地降低查询效率
  • count(*)查询性能问题
    • 在聚合模型中,count(*)查询需要扫描所有的AGGREGATE KEY列
    • 必须同时读取所有Key列的数据,加上查询时聚合,才能得到正确结果
    • 当聚合列非常多时,count(*)查询需要扫描大量的数据

2.2 聚合模型局限性示例

ColumnName

Type

AggregationType

Comment

user_id

LARGEINT

用户id

date

DATE

数据灌入日期

cost

BIGINT

SUM

用户总消费

  • 导入两批次数据:

user_id

date

cost

10001

2025/11/20

50

10002

2025/11/21

39

user_id

date

cost

10001

2025/11/20

1

10001

2025/11/21

5

10003

2025/11/22

22

  • 最终聚合结果:

user_id

date

cost

10001

2025/11/20

51

10001

2025/11/21

5

10002

2025/11/21

39

10003

2025/11/22

22

2.3 count(*)查询的性能问题

-- 这个查询在聚合模型中性能很差
SELECT COUNT(*) FROM user_cost_table;
-- 正确结果应该是4,但需要扫描所有Key列才能得到
问题分析:
  • 如果只扫描user_id列,加上查询时聚合,得到3(10001, 10002, 10003)
  • 如果不加查询时聚合,得到5(两批次一共5行数据)
  • 两个结果都不正确,必须同时读取user_id和date列才能得到正确结果4

2.4 解决方案

2.4.1 方案一:增加SUM类型的count列

-- 修改表结构,增加count列
CREATE TABLE ads_db.user_cost_table_optimized(user_id BIGINT,date DATE,cost BIGINT SUM,count BIGINT SUM  -- 用于计算count
)AGGREGATE KEY(user_id, date)
PROPERTIES ("replication_num" = "1"
);
  • 导入数据时,count列值恒为1:
-- 导入数据
INSERT INTO ads_db.user_cost_table_optimized VALUES
(10001, '2025-08-20', 50, 1),
(10002, '2025-08-21', 39, 1);INSERT INTO ads_db.user_cost_table_optimized VALUES
(10001, '2025-08-20', 1, 1),
(10001, '2025-08-21', 5, 1),
(10003, '2025-08-22', 22, 1);
  • 查询优化:
-- 使用sum(count)替代count(*)
SELECT SUM(count) FROM ads_db.user_cost_table_optimized;
-- 这个查询效率远高于直接使用COUNT(*)
mysql> SELECT SUM(count) FROM ads_db.user_cost_table_optimized;
+--------------+
| sum(`count`) |
+--------------+
|            5 |
+--------------+
1 row in set (0.31 sec)mysql> 

2.4.2 方案二:使用REPLACE类型的count列

-- 修改表结构,使用REPLACE聚合类型
CREATE TABLE ads_db.user_cost_table_optimized_v2(user_id BIGINT,date DATE,cost BIGINT SUM,count BIGINT REPLACE
) AGGREGATE KEY(user_id, date)
DISTRIBUTED BY HASH(user_id) BUCKETS 1  -- 指定分桶键和桶数量
PROPERTIES ("replication_num" = "1"
);
# 优势: 没有导入重复行的限制,sum(count)和count(*)的结果一致

3 Unique模型的写时合并实现优势

3.1 性能优势对比

3.2 实际应用示例

-- 创建使用写时合并的Unique表
CREATE TABLE dwd_db.user_profile_unique(user_id BIGINT NOT NULL,username VARCHAR(50) NOT NULL,city VARCHAR(20),age INT,last_login_time DATETIME,login_count INT DEFAULT "0"
) UNIQUE KEY(user_id, username)
DISTRIBUTED BY HASH(user_id) BUCKETS 1
PROPERTIES ("enable_unique_key_merge_on_write" = "true","replication_num" = "1"
);-- 数据导入
INSERT INTO dwd_db.user_profile_unique VALUES
(1001, 'tom', 'beijing', 25, '2025-01-01 10:00:00', 10),
(1002, 'jerry', 'shanghai', 30, '2025-01-01 11:00:00', 5);-- 更新数据
INSERT INTO dwd_db.user_profile_unique VALUES
(1001, 'tom', 'beijing', 26, '2025-01-02 10:00:00', 15);-- count(*)查询性能优异
SELECT COUNT(*) FROM dwd_db.user_profile_unique;

4 Key列在不同模型中的不同意义

4.1 Key列的含义对比

4.2 各模型Key列特点

模型

Key列含义

唯一约束

数据处理

Duplicate

仅作为排序列

保留所有数据,包括重复数据

Aggregate

排序列+唯一标识列

按聚合函数处理重复数据

Unique

排序列+唯一标识列

保留最新数据,覆盖旧数据

4.3 实际应用中的影响

-- Duplicate模型:Key列只用于排序
CREATE TABLE ods_db.logs_duplicate(log_time DATETIME,log_type INT,content VARCHAR(1024)
)DUPLICATE KEY(log_time, log_type)
PROPERTIES ("replication_num" = "1"
);-- Aggregate模型:Key列用于聚合
CREATE TABLE ads_db.stats_aggregate(date DATE,user_id BIGINT,amount BIGINT SUM
)AGGREGATE KEY(date, user_id)
PROPERTIES ("replication_num" = "1"
);-- Unique模型:Key列用于去重
CREATE TABLE dwd_db.users_unique(user_id BIGINT,username VARCHAR(50),last_login_time DATETIME REPLACE
)UNIQUE KEY(user_id, username)
PROPERTIES ("replication_num" = "1"
);

5 数据模型选择建议

5.1 模型选择决策流程

5.2 各模型适用场景

5.2.1 Aggregate模型适用场景

适用场景:
  • 有固定模式的报表类查询
  • 需要预聚合统计的场景
  • 数据量大且需要汇总分析
  • 对count(*)查询要求不高的场景
不适用场景:
  • 频繁的count(*)查询
  • 需要灵活Ad-hoc查询的场景
  • 数据需要频繁更新的场景

5.2.2 Unique模型适用场景

适用场景:
  • 需要数据更新的场景
  • 有主键唯一约束的业务需求
  • 对查询性能要求高的场景
  • 频繁进行count(*)查询的场景
推荐实现:
  • 优先使用写时合并实现(2.1+版本默认)
  • 对于写多读少的场景,可考虑读时合并

5.2.3 Duplicate模型适用场景

适用场景:
  • 需要保留原始明细数据的场景
  • 需要支持任意维度Ad-hoc查询的场景
  • 有数据审计和追溯需求的场景
  • 数据相对稳定,很少更新的场景
不适用场景:
  • 需要频繁更新数据的场景
  • 对存储空间敏感的场景
  • 有固定查询模式的场景

5.3 模型选择决策表

业务需求

推荐模型

实现方式

原因

频繁count(*)查询

Unique

写时合并

性能最优,支持高效count

固定报表查询

Aggregate

默认

预聚合,查询性能好

灵活Ad-hoc查询

Duplicate

默认

支持任意维度查询

数据需要更新

Unique

写时合并

支持数据更新和去重

数据需要审计

Duplicate

默认

保留原始数据

存储空间敏感

Aggregate

默认

预聚合节省空间

写多读少场景

Unique

读时合并

写入性能好

6 总结

正确选择和使用Doris数据表模型是充分发挥其性能优势的关键。在实际应用中,我们需要:
  • 深入理解各模型特点:充分了解Aggregate、Unique、Duplicate三种模型的优势和局限性
  • 根据业务需求选择:结合具体的业务场景、查询模式、更新需求等因素选择合适的模型
  • 持续优化和监控:建立完善的监控体系,持续优化查询性能和存储效率
  • 关注最佳实践:遵循本文提到的最佳实践,避免常见的性能陷阱
通过合理的数据模型选择和优化,Doris能够为各种大数据分析场景提供卓越的性能支持,助力企业构建高效的数据分析平台。
http://www.xdnf.cn/news/19895.html

相关文章:

  • select, poll, epoll
  • PyTorch 损失函数与优化器全面指南:从理论到实践
  • 论文理解:Reflexion: Language Agents with Verbal Reinforcement Learning
  • 【正则表达式】 正则表达式运算法优先级的先后是怎么排序的?
  • 【Pytest】解决Pytest中Teardown钩子的TypeError:实例方法与类方法的调用差异
  • Java中最常用的设计模式
  • Mysql主从复制之延时同步
  • 【Linux基础】Linux系统管理:深入理解Linux运行级别及其应用
  • 面经分享二:Kafka、RabbitMQ 、RocketMQ 这三中消息中间件实现原理、区别与适用场景
  • 笔记:卷积神经网络(CNN)
  • VS2015+QT编译protobuf库
  • 【倒计时2个月】好•真题资源+专业•练习平台=高效备赛2025初中古诗文大会
  • 达人数据导出:小青苔如何让达人数据管理效率飙升?
  • 海康摄像头开发---JSON数据与图片分离
  • 近期刷题总结
  • ChartView的基本介绍与使用
  • 江协科技STM32学习笔记补充之004 基于XC6206P332MR(Torex)的5V到3.3V的电压转换电路分析
  • 2025年中国GEO优化服务机构官方信息汇总与能力概览
  • 《增广贤文》读书笔记(四)
  • 热烈庆祝 | 一二三物联网携这款产品入选2025年度山东省首台(套)技术装备生产企业及产品名单
  • “硬件初始化配置,包括芯片选型、时钟树设计、GPIO/外设参数设置”一般都是哪些需要配置
  • 腾讯云《意愿核身移动 H5》 快速完成身份验证接入
  • 【GitOps】初始Argo CD
  • Unity学习----【进阶】Addressables(一)--概述与简单的使用
  • 小企业环境-流水线管理
  • vue2头部布局示例
  • 基于https+域名的Frp内网穿透教程(Linux+Nginx反向代理)
  • c语言程序之魂——算法(练习题,流程图,程序源码)
  • 2025 年国内外十大顶尖低代码开发平台排行榜
  • 【C++】控制台输入与输出