ROLLUP 在多维分析中是“上卷”的意思,即将数据按某种指定的粒度进行进一步聚合。
一、基本概念
- 在 Doris 中,我们将用户通过建表语句创建出来的表称为 Base 表(Base Table)。Base 表中保存着按用户建表语句指定的方式存储的基础数据。
- 在 Base 表之上,我们可以创建任意多个 ROLLUP 表。这些 ROLLUP 的数据是基于 Base 表产生的,并且在物理上是独立存储的。
- ROLLUP 表的基本作用,在于在 Base 表的基础上,获得更粗粒度的聚合数据。
二、Aggregate 和 Uniq 模型中的 ROLLUP
因为 Uniq 只是 Aggregate 模型的一个特例,所以这里我们不加以区别。
2.1 准备数据
-- step1:建表
CREATE TABLE IF NOT EXISTS test_db.example_site_visit2 (
`user_id` LARGEINT NOT NULL COMMENT "用户id",
`date` DATE NOT NULL COMMENT "数据灌入日期时间",
`timestamp` DATETIME 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 10
;
-- step2:插入数据
insert into test_db.example_site_visit2
values
(10000,'2017-10-01','2017-10-01 08:00:05','北京',20,0,'2017-10-01 06:00:00',20,10,10),
(10000,'2017-10-01','2017-10-01 09:00:05','北京',20,0,'2017-10-01 07:00:00',15,2,2),
(10001,'2017-10-01','2017-10-01 18:12:10','北京',30,1,'2017-10-01 17:05:45',2,22,22),
(10002,'2017-10-02','2017-10-02 13:10:00','上海',20,1,'2017-10-02 12:59:12',200,5,5),
(10003,'2017-10-02','2017-10-02 13:15:00','广州',32,0,'2017-10-02 11:20:00',30,11,11),
(10004,'2017-10-01','2017-10-01 12:12:48','深圳',35,0,'2017-10-01 10:00:15',100,3,3),
(10004,'2017-10-03','2017-10-03 12:38:20','深圳',35,0,'2017-10-03 10:20:22',11,6,6)
;
-- step3:查看表
select *
from test_db.example_site_visit2
;
-- 可以看到,存储的数据,和导入数据完全一样,没有发生任何聚合。
-- 这是因为,这批数据中,因为加入了 timestamp 列,所有行的 Key 都不完全相同。
-- 也就是说,只要保证导入的数据中, 每一行的 Key 都不完全相同,那么即使在聚合模型下,Doris 也可以保存完整的明细数据。
2.2 查看某个用户的总消费
desc example_site_visit2 all;
- 比如需要查看某个用户的总消费,那么可以建立一个只有 user_id 和 cost 的 rollup
alter table example_site_visit2
add rollup
rollup_cost_userid(user_id,cost);
desc example_site_visit2 all;
- 然后可以通过 explain 查看执行计划,是否使用到了 rollup
- Doris 会自动命中这个 ROLLUP 表,从而只需扫描极少的数据量,即可完成这次聚合查询。
explain
SELECT user_id
, sum(cost)
FROM example_site_visit2
GROUP BY user_id
;
show alter table rollup;
2.3 获得不同城市,不同年龄段用户的总消费、最长和最短页面驻留时间
alter table example_site_visit2
add rollup
rollup_city_age_cost_maxd_mind(city,age,cost,max_dwell_time,min_dwell_time);
explain
SELECT city,
age,
sum(cost),
max(max_dwell_time),
min(min_dwell_time)
FROM example_site_visit2
GROUP BY city,
age
;
explain
SELECT city,
sum(cost),
max(max_dwell_time),
min(min_dwell_time)
FROM example_site_visit2
GROUP BY city
;
explain
SELECT city,
age,
sum(cost),
min(min_dwell_time)
FROM example_site_visit2
GROUP BY city,
age
;
show alter table rollup;
三、Duplicate 模型中的 ROLLUP
- 因为 Duplicate 模型没有聚合的语意。所以该模型中的 ROLLUP,已经失去了“上卷” 这一层含义。而
仅仅是作为调整列顺序,以命中前缀索引的作用
。 - 下面详细介绍前缀索引, 以及如何使用 ROLLUP 改变前缀索引,以获得更好的查询效率。
3.1 前缀索引
- 不同于传统的数据库设计,Doris 不支持在任意列上创建索引。Doris 这类 MPP 架构的 OLAP 数据库,通常都是通过提高并发,来处理大量数据的。
- 本质上,Doris 的数据存储在类似 SSTable(Sorted String Table)的数据结构中。该结构是一种有序的数据结构,可以按照指定的列进行排序存储。在这种数据结构上,以排序列作为条件进行查找,会非常的高效。
- 在 Aggregate、Uniq 和 Duplicate 三种数据模型中。底层的数据存储,是按照各自建表语句中,AGGREGATE KEY、UNIQ KEY 和 DUPLICATE KEY 中指定的列进行排序存储 的。而前缀索引,即在排序的基础上,实现的一种根据给定前缀列,快速查询数据的索引方式。
- 我们将一行数据的前 36 个字节 作为这行数据的前缀索引。当遇到 VARCHAR 类型 时,前缀索引会直接截断。
- 举例说明:
- 以下表结构的前缀索引为 user_id(8 Bytes) + age(4 Bytes) + message(prefix 20 Bytes)。
ColumnName | Type |
user_id | BIGINT |
age | INT |
message | VARCHAR(100) |
max_dwell_time | DATETIME |
min_dwell_time | DATETIME |
- 以下表结构的前缀索引为 user_name(20 Bytes)。即使没有达到 36 个字节,因为遇到 VARCHAR,所以直接截断,不再往后继续。
ColumnName | Type |
user_name | VARCHAR(20) |
age | INT |
message | VARCHAR(100) |
max_dwell_time | DATETIME |
min_dwell_time | DATETIME |
- 当我们的查询条件,是前缀索引的前缀时,可以极大的加快查询速度。
- 所以在建表时,正确的选择列顺序,能够极大地提高查询效率。
比如在第一个例子中,我们执行如下查询:
SELECT *
FROM table
WHERE user_id=1829239 and age=20
;
该查询的效率会远高于如下查询:
SELECT *
FROM table
WHERE age=20
;
3.2 ROLLUP 调整前缀索引
- 因为建表时已经指定了列顺序,所以一个表只有一种前缀索引。这对于使用其他不能命中前缀索引的列作为条件进行的查询来说,效率上可能无法满足需求。因此,我们可以通过 创建 ROLLUP 来人为的调整列顺序
- 举例说明。
- Base 表结构如下:
ColumnName | Type |
user_id | BIGINT |
age | INT |
message | VARCHAR(100) |
max_dwell_time | DATETIME |
min_dwell_time | DATETIME |
ColumnName | Type |
age | INT |
user_id | BIGINT |
message | VARCHAR(100) |
max_dwell_time | DATETIME |
min_dwell_time | DATETIME |
- 可以看到,ROLLUP 和 Base 表的列完全一样,只是将 user_id 和 age 的顺序调换了。那么当我们进行如下查询时:
- 会优先选择 ROLLUP 表,因为 ROLLUP 的前缀索引匹配度更高。
SELECT *
FROM table
where age=20 and message LIKE "%error%"
;
四、ROLLUP 的几点说明
- ROLLUP 最根本的作用是提高某些查询的查询效率(无论是通过聚合来减少数据量,还是修改列顺序以匹配前缀索引)。因此 ROLLUP 的含义已经超出了“上卷” 的范围。这也是为什么在源代码中,将其命名为 Materialized Index(物化索引)的原因。
- ROLLUP是附属于Base表的,可以看做是Base表的一种辅助数据结构。用户可以 在 Base 表的基础上,创建或删除 ROLLUP,但是不能在查询中显式的指定查询某 ROLLUP。是否命中 ROLLUP 完全由 Doris 系统自动决定。
- ROLLUP的数据是独立物理存储的。因此,创建的ROLLUP越多,占用的磁盘空间也就越大。同时对导入速度也会有影响(导入的 ETL 阶段会自动产生所有 ROLLUP 的数据),但是不会降低查询效率(只会更好)。
- ROLLUP的数据更新与Base表是完全同步的。用户无需关心这个问题。
- ROLLUP中列的聚合方式,与Base表完全相同。在创建ROLLUP无需指定,也不能修改。
- 查询能否命中 ROLLUP 的一个必要条件(非充分条件)是,查询所涉及的所有列(包括 select list 和 where 中的查询条件列等)都存在于该 ROLLUP 的列中。否则,查询只能命中 Base 表。
- 某些类型的查询(如count(*))在任何条件下,都无法命中ROLLUP。
- 可以通过 EXPLAIN your_sql; 命令获得查询执行计划,在执行计划中,查看是否命中 ROLLUP。
- 可以通过 DESC tbl_name ALL; 语句显示 Base 表和所有已创建完成的 ROLLUP。
原文链接:Doris--Rollup_doris rollup-CSDN博客