Doris -- 基本概念和数据表模型

Posted _泡泡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Doris -- 基本概念和数据表模型相关的知识,希望对你有一定的参考价值。

字段类型

数据类型 字节 范围
TINYINT 1 字节 -2^7 + 1 ~ 2^7 - 1
SMALLINT 2 字节 -2^15 + 1 ~ 2^15 - 1
INT 4 字节 -2^31 + 1 ~ 2^31 - 1
BIGINT 8 字节 -2^63 + 1 ~ 2^63 - 1
LARGEINT 16 字节 -2^127 + 1 ~ 2^127 - 1
FLOAT 4 字节 支持科学计数法
DOUBLE 12 字节 支持科学计数法
DECIMAL[(precision, scale)] 16 字节 保证精度的小数类型。默认是DECIMAL(10, 0) ,precision: 1 ~ 27 ,scale: 0 ~ 9,其中整数部分为 1 ~ 18,不支持科学计数法
DATE 3 字节 0000-01-01 ~ 9999-12-31
DATETIME 8 字节 0000-01-01 00:00:00 ~ 9999-12-31 23:59:59
CHAR[(length)] 定长字符串。长度范围:1 ~ 255。默认为 1
VARCHAR[(length)] 变长字符串。长度范围:1 ~ 65533
BOOLEAN 与 TINYINT 一样,0 代表 false,1 代表 true
HLL 1~16385 个字节 hll 列类型,不需要指定长度和默认值,长度根据数据的聚合程度系统内控制,并且 HLL 列只能通过 配套的hll_union_agg、Hll_cardinality、hll_hash 进行查询或使用
BITMAP bitmap 列类型,不需要指定长度和默认值。表示整型的集合,元素最大支持到 2^64 - 1
STRING 变长字符串,0.15 版本支持,最大支持 2147483643 字节(2GB-4),长度还受 be 配置string_type_soft_limit, 实际能存储的最大长度取两者最小值。只能用在 value 列,不能用在 key列和分区、分桶列

表的基本概念

行和列

一张表包括行(Row)和列(Column);
Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。
doris中的列分为两类:key列和value列
key列在doris中有两种作用:
聚合表模型中,key是聚合和排序的依据
其他表模型中,key是排序依据

数据表模型

Doris 的数据模型主要分为3类:
• Aggregate 聚合模型
• Unique 唯一模型
• Duplicate 明细模型

Aggregate 模型

相同key的数据进行自动聚合的表模型。表中的列按照是否设置了 AggregationType,分为 Key(维度列)和 Value(指标列),没有设置AggregationType 的称为 Key,设置了 AggregationType 的称为 Value。当我们导入数据时,对于 Key 列相同的行会聚合成一行,而 Value 列会按照设置的AggregationType 进行聚合。AggregationType 目前有以下五种聚合方式:

  1. SUM:求和,多行的 Value 进行累加。
  2. REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
  3. REPLACE_IF_NOT_NULL :当遇到 null 值则不更新。
  4. MAX:保留最大值。
  5. MIN:保留最小值。

有如下场景:需要创建一个表,来记录公司每个用户的每一次消费行为信息,有如下字段:

而且,公司对这份数据,特别关心一个报表
每一个用户最后一次访问我们页面的时间,用户消费的总金额,用户停留在我们页面上的最大最小时长

每次要看这个报表,都需要在“明细表”上运行一个统计sql

Select
    user_id,data,city,age,gender,
    max(visit_data) as last_visit_data,
    sum(cost) as cost,
    max(dwell_time) as max_dwell_time,
    min(dwell_time) as min_dwell_time
From  t
Group by  user_id,data,city,age,gender  -- 对应的是聚合模型型key

聚合模型

