一、安装部署
安装教程:GitHub地址
Doc文档:Apache Doris 简介 - Apache Doris
二、功能及作用
- Apache Doris 是一款基于MPP 架构的高性能、实时分析型数据库。它以高效、简单和统一的特性著称,能够在亚秒级的时间内返回海量数据的查询结果。Doris 既能支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。
- 基于这些优势,Apache Doris 非常适合用于报表分析、即席查询、统一数仓构建、数据湖联邦查询加速等场景。用户可以基于 Doris 构建大屏看板、用户行为分析、AB 实验平台、日志检索分析、用户画像分析、订单分析等应用,适合处理结构化数据
三、整体架构
Apache Doris 使用 MySQL 协议,与 MySQL 语法高度兼容,并支持标准 SQL。用户可以通过各种客户端工具访问 Apache Doris,它与BI工具无缝集成 - Frontend(FE):主要负责用户请求的接入、查询解析规划、元数据的管理、节点管理相关工作
- Master节点:负责元数据的读写。当 Master 节点的元数据发生变更后,会通过 BDB JE 协议同步给 Follower 或 Observer 节点
- Follower节点:负责读取元数据。当 Master 节点发生故障时,可以选取一个 Follower 节点作为新的 Master 节点
- Observer节点:负责读取元数据,主要目的是增加集群的查询并发能力。Observer 节点不参与集群的选主过程
- Backend(BE):主要负责数据存储、查询计划的执行
四、数据表设计
1. 数据模型
Doris 中建表时需要指定表模型,以定义数据存储与管理方式。在 Doris 中提供了明细模型、聚合模型以及主键模型三种表模型,可以应对不同的应用场景需求。
- 明细模型(Duplicate Key Model)(默认):允许指定的 Key 列重复,Doirs 存储层保留所有写入的数据,适用于必须保留所有原始数据记录的情况;
- 主键模型(Unique Key Model):每一行的 Key 值唯一,可确保给定的 Key 列不会存在重复行,Doris 存储层对每个 key 只保留最新写入的数据,适用于数据更新的情况;
- 聚合模型(Aggregate Key Model):可根据 Key 列聚合数据,Doris 存储层保留聚合后的数据,从而可以减少存储空间和提升查询性能;通常用于需要汇总或聚合信息(如总数或平均值)的情况。
1.1 明细模型(Duplicate Key Model)
一般明细模型中的数据只进行追加,旧数据不会更新。明细模型适用于需要存储全量原始数据的场景:如日志存储、用户行为数据、交易数据。
1.1.1 建表
在建表时,可以通过 DUPLICATE KEY 关键字指定明细模型。明细表必须指定数据的 Key 列,用于在存储时对数据进行排序。下例的明细表中存储了日志信息,并针对于 log_time、log_type 及 error_code 三列进行了排序:
CREATE TABLE IF NOT EXISTS example_tbl_duplicate
(log_time DATETIME NOT NULL,log_type INT NOT NULL,error_code INT,error_msg VARCHAR(1024),op_id BIGINT,op_time DATETIME
)
ENGINE = OLAP --指定使用 Doris 的 OLAP 引擎,支持高效数据分析
==DUPLICATE KEY(log_time, log_type, error_code) --log_time, log_type, error_code作为排序键,优化按时间和类型的查询
DISTRIBUTED BY HASH(log_type) BUCKETS 10 --按log_type字段进行哈希分桶、创建 10 个数据桶
PROPERTIES ("replication_num" = "1" -- 将副本数设置为1,与可用后端节点数匹配
);
1.1.2 数据插入与存储
-- 4 rows raw data
INSERT INTO example_tbl_duplicate VALUES
('2024-11-01 00:00:00', 2, 2, 'timeout', 12, '2024-11-01 01:00:00'),
('2024-11-02 00:00:00', 1, 2, 'success', 13, '2024-11-02 01:00:00'),
('2024-11-03 00:00:00', 2, 2, 'unknown', 13, '2024-11-03 01:00:00'),
('2024-11-04 00:00:00', 2, 2, 'unknown', 12, '2024-11-04 01:00:00');-- insert into 2 rows
INSERT INTO example_tbl_duplicate VALUES
('2024-11-01 00:00:00', 2, 2, 'timeout', 12, '2024-11-01 01:00:00'),
('2024-11-01 00:00:00', 2, 2, 'unknown', 13, '2024-11-01 01:00:00');-- check the rows of table
SELECT * FROM example_tbl_duplicate;
- 结果如下:
明细模型默认存储排序
ORDER BY 显式排序
- 不同于直接
SELECT * FROM example_tbl_duplicate ORDER BY log_time, log_type, error_code;
默认查询是 “底层存储顺序的直接呈现”(受分桶、写入等影响),ORDER BY 是 “结果集的强制重排” 。
1.2 主键模型(Unique Key Model)
需要更新数据时,可以选择主键模型(Unique Key Model)。该模型保证 Key 列的唯一性,插入或更新数据时,新数据会覆盖具有相同 Key 的旧数据,确保数据记录为最新。与其他数据模型相比,主键模型适用于数据的更新场景,在插入过程中进行主键级别的更新覆盖。如高频数据更新、数据高效去重以及需要部分列更新的场景。
1.3 实现方式
1.3.1 写时合并(默认)
数据在写入时立即合并相同 Key 的记录,确保存储的始终是最新数据。写时合并兼顾查询和写入性能,避免多个版本的数据合并,并支持谓词下推到存储层。大多数场景推荐使用此模式;
CREATE TABLE IF NOT EXISTS example_tbl_unique
(user_id LARGEINT NOT NULL,user_name VARCHAR(50) NOT NULL,city VARCHAR(20),age SMALLINT,sex TINYINT
)
UNIQUE KEY(user_id, user_name)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES ("enable_unique_key_merge_on_write" = "true", --指定写时合并模式"replication_num" = "1"
);
1.3.2 读时合并(老版本)
在 1.2 版本前,Doris 中的主键模型默认使用读时合并模式,数据在写入时并不进行合并,以增量的方式被追加存储,在 Doris 内保留多个版本。查询或 Compaction 时,会对数据进行相同 Key 的版本合并。读时合并适合写多读少的场景,在查询是需要进行多个版本合并,谓词无法下推,可能会影响到查询速度。
CREATE TABLE IF NOT EXISTS example_tbl_unique_read
(user_id LARGEINT NOT NULL,user_name VARCHAR(50) NOT NULL,city VARCHAR(20),age SMALLINT,sex TINYINT
)
UNIQUE KEY(user_id, user_name)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES ("enable_unique_key_merge_on_write" = "true", --指定写时合并模式"replication_num" = "1"
);
1.3.3 数据插入与存储
INSERT INTO example_tbl_unique_write VALUES
(101, 'Tom', 'BJ', 26, 1),
(101,'cat,'BJ', 26, 1),
(102, 'Jason', 'BJ', 27, 1),
(103, 'Juice', 'SH', 20, 2),
(104, 'Olivia', 'SZ', 22, 2);-- insert into data to update by key
INSERT INTO example_tbl_unique_write VALUES
(101, 'Tom', 'BJ', 27, 1),
(102, 'Jason', 'SH', 28, 1);-- check updated data
SELECT * FROM example_tbl_unique_write;
- 结果如下:
1.4 聚合模型(Aggregate Key Model)
Doris 的聚合模型专为高效处理大规模数据查询中的聚合操作设计。它通过预聚合数据,减少重复计算,提升查询性能。聚合模型只存储聚合后的数据,节省存储空间并加速查询。用于对明细数据进行汇总以及不需要查询原始明细数据。
1.4.1 原理
每一次数据导入会在聚合模型内形成一个版本,在 Compaction 阶段进行版本合并,在查询时会按照主键进行数据聚合:
- 数据导入阶段:数据按批次导入,每批次生成一个版本,并对相同聚合键的数据进行初步聚合(如求和、计数);
- 后台文件合并阶段(Compaction):多个版本文件会定期合并,减少冗余并优化存储;
- 查询阶段:查询时,系统会聚合同一聚合键的数据,确保查询结果准确。
1.4.2 建表
CREATE TABLE IF NOT EXISTS example_tbl_agg
(user_id LARGEINT NOT NULL,load_dt DATE NOT NULL,city VARCHAR(20),last_visit_dt DATETIME REPLACE DEFAULT "1970-01-01 00:00:00",cost BIGINT SUM DEFAULT "0",max_dwell INT MAX DEFAULT "0",
)
AGGREGATE KEY(user_id, load_dt, city)
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
PROPERTIES ("replication_num" = "1"
);
1.4.3 聚合函数
聚合方式 | 描述 |
---|---|
SUM | 求和,多行的 Value 进行累加。 |
REPLACE | 替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。 |
MAX | 保留最大值。 |
MIN | 保留最小值。 |
REPLACE_IF_NOT_NULL | 非空值替换。与 REPLACE 的区别在于对 null 值,不做替换。 |
HLL_UNION HLL | 类型的列的聚合方式,通过 HyperLogLog 算法聚合。 |
BITMAP_UNION BITMAP | 类型的列的聚合方式,进行位图的并集聚合。 |
1.4.4 数据插入与存储
在聚合表中,数据基于主键进行聚合操作。数据插入后及完成聚合操作。
-- 4 rows raw data
INSERT INTO example_tbl_agg VALUES
(101, '2024-11-01', 'BJ', '2024-10-29', 10, 20),
(102, '2024-10-30', 'BJ', '2024-10-29', 20, 20),
(101, '2024-10-30', 'BJ', '2024-10-28', 5, 40),
(101, '2024-10-30', 'SH', '2024-10-29', 10, 20);-- insert into 2 rows
INSERT INTO example_tbl_agg VALUES
(101, '2024-11-01', 'BJ', '2024-10-30', 20, 10),
(102, '2024-11-01', 'BJ', '2024-10-30', 10, 30);-- check the rows of table
SELECT * FROM example_tbl_agg;
+---------+------------+------+---------------------+------+----------------+
| user_id | load_date | city | last_visit_date | cost | max_dwell_time |
+---------+------------+------+---------------------+------+----------------+
| 102 | 2024-10-30 | BJ | 2024-10-29 00:00:00 | 20 | 20 |
| 102 | 2024-11-01 | BJ | 2024-10-30 00:00:00 | 10 | 30 |
| 101 | 2024-10-30 | BJ | 2024-10-28 00:00:00 | 5 | 40 |
| 101 | 2024-10-30 | SH | 2024-10-29 00:00:00 | 10 | 20 |
| 101 | 2024-11-01 | BJ | 2024-10-30 00:00:00 | 30 | 20 |
+---------+------------+------+---------------------+------+----------------+
1.5注意
- 建表时列类型建议
- Key 列必须在所有 Value 列之前。
- 尽量选择整型类型。因为整型类型的计算和查找效率远高于字符串。
- 对于不同长度的整型类型的选择原则,遵循够用即可。
- 对于 VARCHAR 和 STRING 类型的长度,遵循够用即可。
1.6自增列
要使用自增列,需要在建表CREATE-TABLE
时为对应的列添加AUTO_INCREMENT属性。若要手动指定自增列起始值,可以通过建表时AUTO_INCREMENT(start_value)语句指定,如果未指定,则默认起始值为 1。
CREATE TABLE `demo`.`tbl` (`id` BIGINT NOT NULL AUTO_INCREMENT,`value` BIGINT NOT NULL
) ENGINE=OLAP
DUPLICATE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
2. 数据类型 - Apache Doris
- 数值类型
BOOLEAN
TINYINT
SMALLINT
INT
BIGINT
LARGEINT
FLOAT
DOUBLE
DECIMAL - 日期类型
DATE
DATETIME - 字符串类型
CHAR
VARCHAR
STRING - 半结构类型
ARRAY
MAP
STRUCT
JSON
VARIANT - 聚合类型
HLL
BITMAP
QUANTILE_STATE
AGG_STATE - IP类型
IPv4
IPv6
具体内容可观看doc文档
3. 数据压缩
Doris 采用 列式存储 模型来组织和存储数据,这种存储模型特别适合分析型负载,能够显著提高查询效率。在列式存储中,表的每一列会独立存储,这为压缩技术的应用提供了便利,从而提高了存储效率。Doris 提供多种压缩算法,用户可以根据工作负载的需求,选择合适的压缩方式来优化存储和查询性能。
- 选择合适的压缩算法需根据工作负载特性:
- 对于 高性能实时分析 场景,推荐使用 LZ4 或 Snappy。
- 对于 存储效率优先 的场景,推荐使用 ZSTD 或 Zlib。
- 对于需要兼顾速度和压缩率的场景,可选择 LZ4F。
- 对于 归档或冷数据存储 场景,建议使用 Zlib 或 LZ4HC。
CREATE TABLE example_table (id INT,name STRING,age INT
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES ("compression" = "zstd", --使用zstd压缩算法"replication_num" = "1"
);
4. 变更表结构(Schema)
用户可以通过Alter Table
操作来修改 Doris 表的 Schema。Schema 变更主要涉及列的修改和索引的变化。
ALTER TABLE [database.]table alter_clause;
特性 | 轻量级 Schema Change | 重量级 Schema Change |
---|---|---|
执行速度 | 秒级(几乎实时) | 分钟级、小时级、天级(依赖表的数据量,数据量越大,执行越慢) |
是否需要数据重写 | 不需要 | 需要,涉及数据文件的重写 |
系统性能影响 | 影响较小 | 可能影响系统性能,尤其是在数据转换过程中 |
资源消耗 | 较低 | 较高,会占用计算资源重新组织数据,过程中涉及到的表的数据占用的存储空间翻倍。 |
操作类型 | 增加、删除 Value 列,修改列名,修改 VARCHAR 长度 | 修改列的数据类型、更改主键、修改列的顺序等 |
- 修改列名称
ALTER TABLE [database.]table RENAME COLUMN old_column_name new_column_name;
- 添加一列
- 聚合模型如果增加 Value 列,需要指定 agg_type。
首先建表
-- 1.建表
CREATE TABLE IF NOT EXISTS schema_my_table(col1 int,col2 int,col3 int,col4 int SUM,col5 varchar(32) REPLACE DEFAULT "abc"
) AGGREGATE KEY(col1, col2, col3)
DISTRIBUTED BY HASH(col1) BUCKETS 10; #分桶算法:Hash 分桶语法、Random 分桶语法
PROPERTIES ("replication_num" = "1"
);
第二步:向schema_my_table
的 col1 后添加一个 Key 列 key_col
ALTER TABLE example_db.my_table ADD COLUMN key_col INT DEFAULT "0" AFTER col1;
第三步:向 schema_my_table
的 col4 后添加一个 Value 列 value_colSUM
聚合类型
ALTER TABLE example_db.my_table ADD COLUMN value_col INT SUM DEFAULT "0" AFTER col4;
- 非聚合模型(如 DUPLICATE KEY)如果增加 Key 列,需要指定 KEY 关键字。
-- 1.建表
CREATE TABLE IF NOT EXISTS test.schema_my_table(col1 int,col2 int,col3 int,col4 int,col5 int
) DUPLICATE KEY(col1, col2, col3)
DISTRIBUTED BY RANDOM BUCKETS 10
PROPERTIES ("replication_num" = "1"
);
第二步:向schema_my_table
的 col1 后添加一个 Key 列 key_co
l
ALTER TABLE schema_my_table ADD COLUMN key_col INT KEY DEFAULT "0" AFTER col1;
第三步:向 schema_my_table
的 col4 后添加一个 Value 列 value_col
ALTER TABLE schema_my_table ADD COLUMN value_col INT DEFAULT "0" AFTER col4;
3. 添加多列
- 聚合模型如果增加 Value 列,需要指定
agg_type
ALTER TABLE example_db.my_table ADD COLUMN (c1 INT DEFAULT "1", c2 FLOAT SUM DEFAULT "0");
- 聚合模型如果增加 Key 列,需要指定 KEY 关键字
- 删除列
ALTER TABLE example_db.my_table DROP COLUMN col4;
- 修改列类型
ALTER TABLE example_db.my_table MODIFY COLUMN col1 BIGINT KEY DEFAULT "1" AFTER col2;
- 重新排序
ALTER TABLE example_db.my_table ORDER BY (k3,k1,k2,k4,v2,v1);
五、测试
5.1 Star Schema Benchmark
Star Schema Benchmark(SSB) 是一个轻量级的数仓场景下的性能测试集。SSB 基于 TPC-H 提供了一个简化版的星型模型数据集,主要用于测试在星型模型下,多表关联查询的性能表现。另外,业界内通常也会将 SSB 打平为宽表模型(以下简称:SSB flat),来测试查询引擎的性能。
# 执行以下脚本下载并编译 ssb-tools 工具。
./bin/build-ssb-dbgen.sh# 生成 SSB 测试集
./bin/gen-ssb-data.sh -s 100 # 生成 100GB 数据量的表结构# 建表
./bin/create-ssb-tables.sh -s 100# SSB 测试集所有数据导入及 SSB FLAT 宽表数据合成并导入到表里。
./bin/load-ssb-data.sh# 执行SSB查询
./bin/run-ssb-queries.sh#或者要执行宽表查询 flat queries
./bin/run-ssb-flat-queries.sh
普通查询结果
宽表查询结果