本文主要从逻辑层面,描述Doris的数据模型,以帮助用户更好的使用 Doris 应对不同的业务场景。
在 Doris 中,数据以表(Table)的形式进行逻辑上的描述。 一张表包括行(Row)和列(Column)。Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。
Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列
Doris的数据模型主要分为3类:
下面我们分别介绍
ColumnName | Type | AggregationType | Comment |
---|---|---|---|
user_id | LARGEINT | 用户id | |
date | DATE | 数据灌入日期 | |
city | VARCHAR(20) | 用户所在城市 | |
age | SMALLINT | 用户年龄 | |
sex | TINYINT | 用户性别 | |
last_visit_date | DATETIME | REPLACE | 用户最后一次访问时间 |
cost | BIGINT | SUM | 用户总消费 |
max_dwell_time | INT | MAX | 用户最大停留时间 |
min_dwell_time | INT | MIN | 用户最小停留时间 |
转换为SQL语句如下:
CREATE TABLE IF NOT EXISTS example_db.example_tbl
(
`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"
);
当我们插入两次这条数据:
INSERT INTO example_tbl(user_id,DATE,city,age,sex,last_visit_date,cost,max_dwell_time,min_dwell_time)
VALUES(10004,'2017/10/03','深圳',35,0,'2019/10/03 10:00:00',20,9,9)
得到的结果是这样的:
可以看出cost字段对两条数据进行了求和。而求和的依据是根据相同的key来的。
在某些多维分析场景下,用户更关注的是如何保证 Key 的唯一性,即如何获得 Primary Key 唯一性约束。因此,我们引入了Unique的数据模型。该模型本质上是聚合模型的一个特例,也是一种简化的表结构表示方式。
也举例说明,我们新建一个用户基础信息表。这类数据没有聚合需求,只需保证主键唯一性。
建表语句如下:
CREATE TABLE IF NOT EXISTS example_tb2
(
`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"
);
这个模型名字叫Unique模型,但跟Mysql中的唯一约束不同。Mysql中同一个key多次插入会报错,而这个模型同一个key插入,后面插入的总是会覆盖之前的。相当于聚合模型(Aggregate)的字段都是REPLACE类型。
在某些多维分析场景下,数据既没有主键,也没有聚合需求。因此,我们引入 Duplicate 数据模型来满足这类需求。举例说明:
CREATE TABLE IF NOT EXISTS example_tb3
(
`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"
);
此种模式下,插入两条完全一样的数据会保留2条数据。而在建表语句中指定的 DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序
INSERT INTO example_tb3 (TIMESTAMP,TYPE,error_code,error_msg,op_id,op_time) VALUES('2022-12-04 00:00:00',1,1,'重复数据',3,'2022-12-05 00:00:00');
在聚合模型中,模型对外展现的就是最终聚合后的数据。也就是说任何还未聚合的数据必须通过某种方式来保证对外展示的一致性。
ColumnName | Type | AggregationType | Comment |
---|---|---|---|
user_id | LARGEINT | 用户id | |
date | DATE | 数据灌入日期 | |
cost | BIGINT | SUM | 用户总消费 |
假设导入两批数据:
我们再去查询,查询到的结果一定是聚合以后的,如user10001 date:2017-11-20这个数据的cost应该是51。这是doris在查询引擎中加入了聚合算子,来保证数据对外的一致性。
这种一致性的保证,就让某些查询效率很低。如:
SELECT COUNT(*) FROM table;
如果是mysql这种,它只需要对扫描某一列数据就能获得count值,但是在Doris的聚合模型中,开销就很大。他如果只扫描user_id列的话,不加查询聚合结果为5,加上聚合结果为3,都是不对的,正确结果应该为4.因为它要想正确的结果必须同时取user_id、date这两列数据,再加上查询时聚合,才能返回正确的结果。
因此,当业务上有频繁的 count(* )查询时,我们建议用户通过增加一个值恒为 1 的,聚合类型为 REPLACE的列来模拟 count(*)
ColumnName | Type | AggregationType | Comment |
---|---|---|---|
user_id | LARGEINT | 用户id | |
date | DATE | 数据灌入日期 | |
cost | BIGINT | SUM | 用户总消费 |
count | BIGINT | REPLACE | 用于计算count |
这样的话,通过这个语句就能得到之前count(*)的结果
select sum(count) from table;
数据模型在建表时就已经确定,且无法修改。所以,选择一个合适的数据模型非常重要。
书山有路勤为径,学海无涯苦作舟
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.cnblogs.com/javammc/p/16961862.html
内容来源于网络,如有侵权,请联系作者删除!