-- 这是一个用户消费和行为记录的数据表
CREATE TABLE IF NOT EXISTS test.ex_user
(
 `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 "用户最小停留时间" 
 )
ENGINE=olap
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
-- 分桶 创建表时必须指定分桶 分桶的概念下面说
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1;

-- 插入数据
insert into test.ex_user values\\
(10000,\'2017-10-01\',\'北京\',20,0,\'2017-10-01 06:00:00\',20,10,10),\\
(10000,\'2017-10-01\',\'北京\',20,0,\'2017-10-01 07:00:00\',15,2,2),\\
(10001,\'2017-10-01\',\'北京\',30,1,\'2017-10-01 17:05:45\',2,22,22),\\
(10002,\'2017-10-02\',\'上海\',20,1,\'2017-10-02 12:59:12\',200,5,5),\\
(10003,\'2017-10-02\',\'广州\',32,0,\'2017-10-02 11:20:00\',30,11,11),\\
(10004,\'2017-10-01\',\'深圳\',35,0,\'2017-10-01 10:00:15\',100,3,3),\\
(10004,\'2017-10-03\',\'深圳\',35,0,\'2017-10-03 10:20:22\',11,6,6);

查看数据的时候发现,数据只剩下6条了,就是因为再key相同的时候,将后面的结果聚合了

UNIQUE 模型

相同key的数据进行自动去重的表模型。在某些多维分析场景下,用户更关注的是如何保证 Key 的唯一性,即如何获得 Primary Key 唯一性约束。因此,引入了 Uniq 的数据模型。该模型本质上是聚合模型的一个特例,也是一种简化的表结构表示方式。

-- 创建表
drop table if exists test.user;
CREATE TABLE IF NOT EXISTS test.user
(
-- key列
 `user_id` LARGEINT NOT NULL COMMENT "用户 id",
 `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
 -- value列
 `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;

-- 插入数据
insert into test.user values\\
(10000,\'zss\',\'北京\',18,0,12345678910,\'北京朝阳区 \',\'2017-10-01 07:00:00\'),\\
(10000,\'zss\',\'北京\',19,0,12345678910,\'北京顺义区 \',\'2018-10-01 07:00:00\'),\\
(10000,\'lss\',\'北京\',20,0,12345678910,\'北京海淀区\',\'2017-11-15 06:10:20\');


insert into test.user1 values\\
(10000,\'zss\',\'北京\',18,0,12345678910,\'北京朝阳区 \',\'2017-10-01 07:00:00\'),\\
(10000,\'zss\',\'北京\',19,0,12345678910,\'北京顺义区 \',\'2018-10-01 07:00:00\'),\\
(10000,\'lss\',\'北京\',20,0,12345678910,\'北京海淀区\',\'2017-11-15 06:10:20\');

查询结果后发现,相同的数据就会被替换掉

Duplicate 模型

就是存明细数据的表模型,既不做聚合也不做去重。在某些多维分析场景下,数据既没有主键,也没有聚合需求。Duplicate 数据模型可以满足这类需求。数据完全按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留。 而在建表语句中指定的 DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序。

-- 创建表
CREATE TABLE IF NOT EXISTS test.log_detail
(
 `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`) -- key用作排序使用
DISTRIBUTED BY HASH(`timestamp`) BUCKETS 1;

-- 插入数据
insert into test.log_detail values\\
(\'2017-10-01 08:00:05\',1,404,\'not found page\', 101, \'2017-10-01 08:00:05\'),\\
(\'2017-10-01 08:00:05\',1,404,\'not found page\', 101, \'2017-10-01 08:00:05\'),\\
(\'2017-10-01 08:00:05\',2,404,\'not found page\', 101, \'2017-10-01 08:00:06\'),\\
(\'2017-10-01 08:00:06\',2,404,\'not found page\', 101, \'2017-10-01 08:00:07\');

数据模型的选择

数据模型在建表时就已经确定,且无法修改;所以,选择一个合适的数据模型非常重要。

  • Aggregate 模型可以通过预聚合,极大地降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表类查询场景。

  • Uniq 模型针对需要唯一主键约束的场景,可以保证主键唯一性约束。但是无法利用 ROLLUP 等预聚合带来的查询优势(因为本质是 REPLACE,没有 SUM 这种聚合方式)。

  • Duplicate 适合任意维度的查询。虽然同样无法利用预聚合的特性,但是不受聚合模型的约束,可以发挥列存模型的优势(只读取相关列,而不需要读取所有 Key 列)

分区与分桶

  • partition(分区):是在逻辑上将一张表按行(横向)划分
  • tablet(又叫bucket,分桶):在物理上对一个分区再按行(横向)划分

分区(Partition)

• Partition 列可以指定一列或多列,在聚合模型中,分区列必须为 KEY 列。
• 不论分区列是什么类型,在写分区值时,都需要加双引号。
• 分区数量理论上没有上限。
• 当不使用 Partition 建表时,系统会自动生成一个和表名同名的,全值范围的 Partition。该 Partition 对用户不可见,并且不可删改。
• 创建分区时不可添加范围重叠的分区。

Range 分区

单列分区

drop table if exists test.expamle_range_tbl;
CREATE TABLE IF NOT EXISTS test.expamle_range_tbl
(
    `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 "用户性别"
)
ENGINE=OLAP
DUPLICATE KEY(`user_id`, `date`) -- 表模型
-- 分区的语法
PARTITION BY RANGE(`date`) -- 指定分区类型和分区列
(
    -- 指定分区名称,分区的上界   前闭后开
    PARTITION `p201701` VALUES LESS THAN ("2017-02-01"), 
    PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
    PARTITION `p201703` VALUES LESS THAN ("2017-04-01")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1;

• 分区列通常为时间列,以方便的管理新旧数据。
• Partition 支持通过 VALUES LESS THAN (...) 仅指定上界,系统会将前一个分区的上界作为该分区的下界,生成一个左闭右开的区间。同时,也支持通过 VALUES[...) 指定上下界,生成一个左闭右开的区间。
• 通过 VALUES ...) 同时指定上下界比较容易理解。这里举例说明,当使用 VALUES LESS THAN (...) 语句进行分区的增删操作时,分区范围的变化情况:
如上 expamle_range_tbl 得建表语句中可以看到,当建表完成后,会自动生成如下3个分区:

