一、基本概念

在 Doris 中,数据以表(Table)的形式进行逻辑上的描述。 一张表包括行(Row)和列(Column)。Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。

Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。Doris 的 key 列是建表语句中指定的列,建表语句中的关键字’unique key’或’aggregate key’或’duplicate key’后面的列就是 Key 列,除了 Key 列剩下的就是 Value 列。

Doris 的数据模型主要分为 3 类:

  • Aggregate
  • Unique
  • Duplicate

下面我们分别介绍。

可以在官网看到对应的文章,此处为个人理解,仅做笔记,官网传送门

二、Aggregate 模型(聚合模型)

一、案例1(无唯一字段)

1、表结构

ColumnNameTypeAggregationTypeComment
user_idLARGEINT用户 id
dateDATE数据灌入日期
cityVARCHAR(20)用户所在城市
ageSMALLINT用户年龄
sexTINYINT用户性别
last_visit_dateDATETIMEREPLACE用户最后一次访问时间
costBIGINTSUM用户总消费
max_dwell_timeINTMAX用户最大停留时间
min_dwell_timeINTMIN用户最小停留时间

2、建表语句

CREATE DATABASE IF NOT EXISTS example_db;

CREATE TABLE IF NOT EXISTS example_db.example_tbl_agg1
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
)
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

其中AGGREGATE KEY(user_id, date, city, age, sex)为聚合字段,可以看出聚合键为用户ID、日期、城市、年龄、性别,也就是如果这五个字段相同,那么就会进行聚合操作。

3、原始数据

序号user_iddatecityagesexlast_visit_datecostmax_dwell_timemin_dwell_time
1100002017-10-01北京2002017-10-01 06:00:00201010
2100002017-10-01北京2002017-10-01 07:00:001522
3100012017-10-01北京3012017-10-01 17:05:4522222
4100022017-10-02上海2012017-10-02 12:59:1220055
5100032017-10-02广州3202017-10-02 11:20:00301111
6100042017-10-01深圳3502017-10-01 10:00:1510033
7100042017-10-03深圳3502017-10-03 10:20:221166

4、结果分析

user_iddatecityagesexlast_visit_datecostmax_dwell_timemin_dwell_time
100002017-10-01北京2002017-10-01 07:00:0035102
100012017-10-01北京3012017-10-01 17:05:4522222
100022017-10-02上海2012017-10-02 12:59:1220055
100032017-10-02广州3202017-10-02 11:20:00301111
100042017-10-01深圳3502017-10-01 10:00:1510033
100042017-10-03深圳3502017-10-03 10:20:221166

第一行数据和第二行数据的ID、日期、城市、年龄、性别完全一致,因此这两行数据会进行聚合。
第六行和第七行数据,因为日期不一致,所以不会进行聚合。

二、案例2(有唯一字段)

1、表结构

ColumnNameTypeAggregationTypeComment
user_idLARGEINT用户 id
dateDATE数据灌入日期
timestampDATETIME数据灌入时间,精确到秒
cityVARCHAR(20)用户所在城市
ageSMALLINT用户年龄
sexTINYINT用户性别
last_visit_dateDATETIMEREPLACE用户最后一次访问时间
costBIGINTSUM用户总消费
max_dwell_timeINTMAX用户最大停留时间
min_dwell_timeINTMIN用户最小停留时间

2、建表语句

