数据表的基本使用(五):ROLLUP
- 1.基本概念
- [2.Aggregate 和 Uniq 模型中的 ROLLUP](#2.Aggregate 和 Uniq 模型中的 ROLLUP)
-
- [2.1 获得每个用户的总消费](#2.1 获得每个用户的总消费)
- [2.2 获得不同城市,不同年龄段用户的总消费、最长和最短页面驻留时间](#2.2 获得不同城市,不同年龄段用户的总消费、最长和最短页面驻留时间)
- [3.Duplicate 模型中的 ROLLUP](#3.Duplicate 模型中的 ROLLUP)
-
- [3.1 前缀索引](#3.1 前缀索引)
- [3.2 ROLLUP 调整前缀索引](#3.2 ROLLUP 调整前缀索引)
- [4.ROLLUP 的几点说明](#4.ROLLUP 的几点说明)
ROLLUP 在多维分析中是 "上卷" 的意思,即将数据按某种指定的粒度进行进一步聚合。
1.基本概念
在 Doris 中,我们将用户通过建表语句创建出来的表称为 Base 表(Base Table
)。Base 表中保存着按用户建表语句指定的方式存储的基础数据。
在 Base 表之上,我们可以创建任意多个 ROLLUP 表。这些 ROLLUP 的数据是基于 Base 表产生的,并且在物理上是独立存储的。
ROLLUP 表的基本作用,在于在 Base 表的基础上,获得更粗粒度的聚合数据。
2.Aggregate 和 Uniq 模型中的 ROLLUP
因为 Uniq 只是 Aggregate 模型的一个特例,所以这里我们不加以区别。
sql
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;
2.1 获得每个用户的总消费
(1)查看表的结构信息。
sql
desc example_site_visit2 all;
(2)比如需要查看某个用户的总消费,那么可以建立一个只有 user_id
和 cost
的 ROLLUP。
sql
alter table example_site_visit2 add rollup rollup_cost_userid(user_id,cost);
(3)查看表的结构信息。
sql
desc example_site_visit2 all;
(4)然后可以通过 explain
查看执行计划,是否使用到了 ROLLUP。Doris 会自动命中这个 ROLLUP 表,从而只需扫描极少的数据量,即可完成这次聚合查询。
sql
explain SELECT user_id, sum(cost) FROM example_site_visit2 GROUP BY user_id;
(5)通过命令查看完成状态
sql
SHOW ALTER TABLE ROLLUP;
2.2 获得不同城市,不同年龄段用户的总消费、最长和最短页面驻留时间
(1)我们在 Base 表基础之上,再创建一个 ROLLUP。
sql
alter table example_site_visit2 add rollup rollup_city_age_cost_maxd_mind(city,age,cost,max_dwell_time,min_dwell_time);
(2)当我们进行如下这些查询时,Doris 执行这些 SQL 时会自动命中这个 ROLLUP 表。
sql
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;
(3)通过命令查看完成状态。
sql
SHOW ALTER TABLE ROLLUP;
3.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 36 36 个字节作为这行数据的前缀索引。当遇到 VARCHAR 类型时,前缀索引会直接截断。举例说明:
(1)以下表结构的前缀索引为 user_id
( 8 8 8 Bytes)+ age
( 4 4 4 Bytes)+ message
(前 20 20 20 Bytes)。
ColumnName | Type |
---|---|
user_id |
BIGINT |
age |
INT |
message |
VARCHAR(100) |
max_dwell_time |
DATETIME |
min_dwell_time |
DATETIME |
(2)而以下表结构的前缀索引为 user_name
( 20 20 20 Bytes)。即使没有达到 36 36 36 个字节,因为遇到 VARCHAR,所以直接截断,不再往后继续。
ColumnName | Type |
---|---|
user_name |
VARCHAR(20) |
age |
INT |
message |
VARCHAR(100) |
max_dwell_time |
DATETIME |
min_dwell_time |
DATETIME |
当我们的查询条件,是前缀索引的前缀时,可以极大的加快查询速度。比如在第一个例子中,我们执行如下查询:
sql
SELECT * FROM table WHERE user_id=1829239 and age=20;
该查询的效率会远高于如下查询:
sql
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 |
我们可以在此基础上创建一个 ROLLUP 表:
ColumnName | Type |
---|---|
age |
INT |
user_id |
BIGINT |
message |
VARCHAR(100) |
max_dwell_time |
DATETIME |
min_dwell_time |
DATETIME |
可以看到,ROLLUP 和 Base 表的列完全一样,只是将 user_id
和 age
的顺序调换了。那么当我们进行如下查询时:
sql
SELECT * FROM table where age=20 and message LIKE "%error%";
会优先选择 ROLLUP 表,因为 ROLLUP 的前缀索引匹配度更高。
4.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。