-- 查看表中分区得情况
SHOW PARTITIONS FROM test.expamle_range_tbl \\G;

-- 增加一个分区
ALTER TABLE test.expamle_range_tbl ADD PARTITION p201705 VALUES LESS THAN ("2017-06-01");

--删除分区
ALTER TABLE test.expamle_range_tbl DROP PARTITION p201703;

-- 此时的分区状态如下:
-- p201701: [MIN_VALUE,  2017-02-01)
-- p201702: [2017-02-01, 2017-03-01)
-- p201705: [2017-04-01, 2017-06-01)
-- 出现了一个空洞:[2017-03-01, 2017-04-01)
-- 如果导入的数据范围在这个空洞范围内,是无法导入的

多列分区

PARTITION BY RANGE(`date`, `id`)     前闭后开
(
    PARTITION `p201701_1000` VALUES LESS THAN ("2017-02-01", "1000"),
    PARTITION `p201702_2000` VALUES LESS THAN ("2017-03-01", "2000"),
    PARTITION `p201703_all`  VALUES LESS THAN ("2017-04-01")-- 默认采用id类型的最小值
)
-- 当用户插入数据时,分区列值会按照顺序依次比较,最终得到对应的分区

List分区

• 分区列支持 BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR 数据类型,分区值为枚举值。只有当数据为目标分区枚举值其中之一时,才可以命中分区。

• Partition 支持通过 VALUES IN (...) 来指定每个分区包含的枚举值。

-- 单列
CREATE TABLE IF NOT EXISTS test.expamle_list_tbl
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `timestamp` DATETIME NOT NULL COMMENT "数据灌入的时间戳",
    `city` VARCHAR(20) NOT NULL COMMENT "用户所在城市",
    `age` SMALLINT NOT NULL COMMENT "用户年龄",
    `sex` TINYINT NOT NULL 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 "用户最小停留时间"
)
ENGINE=olap
AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
PARTITION BY LIST(`city`)
(
    PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
    PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
    PARTITION `p_jp` VALUES IN ("Tokyo")
)
-- 指定分桶的语法
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES
(
    "replication_num" = "3"
);



-- 多列
PARTITION BY LIST(`id`, `city`)
(
    PARTITION `p1_city` VALUES IN (("1", "Beijing",), ("2", "Shanghai")),
    PARTITION `p2_city` VALUES IN (("2", "Beijing"), ("1", "Shanghai")),
    PARTITION `p3_city` VALUES IN (("3", "Beijing"), ("4", "Shanghai"))

)

