未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用范围与待维护事项
本文用于理解Doris核心概念,不是已验证的生产建模或性能调优方案。原稿未注明Doris版本,也未提供官方依据或SQL执行记录;前缀索引字节规则、JSON支持及物化视图能力需按目标版本核对。执行示例前须选择数据库并确认BE与副本配置;Unique模型的“最新”不等于业务时间最新,排序键和分桶效果取决于负载与数据分布。同步、异步物化视图的刷新及查询改写不可混用,本文的修订不代表示例已实测。
Doris核心概念 - 构建数据模型的基石
abstract 📋 本节概览
- 🎯 学习目标:掌握Doris的核心概念及其关系
- 📚 前置知识:02-Doris架构深度解析|Doris架构深度解析
- ⏱️ 预计用时:20分钟
- 💡 关键收获:理解Doris数据组织方式和查询优化基础
🎯 实际应用价值
理解Doris的数据模型、分区分桶和索引,有助于分析建表与查询设计问题;实际收益需结合数据分布和查询负载验证。
在实际工作环境中,这样的挑战每天都在发生。理解相关知识不仅能解决当前问题,更能提升整体的专业能力。
💡 数据库与表
数据库(Database)
- 逻辑隔离的数据集合,类似MySQL的数据库概念
- 用于组织和管理相关表的容器
表(Table)
- 数据的基本组织单元,由行和列组成
- 在Doris中,表有明确的模型定义和数据分布策略
-- 创建数据库
CREATE DATABASE IF NOT EXISTS example_db;
-- 创建表
CREATE TABLE example_db.example_table (
user_id BIGINT,
username VARCHAR(50),
age INT
) ENGINE=OLAP
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
🧩 数据模型
Doris支持三种主要数据模型,每种模型针对不同的应用场景进行了优化:
1. 明细模型(Duplicate)
info 📚 明细模型类比
就像图书馆保存每一本原版图书,不做任何合并或删减,保留所有原始信息。
- 特点:存储完整明细数据,无聚合,支持按任意列查询
- 适用场景:需要查询原始数据,如日志明细、原始事件
- 数据导入:支持追加写入,不会合并或聚合数据
CREATE TABLE user_logs (
log_id BIGINT,
user_id INT,
action VARCHAR(20),
log_time DATETIME,
page VARCHAR(50)
) ENGINE=OLAP
DUPLICATE KEY(log_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
2. 聚合模型(Aggregate)
info 📚 聚合模型类比
类似超市每日销售报表,不记录每一笔交易明细,而是自动按商品、类别等维度汇总销售额、销量等指标。
- 特点:按维度列自动聚合指标列,减少存储空间,提高查询性能
- 适用场景:指标分析,如销售汇总、用户访问统计
- 数据导入:自动按Key列聚合Value列,多批次导入会合并计算
CREATE TABLE sales_agg (
sale_date DATE,
product_id INT,
region VARCHAR(50),
sales_count BIGINT SUM,
sales_amount DECIMAL(10,2) SUM
) ENGINE=OLAP
AGGREGATE KEY(sale_date, product_id, region)
DISTRIBUTED BY HASH(product_id) BUCKETS 10;
3. 更新模型(Unique)
info 📚 更新模型类比
就像手机通讯录,同一个联系人只保留最新信息,旧信息会被覆盖更新。
- 特点:按Key列去重并更新数据;“最新”取决于写入顺序或配置的Sequence列,不一定是业务时间最新
- 适用场景:维度表、需要更新的事实表、保留最新状态的场景
- 数据导入:按Key列处理重复记录;同一Key的覆盖顺序需结合写入顺序和Sequence列配置理解
CREATE TABLE user_profiles (
user_id INT,
username VARCHAR(50),
email VARCHAR(100),
last_login DATETIME,
status TINYINT
) ENGINE=OLAP
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
| 模型类型 | 特点 | 适用场景 | 生活类比 |
|---|---|---|---|
| Duplicate | 存储全部数据 | 日志、原始数据 | 图书馆完整收藏 |
| Aggregate | 自动聚合 | 指标分析 | 超市销售日报表 |
| Unique | 自动去重更新 | 维度表、需更新的表 | 通讯录(同名覆盖) |
🗂️ 分区与分桶
分区(Partition)
info 📚 分区类比
就像大型图书馆按主题划分不同区域(历史区、科学区、文学区),方便管理和快速定位。
- 作用:水平切分数据的第一层,通常按时间或地区等维度
- 优势:
- 支持分区级别的数据生命周期管理
- 查询时可跳过不相关分区(分区裁剪)
- 支持动态添加/删除分区
分桶(Bucket)
info 📚 分桶类比
在图书馆的每个主题区内,再按作者姓氏首字母分类存放,进一步缩小查找范围。
- 作用:分区内的进一步细分,通常按Hash值分布
- 优势:
- 通过分桶将数据分布到多个BE节点;是否均匀取决于分桶键取值分布、桶数和节点布局
- 支持并行处理提高查询性能
- 合理选择分桶键可缓解数据倾斜;热点键集中时仍可能倾斜
-- 创建带分区分桶的表
CREATE TABLE sales (
sale_date DATE,
region VARCHAR(50),
product_id INT,
sales_amount DECIMAL(10,2)
)
ENGINE=OLAP
PARTITION BY RANGE(sale_date) (
PARTITION p202301 VALUES [('2023-01-01'), ('2023-02-01')),
PARTITION p202302 VALUES [('2023-02-01'), ('2023-03-01'))
)
DISTRIBUTED BY HASH(product_id) BUCKETS 10;
📊 索引与排序
前缀索引
info 📚 前缀索引类比
类似字典的页眉,标记每页开头的单词,帮助快速定位到大致位置。
- 特点:基于有序列的前缀构建,默认索引前36个字节
- 作用:加速查询条件过滤,减少数据扫描量
排序键(Sort Key)
info 📚 排序键类比
就像图书按特定顺序排列在书架上,方便快速查找。
- 特点:决定数据物理存储顺序的列
- 作用:影响查询性能和数据压缩率
- 设计原则:
- 结合高频过滤条件、谓词类型和基数选择排序键顺序,不应仅因ID基数高就将其放在最前
- 常用过滤条件列放前面
- 经常一起查询的列放一起
稀疏索引
- 特点:按数据块而非每行记录索引项,以减少索引大小;Doris前缀索引采用稀疏索引组织方式,二者不是两种独立索引
- 作用:快速定位数据块,减少不必要的IO
🔄 物化视图
info 📚 物化视图类比
就像超市预先准备的各种统计报表,虽然从原始销售数据也能计算出来,但预先计算好可以极大提高查询速度。
- 定义:预先计算并存储的查询结果集
- 作用:加速查询、减少计算量
- 特点:
- 优化器可在满足改写条件时选择物化视图,不保证每个查询都命中或获得最优性能
- 同步物化视图随基表写入维护;异步物化视图按配置刷新,可能存在数据延迟
- 支持的聚合、索引及查询改写能力取决于物化视图类型和Doris版本
-- 创建物化视图
CREATE MATERIALIZED VIEW mv_region_daily
AS SELECT region, sale_date, SUM(sales_amount) as total_sales
FROM sales GROUP BY region, sale_date;
📋 数据类型
Doris支持多种数据类型,适应不同的业务需求:
整数类型
- TINYINT:1字节,范围-128到127
- SMALLINT:2字节,范围-32768到32767
- INT:4字节,范围约±21亿
- BIGINT:8字节,范围约±9.2E18
浮点与定点类型
- FLOAT:4字节单精度浮点数
- DOUBLE:8字节双精度浮点数
- DECIMAL:精确的定点数,如DECIMAL(10,2),不属于浮点类型
字符串类型
- CHAR:固定长度字符串
- VARCHAR:可变长度字符串
- STRING:可变长度字符串类型,可用于较长文本;长度限制及使用约束需按Doris版本确认
日期时间类型
- DATE:日期类型,格式YYYY-MM-DD
- DATETIME:日期时间类型,格式YYYY-MM-DD HH:MM:SS
其他类型
- BOOLEAN:布尔类型,值为TRUE或FALSE
- ARRAY:数组类型,如ARRAY<INT>
- JSON:JSON类型(新版本支持)
🔄 数据导入与导出
导入方式
- Stream Load:适合小批量实时导入
- Broker Load:适合从HDFS等外部存储导入大量数据
- Routine Load:持续消费Kafka数据
- Insert Into:通过SQL插入数据
导出方式
- Select Into Outfile:查询结果导出到文件
- Export:导出表数据到外部存储
🔄 概念之间的关系
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TD
classDef default fill:#FDFBF5,stroke:#6E6A60,color:#2D2A26,stroke-width:1.5px
classDef startend fill:#2D2A26,stroke:#2D2A26,color:#FDFBF5,font-weight:bold
classDef decision fill:#F6EAD8,stroke:#C08A1A,color:#2D2A26,font-weight:bold
classDef hub fill:#3B5A80,stroke:#3B5A80,color:#FDFBF5,font-weight:bold
classDef io fill:#E5EAF0,stroke:#3B5A80,color:#2D2A26
classDef accent fill:#A6432C,stroke:#A6432C,color:#FDFBF5,font-weight:bold
classDef storage fill:#F3EEDF,stroke:#9A9488,color:#2D2A26
A[数据库]
B[表]
A --> B --> C1[数据模型]
C1 --> D1[Duplicate]
C1 --> D2[Aggregate]
C1 --> D3[Unique]
B --> C2[分区策略]
C2 --> E1[Range分区]
C2 --> E2[List分区]
B --> C3[分桶策略]
B --> C4[物化视图]
B --> C5[索引]
C5 --> F1[前缀索引]
C5 --> F2[Bitmap索引]
C5 --> F3[Bloom Filter]
class A,B,C1,D1,D2,D3,C2,E1,E2,C3,C4,C5,F1,F2,F3 default
- 数据库包含多个表
- 表采用特定数据模型
- 表可按需划分为多个分区
- 分区内数据按分桶分布在多个BE节点
- 数据按排序键排序并建立前缀索引
- 物化视图基于基表创建,加速特定查询
⚠️ 常见问题与解决方案
warning 🚨 注意事项
- 分区设计过细:导致元数据过多,FE压力大
- 解决:控制分区数量,按查询过滤和生命周期管理需求适当增大分区粒度
- 分桶数过多:增加Tablet数量和管理开销,可能影响查询与写入性能
- 解决:根据数据量和节点数合理设置分桶数
- 排序键设计不当:影响数据压缩率和查询性能
- 解决:根据高频过滤条件、谓词类型及列基数安排排序键,并通过实际查询验证效果
✅ 掌握检查
- [ ] 能说出Doris三种主要数据模型及适用场景
- [ ] 理解分区分桶的作用及设计原则
- [ ] 掌握排序键和前缀索引的概念及设计原则
- [ ] 了解物化视图的作用及使用场景
- [ ] 熟悉常用数据类型及选择原则
🔗 学习连接
前置知识: Doris架构 下一步: Doris与其他数据库对比 相关概念: 数据模型设计, 表结构设计与优化