表格存储服务 CLOUDTABLE-数据模型:Aggregate模型
Aggregate模型
以实际的例子来说明什么是聚合模型,以及如何正确的使用聚合模型。
- 示例1:导入数据聚合
表1 参数说明 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
用户最小停留时间
转换成建表语句,如下所示。
CREATE TABLE IF NOT EXISTS demo.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" );
可以看到,这是一个典型的用户信息和访问行为的事实表。在一般星型模型中,用户信息和访问行为一般分别存放在维度表和事实表中。这里我们为了更加方便的解释Doris的数据模型,将两部分信息统一存放在一张表中。
表中的列按照是否设置了AggregationType,分为Key(维度列)和Value(指标列)。没有设置AggregationType的,如user_id、date、age、sex称为Key,而设置了AggregationType的称为Value。
当导入数据时,对于Key列相同的行会聚合成一行,而Value列会按照设置的AggregationType进行聚合。AggregationType目前有以下四种聚合方式:- SUM:求和,多行的Value进行累加。
- REPLACE:替代,下一批数据中的Value会替换之前导入过的行中的Value。
- MAX:保留最大值。
- MIN:保留最小值。
表2 原始数据 user_id
date
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10000
2017-10-01
A
20
0
2017-10-01 06:00:00
20
10
10
10000
2017-10-01
A
20
0
2017-10-01 07:00:00
15
2
2
10001
2017-10-01
A
30
1
2017-10-01 17:05:45
2
22
22
10002
2017-10-02
B
20
1
2017-10-02 12:59:12
200
5
5
10003
2017-10-02
C
32
0
2017-10-02 11:20:00
30
11
11
10004
2017-10-01
D
35
0
2017-10-01 10:00:15
100
3
3
10004
2017-10-03
D
35
0
2017-10-03 10:20:22
11
6
6
我们假设这是一张记录用户访问某商品页面行为的表。我们以第一行数据为例,解释如下:
表3 参数说明 数据
说明
10000
用户id,每个用户唯一识别id
2017-10-01
数据入库时间,精确到日期
A
用户所在城市
20
用户年龄
0
性别男(1 代表女性)
2017-10-01 06:00:00
用户本次访问该页面的时间,精确到秒
20
用户本次访问产生的消费
10
用户本次访问,驻留该页面的时间
10
用户本次访问,驻留该页面的时间(冗余)
那么当这批数据正确导入到Doris中后,Doris中最终存储如下:
表4 插入数据 user_id
date
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10000
2017-10-01
A
20
0
2017-10-01 07:00:00
35
10
2
10001
2017-10-01
A
30
1
2017-10-01 17:05:45
2
22
22
10002
2017-10-02
B
20
1
2017-10-02 12:59:12
200
5
5
10003
2017-10-02
C
32
0
2017-10-02 11:20:00
30
11
11
10004
2017-10-01
D
35
0
2017-10-01 10:00:15
100
3
3
10004
2017-10-03
D
35
0
2017-10-03 10:20:22
11
6
6
可以看到,用户10000只剩下了一行聚合后的数据。而其余用户的数据和原始数据保持一致。这里先解释下用户10000 聚合后的数据:
前5列没有变化,从第6列 last_visit_date 开始:
- 2017-10-01 07:00:00:因为last_visit_date列的聚合方式为REPLACE,所以2017-10-01 07:00:00替换了2017-10-01 06:00:00保存了下来。
在同一个导入批次中的数据,对于REPLACE这种聚合方式,替换顺序不做保证。如在这个例子中,最终保存下来的,也有可能是2017-10-01 06:00:00。而对于不同导入批次中的数据,可以保证,后一批次的数据会替换前一批次。
- 35:因为cost列的聚合类型为SUM,所以由20+15累加获得35。
- 10:因为max_dwell_time列的聚合类型为MAX,所以10和2取最大值,获得10。
- 2:因为min_dwell_time列的聚合类型为MIN,所以10和2取最小值,获得2。
- 示例2:保留,明细数据。
表5 参数说明 ColumnName
Type
AggregationType
Comment
user_id
LARGEINT
-
用户 ID
date
DATE
-
数据导入日期
timestamp
DATETIME
-
数据导入时间,精确到秒
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
用户最小停留时间
即增加了一列timestamp,记录精确到秒的数据导入时间。
同时,将AGGREGATE KEY设置为AGGREGATE KEY(user_id, date, timestamp, city, age, sex)。
导入数据如下:
表6 原始数据 user_id
date
timestamp
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10000
2017-10-01
2017-10-01 08:00:05
A
20
0
2017-10-01 06:00:00
20
10
10
10000
2017-10-01
2017-10-01 09:00:05
A
20
0
2017-10-01 07:00:00
15
2
2
10001
2017-10-01
2017-10-01 18:12:10
A
30
1
2017-10-01 17:05:45
2
22
22
10002
2017-10-02
2017-10-02 13:10:00
B
20
1
2017-10-02 12:59:12
200
5
5
10003
2017-10-02
2017-10-02 13:15:00
C
32
0
2017-10-02 11:20:00
30
11
11
10004
2017-10-01
2017-10-01 12:12:48
D
35
0
2017-10-01 10:00:15
100
3
3
10004
2017-10-03
2017-10-03 12:38:20
D
35
0
2017-10-03 10:20:22
11
6
6
那么当这批数据正确导入到Doris中后,Doris中最终存储如下:
表7 数据结果 user_id
date
timestamp
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10000
2017-10-01
2017-10-01 08:00:05
A
20
0
2017-10-01 06:00:00
20
10
10
10000
2017-10-01
2017-10-01 09:00:05
A
20
0
2017-10-01 07:00:00
15
2
2
10001
2017-10-01
2017-10-01 18:12:10
A
30
1
2017-10-01 17:05:45
2
22
22
10002
2017-10-02
2017-10-02 13:10:00
B
20
1
2017-10-02 12:59:12
200
5
5
10003
2017-10-02
2017-10-02 13:15:00
C
32
0
2017-10-02 11:20:00
30
11
11
10004
2017-10-01
2017-10-01 12:12:48
D
35
0
2017-10-01 10:00:15
100
3
3
10004
2017-10-03
2017-10-03 12:38:20
D
35
0
2017-10-03 10:20:22
11
6
6
- 示例3:导入数据与已有数据聚合。
接示例1中的参数列表,插入以下表中数据。
表8 原始数据 user_id
date
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10000
2017-10-01
A
20
0
2017-10-01 07:00:00
35
10
2
10001
2017-10-01
A
30
1
2017-10-01 17:05:45
2
22
22
10002
2017-10-02
B
20
1
2017-10-02 12:59:12
200
5
5
10003
2017-10-02
C
32
0
2017-10-02 11:20:00
30
11
11
10004
2017-10-01
D
35
0
2017-10-01 10:00:15
100
3
3
10004
2017-10-03
D
35
0
2017-10-03 10:20:22
11
6
6
在导入一批新的数据:
表9 新数据 user_id
date
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10004
2017-10-03
D
35
0
2017-10-03 11:22:00
44
19
19
10005
2017-10-03
E
29
1
2017-10-03 18:11:02
3
1
1
那么当这批数据正确导入到Doris中后,Doris中最终存储如下:
表10 user_id
date
city
age
sex
last_visit_date
cost
max_dwell_time
min_dwell_time
10000
2017-10-01
A
20
0
2017-10-01 07:00:00
35
10
2
10001
2017-10-01
A
30
1
2017-10-01 17:05:45
2
22
22
10002
2017-10-02
B
20
1
2017-10-02 12:59:12
200
5
5
10003
2017-10-02
C
32
0
2017-10-02 11:20:00
30
11
11
10004
2017-10-01
D
35
0
2017-10-01 10:00:15
100
3
3
10004
2017-10-03
D
35
0
2017-10-03 11:22:00
55
19
6
10005
2017-10-03
E
29
1
2017-10-03 18:11:02
3
1
1
可以看到,用户10004的已有数据和新导入的数据发生了聚合。同时新增了10005用户的数据。
数据的聚合,在Doris中有如下三个阶段发生:
- 每一批次数据导入的ETL阶段。该阶段会在每一批次导入的数据内部进行聚合。
- 底层BE进行数据Compaction的阶段。该阶段,BE会对已导入的不同批次的数据进行进一步的聚合。
- 数据查询阶段。在数据查询时,对于查询涉及到的数据,会进行对应的聚合。
数据在不同时间,可能聚合的程度不一致。例如一批数据刚导入时,可能还未与之前已存在的数据进行聚合。但是对于用户而言,用户只能查询到聚合后的数据。即不同的聚合程度对于用户查询而言是透明的。用户需始终认为数据以最终的完成的聚合程度存在,而不应假设某些聚合还未发生。