分桶(Bucket)

  • 如果使用了 Partition,则 DISTRIBUTED ... 语句描述的是数据在各个分区内的划分规则。如果不使用 Partition,则描述的是对整个表的数据的划分规则。
  • 分桶列可以是多列,但必须为 Key 列。分桶列可以和 Partition 列相同或不同。
  • 分桶列的选择,是在 查询吞吐 和 查询并发 之间的一种权衡:
    • 如果选择多个分桶列,则数据分布更均匀。如果一个查询条件不包含所有分桶列的等值条件,那么该查询会触发所有分桶同时扫描,这样查询的吞吐会增加,单个查询的延迟随之降低。这个方式适合大吞吐低并发的查询场景。
    • 如果仅选择一个或少数分桶列,则对应的点查询可以仅触发一个分桶扫描。此时,当多个点查询并发时,这些查询有较大的概率分别触发不同的分桶扫描,各个查询之间的IO影响较小(尤其当不同桶分布在不同磁盘上时),所以这种方式适合高并发的点查询场景。
  • 分桶的数量理论上没有上限


关于 Partition 和 Bucket的数量和数据量的建议。

  1. 一个表的 Tablet 总数量等于 (Partition num * Bucket num)。
  2. 一个表的 Tablet 数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。
  3. 单个 Tablet 的数据量理论上没有上下界,但建议在 1G - 10G 的范围内。如果单个 Tablet 数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加 Schema Change 或者 Rollup 操作失败重试的代价(这些操作失败重试的粒度是 Tablet)。分桶应该控制桶内数据量 ,不易过大或者过小
  4. 当 Tablet 的数据量原则和数量原则冲突时,建议优先考虑数据量原则。
  5. 在建表时,每个分区的 Bucket 数量统一指定。但是在动态增加分区时(ADD PARTITION),可以单独指定新分区的 Bucket 数量。可以利用这个功能方便的应对数据缩小或膨胀。
  6. 一个 Partition 的 Bucket 数量一旦指定,不可更改。所以在确定 Bucket 数量时,需要预先考虑集群扩容的情况。比如当前只有 3 台 host,每台 host 有 1 块盘。如果 Bucket 的数量只设置为 3 或更小,那么后期即使再增加机器,也不能提高并发度。

注:表的数据量可以通过 SHOW DATA命令查看,结果除以副本数,即表的数据量。

复合分区与单分区的选择

复合分区
• 第一级称为 Partition,即分区。用户可以指定某一维度列作为分区列(当前只支持整型和时间类型的列),并指定每个分区的取值范围。
• 第二级称为 Distribution,即分桶。用户可以指定一个或多个维度列以及桶数对数据进行 HASH 分布。

以下场景推荐使用复合分区
• 有时间维度或类似带有有序值的维度,可以以这类维度列作为分区列。分区粒度可以根据导入频次、分区数据量等进行评估。地域、时间
• 历史数据删除需求:如有删除历史数据的需求(比如仅保留最近N 天的数据)。使用复合分区,可以通过删除历史分区来达到目的。也可以通过在指定分区内发送 DELETE 语句进行数据删除。
• 改善数据倾斜问题:每个分区可以单独指定分桶数量。如按天分区,当每天的数据量差异很大时,可以通过指定分区的分桶数,合理划分不同分区的数据,分桶列建议选择区分度大的列。
用户也可以不使用复合分区,即使用单分区。则数据只做 HASH 分布。

PROPERTIES

在建表语句的最后,可以用 PROPERTIES 关键字来设置一些表的属性参数(参数有很多)

PROPERTIES(
  "参数名" = "参数值"
)

分片副本数(replication_num)

每个 Tablet 的副本数量。默认为 3,建议保持默认即可。在建表语句中,所有 Partition中的 Tablet 副本数量统一指定。而在增加新分区时,可以单独指定新分区中 Tablet 的副本数量。

副本数量可以在运行时修改。强烈建议保持奇数。

最大副本数量取决于集群中独立 IP 的数量(注意不是 BE 数量)。Doris 中副本分布的原则是,不允许同一个 Tablet 的副本分布在同一台物理机上,而识别物理机即通过 IP。所以,即使在同一台物理机上部署了 3 个或更多 BE 实例,如果这些 BE 的 IP 相同,则依然只能设置副本数为 1。对于一些小,并且更新不频繁的维度表,可以考虑设置更多的副本数。这样在 Join 查询时,可以有更大的概率进行本地数据 Join。