CREATE TABLE IF NOT EXISTS example_db.example_tbl_agg2
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `timestamp` DATETIME NOT NULL COMMENT "数据灌入日期时间戳",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
)
AGGREGATE KEY(`user_id`, `date`, `timestamp` ,`city`, `age`, `sex`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

其中AGGREGATE KEY(user_id, date, timestamp ,city, age, sex)为聚合字段,可以看出聚合键为用户ID、日期、交易时间(时分秒)、城市、年龄、性别,也就是如果这五个字段相同,那么就会进行聚合操作。

3、原始数据

序号user_iddatetimestampcityagesexlast_visit_datecostmax_dwell_timemin_dwell_time
1100002017-10-012017-10-01 08:00:05北京2002017-10-01 06:00:00201010
2100002017-10-012017-10-01 09:00:05北京2002017-10-01 07:00:001522
3100012017-10-012017-10-01 18:12:10北京3012017-10-01 17:05:4522222
4100022017-10-022017-10-02 13:10:00上海2012017-10-02 12:59:1220055
5100032017-10-022017-10-02 13:15:00广州3202017-10-02 11:20:00301111
6100042017-10-012017-10-01 12:12:48深圳3502017-10-01 10:00:1510033
7100042017-10-032017-10-03 12:38:20深圳3502017-10-03 10:20:221166

4、结果分析

user_iddatetimestampcityagesexlast_visit_datecostmax_dwell_timemin_dwell_time
100002017-10-012017-10-01 08:00:05北京2002017-10-01 06:00:00201010
100002017-10-012017-10-01 09:00:05北京2002017-10-01 07:00:001522
100012017-10-012017-10-01 18:12:10北京3012017-10-01 17:05:4522222
100022017-10-022017-10-02 13:10:00上海2012017-10-02 12:59:1220055
100032017-10-022017-10-02 13:15:00广州3202017-10-02 11:20:00301111
100042017-10-012017-10-01 12:12:48深圳3502017-10-01 10:00:1510033
100042017-10-032017-10-03 12:38:20深圳3502017-10-03 10:20:221166

我们可以看到,存储的数据,和导入数据完全一样,没有发生任何聚合。这是因为,这批数据中,因为加入了 timestamp 列,所有行的 Key 都不完全相同。也就是说,只要保证导入的数据中,每一行的 Key 都不完全相同,那么即使在聚合模型下,Doris 也可以保存完整的明细数据。

三、Unique 模型(唯一模型)

当用户有数据更新需求时,可以选择使用 Unique 数据模型。Unique 模型能够保证 Key 的唯一性,当用户更新一条数据时,新写入的数据会覆盖具有相同 key 的旧数据。

两种实现方式

Unique 模型提供了两种实现方式:

读时合并 (merge-on-read)。在读时合并实现中,用户在进行数据写入时不会触发任何数据去重相关的操作,所有数据去重的操作都在查询或者 compaction 时进行。因此,读时合并的写入性能较好,查询性能较差,同时内存消耗也较高。
写时合并 (merge-on-write)。在 1.2 版本中,我们引入了写时合并实现,该实现会在数据写入阶段完成所有数据去重的工作,因此能够提供非常好的查询性能。
自 2.0 版本起,写时合并已经非常成熟稳定,由于其优秀的查询性能,我们推荐大部分用户选择该实现。自 2.1 版本其,写时合并成为 Unique 模型的默认实现 关于两种实现方式的详细区别,用户可以本章节后续内容的介绍。关于两种实现方式的性能差异,参考后续章节聚合模型的局限性的描述。

数据更新的语意

  • Unique 模型默认的更新语意为整行UPSERT,即 UPDATE OR INSERT,该行数据的 key
    如果存在,则进行更新,如果不存在,则进行新数据插入。在整行UPSERT语意下,即使用户使用 insert into
    指定部分列进行写入,Doris 也会在 Planner 中将未提供的列使用 NULL 值或者默认值进行填充
  • 部分列更新。如果用户希望更新部分字段,需要使用写时合并实现,并通过特定的参数来开启部分列更新的支持。请查阅文档部分列更新获取相关使用建议

1、读时合并(与聚合模型相同的实现方式)

ColumnNameTypeIsKeyComment
user_idBIGINTYes用户 id
usernameVARCHAR(50)Yes用户昵称
cityVARCHAR(20)No用户所在城市
ageSMALLINTNo用户年龄
sexTINYINTNo用户性别
phoneLARGEINTNo用户电话
addressVARCHAR(500)No用户住址
register_timeDATETIMENo用户注册时间

2、建表语句

ColumnNameTypeAggregationTypeComment
user_idBIGINT用户 id
usernameVARCHAR(50)用户昵称
cityVARCHAR(20)REPLACE用户所在城市
ageSMALLINTREPLACE用户年龄
sexTINYINTREPLACE用户性别
phoneLARGEINTREPLACE用户电话
addressVARCHAR(500)REPLACE用户住址
register_timeDATETIMEREPLACE用户注册时间

如果ID和姓名不一致,那么INSERT,如果一致,那么UPDATE

3、建表语句

CREATE TABLE IF NOT EXISTS example_db.example_tbl_unique
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `phone` LARGEINT COMMENT "用户电话",
    `address` VARCHAR(500) COMMENT "用户地址",
    `register_time` DATETIME COMMENT "用户注册时间"
)
UNIQUE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

这里的主键为 user_id + username

四、Duplicate 模型(没有主管也没有聚合需求)

在某些多维分析场景下,数据既没有主键,也没有聚合需求。因此,我们引入 Duplicate 数据模型来满足这类需求。举例说明。

ColumnNameTypeSortKeyComment
timestampDATETIMEYes日志时间
typeINTYes日志类型
error_codeINTYes错误码
error_msgVARCHAR(1024)No错误详细信息
op_idBIGINTNo负责人 id
op_timeDATETIMENo处理时间
CREATE TABLE IF NOT EXISTS example_db.example_tbl_duplicate
(
    `timestamp` DATETIME NOT NULL COMMENT "日志时间",
    `type` INT NOT NULL COMMENT "日志类型",
    `error_code` INT COMMENT "错误码",
    `error_msg` VARCHAR(1024) COMMENT "错误详细信息",
    `op_id` BIGINT COMMENT "负责人id",
    `op_time` DATETIME COMMENT "处理时间"
)
DUPLICATE KEY(`timestamp`, `type`, `error_code`)
DISTRIBUTED BY HASH(`type`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

这种数据模型区别于 Aggregate 和 Unique 模型。数据完全按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留。 而在建表语句中指定的 DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序。(更贴切的名称应该为“Sorted Column”, 这里取名“DUPLICATE KEY”只是用以明确表示所用的数据模型。关于“Sorted Column”的更多解释,可以参阅前缀索引)。在 DUPLICATE KEY 的选择上,我们建议适当的选择前 2-4 列就可以。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