存储介质 和 热数据冷却时间

• storage_medium
• storage_cooldown_time
建表时,可以统一指定所有 Partition 初始存储的介质及热数据的冷却时间,如:

"storage_medium" = "SSD"

-- 要在当前时间之后,并且是一个datetime类型 
"storage_cooldown_time" = "2023-04-20 00:00:00"

-- 默认初始存储介质可通过 fe 的配置文件 fe.conf 中指定 default_storage_medium=xxx,如果没有指定,则默认为 HDD。如果指定为 SSD,则数据初始存放在 SSD 上。没设storage_cooldown_time,则默认 30 天后,数据会从 SSD 自动迁移到 HDD上。如果指定了 storage_cooldown_time,则在到达 storage_cooldown_time 时间后,数据才会迁移。

注意,当指定 storage_medium 时,如果 FE 参数 enable_strict_storage_medium_check 为False 该参数只是一个“尽力而为”的设置。即使集群内没有设置 SSD 存储介质,也不会报错,而是自动存储在可用的数据目录中。 同样,如果 SSD 介质不可访问、空间不足,都可能导致数据初始直接存储在其他可用介质上。而数据到期迁移到 HDD 时,如果 HDD 介质不 可 访 问 、 空 间 不 足 , 也 可 能 迁 移 失 败 ( 但 是 会 不 断 尝 试 ) 。 如 果 FE 参 数enable_strict_storage_medium_check 为 True 则当集群内没有设置 SSD 存储介质时,会报错Failed to find enough host in all backends with storage medium is SSD。

Doris 数据模型及自动分区使用案例

Doris 数据模型及自动分区使用案例代码说明

简介

Doris数据模型使用案例演示。

使用

Duplicate 模型

DROP TABLE test_stu

CREATE TABLE test_stu_duplicate

(id int,

name varchar(100),

gender TINYINT,

score TINYINT

)

DUPLICATE KEY(id, name)

DISTRIBUTED BY HASH (name)

INSERT INTO test_stu_duplicate VALUES(1,'shenliang',1,10);

INSERT INTO test_stu_duplicate VALUES(2,'shenliang',1,20);

INSERT INTO test_stu_duplicate VALUES(1,'shenliang',1,10);

INSERT INTO test_stu_duplicate VALUES(3,'liangshen',0,20);

id

name

gender

score

3

liangshen

0

20

1

shenliang

1

10

2

shenliang

1

20

1

shenliang

1

10

#第3条和第1条完全重复,但进表时不会过滤,即不会去重复数据。

Unique模型

CREATE TABLE test_stu_unique

(id int,

name varchar(100),

gender TINYINT,

score TINYINT

)

UNIQUE KEY(id, name)

DISTRIBUTED BY HASH (id)

PROPERTIES("replication_num" = "1");

INSERT INTO test_stu_unique VALUES(1,'shenliang',1,10);

INSERT INTO test_stu_unique VALUES(2,'shenliang',1,20);

INSERT INTO test_stu_unique VALUES(1,'shenliang',1,10);

INSERT INTO test_stu_unique VALUES(3,'liangshen',0,20);

id

name

gender

score

3

liangshen

0

20

1

shenliang

1

10

2

shenliang

1

20

Aggr模型

CREATE TABLE test_stu_aggr

(name varchar(100),

id int,

gender TINYINT,

score  TINYINT REPLACE,

acc_classtimes  INT SUM,

max_classtimes  INT MAX,

min_classtimes  INT MIN

)

AGGREGATE KEY(name,id,gender)

DISTRIBUTED BY HASH(id)

PROPERTIES("replication_num" = "1");

INSERT INTO test_stu_aggr VALUES('shenliang',1,1,30,5,10,1);

INSERT INTO test_stu_aggr VALUES('shenliang',1,1,40,8,12,3);

name

gender

id

score

acc_classtimes

max_classtimes

min_classtimes

shenliang

1

1

40

13

12

1

这里输入插入了2条记录,但会通过

INSERT INTO test_stu_aggr VALUES('shenliang',2,1,10,2,15,5);

INSERT INTO test_stu_aggr VALUES('shenliang',2,0,20,6,7,7);

name

id

gender

score

acc_classtimes

max_classtimes

min_classtimes

shenliang

1

1

40

13

12

1

shenliang

2

1

10

2

15

5

shenliang

2

0

20

6

7

7

物化视图

物化视图是将预先计算(根据定义好的 SELECT 语句)好的数据集,存储在 Doris 中的一个特殊的表。

 create materialized view test_stu_maxclasstimes as select gender, MAX(max_classtimes)

from test_stu_aggr group by gender;

EXPLAIN select gender, MAX(max_classtimes)

from test_stu_aggr group by gender;

Explain String

PLAN FRAGMENT 0

 OUTPUT EXPRS:<slot 2> `gender` | <slot 3> max(`max_classtimes`)

  PARTITION: UNPARTITIONED

  RESULT SINK

  4:EXCHANGE

PLAN FRAGMENT 1

 OUTPUT EXPRS:

  PARTITION: HASH_PARTITIONED: <slot 2> `gender`

  STREAM DATA SINK

    EXCHANGE ID: 04

    UNPARTITIONED

  3:AGGREGATE (merge finalize)

  |  output: max(<slot 3> max(`max_classtimes`))

  |  group by: <slot 2> `gender`

  |  cardinality=-1

  | 

  2:EXCHANGE

PLAN FRAGMENT 2

 OUTPUT EXPRS:

  PARTITION: HASH_PARTITIONED: `default_cluster:dw_incubate`.`test_stu_aggr`.`id`

  STREAM DATA SINK

    EXCHANGE ID: 02

    HASH_PARTITIONED: <slot 2> `gender`

  1:AGGREGATE (update serialize)

  |  STREAMING

  |  output: max(`max_classtimes`)

  |  group by: `gender`

  |  cardinality=-1

  | 

  0:OlapScanNode

     TABLE: test_stu_aggr

     PREAGGREGATION: ON

     partitions=1/1

     rollup: test_stu_maxclasstimes

     tabletRatio=10/10

     tabletList=292497,292499,292501,292503,292505,292507,292509,292511,292513,292515

     cardinality=0

     avgRowSize=5.0

     numNodes=1

Rollup

Rollup可认为是物化视图的一个子集。

alter table test_stu_aggr add rollup rollup_id(id,acc_classtimes);

EXPLAIN

SElECT id,SUM(acc_classtimes)

FROM test_stu_aggr

GROUP BY id

Explain String

PLAN FRAGMENT 0

 OUTPUT EXPRS:<slot 2> `id` | <slot 3> sum(`acc_classtimes`)

  PARTITION: UNPARTITIONED

  RESULT SINK

  2:EXCHANGE

PLAN FRAGMENT 1

 OUTPUT EXPRS:

  PARTITION: HASH_PARTITIONED: `default_cluster:dw_incubate`.`test_stu_aggr`.`id`

  STREAM DATA SINK

    EXCHANGE ID: 02

    UNPARTITIONED

  1:AGGREGATE (update finalize)

  |  output: sum(`acc_classtimes`)

  |  group by: `id`

  |  cardinality=-1

  | 

  0:OlapScanNode

     TABLE: test_stu_aggr

     PREAGGREGATION: ON

     partitions=1/1

     rollup: rollup_id

     tabletRatio=10/10

     tabletList=292475,292477,292479,292481,292483,292485,292487,292489,292491,292493

     cardinality=0

     avgRowSize=8.0

     numNodes=1

均值

Doris里不支持Avg模型,在指标类数据不为NULL的情况下,可通过追加计数器字段算。详见#2里指标不为NULL的情况。

#1定义常量字段cnt,通过REPLACE方式使用。

CREATE TABLE t_temp

( id int,

  score int SUM,

    cnt int REPLACE default '1'

) AGGREGATE KEY (id)

distributed by hash(id)

INSERT INTO t_temp(id,score) VALUES(1,80);

INSERT INTO t_temp(id,score) VALUES(1,90);

INSERT INTO t_temp(id,score) VALUES(1,70);

INSERT INTO t_temp(id,score) VALUES(2,80);

INSERT INTO t_temp(id,score) VALUES(2,NULL);

SELECT * FROM t_temp

id  score   cnt

1   240 1

2   80  1

#2 定义累计字段cnt并求和

CREATE TABLE t_temp_2

( id int,

  score int SUM,

    cnt int SUM default '1'

) AGGREGATE KEY (id)

distributed by hash(id)

INSERT INTO t_temp_2(id,score) VALUES(1,80);

INSERT INTO t_temp_2(id,score) VALUES(1,90);

INSERT INTO t_temp_2(id,score) VALUES(1,70);

INSERT INTO t_temp_2(id,score) VALUES(2,80);

INSERT INTO t_temp_2(id,score) VALUES(2,NULL);

INSERT INTO t_temp_2(id,score) VALUES(3,70);

INSERT INTO t_temp_2(id,score) VALUES(3,50);

SELECT * FROM t_temp_2

id  score   cnt

1   240 3

3   120 2

2   80  2

均值avg可听过score/cnt得到。

分区简介

Doris字段分区支持手动建分区和动态加分区并追加历史分区信息。

手动指定分区

# 建表时手动指定分区信息,这里核心为

PARTITION BY RANGE(collectionDate)

(

    PARTITION p20211031 VALUES LESS THAN ("2021-11-01"),

    PARTITION p20211101 VALUES LESS THAN ("2021-11-02"),

        PARTITION p20211102 VALUES LESS THAN ("2021-11-03")

)

注:分区字段需要在key列表里,且key字段列表必须放在建表语句的前面。

详细见如下脚本:

DROP TABLE t_deviceinfo;
CREATE TABLE t_deviceinfo(
  collectdate date COMMENT '采集时间',
  deviceid int  COMMENT '设备ID',
  value biggint '指标值'

)UNIQUE KEY(collectdate,deviceid )
 PARTITION BY RANGE(collectdate )
(
    PARTITION p20211031 VALUES LESS THAN ("2021-11-01"),
    PARTITION p20211101 VALUES LESS THAN ("2021-11-02"),
        PARTITION p20211102 VALUES LESS THAN ("2021-11-03")
)
distributed by hash(collectdate,deviceId) buckets 20
 

动态生成分区

 # 建表时指定动态分区信息,这里主要是指定动态分区相关参数,详细解释见下:

"dynamic_partition.enable" = "true" -- 指定开启动态分区

"dynamic_partition.create_history_partition" = "true", -- 历史数据是否分区

"dynamic_partition.history_partition_num" = "200", -- 历史数据回溯时间(天为单位)

"dynamic_partition.time_unit" = "DAY", -- 分区频率,支持天、周、月、年

-- "dynamic_partition.start" = "-100", -- 设置删除删除100天前的分区(其它时间单位类似),不设置该参数即默认不删除历史分区

"dynamic_partition.end" = "7", -- 往前增加7天分区(其它时间单位类似)

"dynamic_partition.prefix" = "p",-- 指定分区名的前缀,这里是以“p”开头

"dynamic_partition.buckets" = "32" – 设置分区内桶的个数

详细建表语句见下:

DROP TABLE t_deviceinfo;
CREATE TABLE t_deviceinfo(
  collectdate date COMMENT '采集时间',
  deviceid int  COMMENT '设备ID',
  value bigint '指标值'

)

UNIQUE KEY(collectdate,deviceid )

 partition by range(collectdate)()

distributed by hash(collectdate,deviceid) buckets 20

 PROPERTIES

(

    "dynamic_partition.enable" = "true",

    "dynamic_partition.create_history_partition" = "true",

    "dynamic_partition.history_partition_num" = "200",

    "dynamic_partition.time_unit" = "DAY",

    -- "dynamic_partition.start" = "-100",

    "dynamic_partition.end" = "7",

    "dynamic_partition.prefix" = "p",

    "dynamic_partition.buckets" = "32"

);

以上是关于Doris -- 基本概念和数据表模型的主要内容,如果未能解决你的问题,请参考以下文章

Doris hdfs数据导入doris动态分区表

Doris 数据模型及自动分区使用案例

Doris 数据模型及自动分区使用案例

Doris 数据模型及自动分区使用案例

Doris 数据模型及自动分区使用案例

Doris-简介架构编译安装和数据表的基本使用