Apache Doris 数据建模之 Aggregate Key 模型

了解 Doris 数据模型对于我们使用 Doris 来解决我们业务问题非常重要,这个系列我们将详细介绍 Doris 的三种数据模型及 Doris 数据分区分桶的一些策略,帮助用户更好的使用 Doris 。

这个系列我会讲解 Doris 的三种数据模型及在这三种数据模型之上的 Rollup,物化视图及前缀索引。还有在这个三种数据模型之上的数据分区分桶的策略。

我们知道在 Doris 中,数据以表(Table)的形式进行逻辑上的描述。 一张表包括行(Row)和列(Column)。Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。

列可以划分为两类:键 Key 和值 Value。从业务角度来看,键和值对应的就是维度列和指标列

Doris 针对不同场景提供了三种数据模型

  • Aggregate (聚合模型) : 将表中的列分为了Key和Value两种,数据会根据维度列进行分组,并对指标列进行聚合。
  • Unique (唯一主键模型):这个模型一般我们是在对接业务系统 RDS 的时候,需要 Doris 数据和业务数据保持一致,支持Upsert、Delete等操作
  • Duplicate (明细模型): 这个模型不保证数据唯一性(如果你数据重复导入两次就会重复),数据既没有主键,也没有聚合需求,一般情况下我们使用这个模型

聚合模型特点

一个正常的模型它肯定会把明细的数据存储在一个数据库中,也就是存在 Doris 中。但是因为 Doris 它最早是给凤巢的一个广告报表做的,广告报表有一个很大的特点,就是它只关心统计分析的结果,而不太关心明细的数据,所以 Doris 最早一代的数据模型,是一个聚合的模型。

聚合模型的特点就是将表中的列分为了Key和Value两种。 Key 就是数据的维度列,比如时间,地区等等。 Value 则是数据的指标列,比如点击量,花费等。每个指标列还会有自己的聚合函数,包括sum、min、max和bitmap_union 等。数据会根据维度列进行分组,并对指标列进行聚合。如下图:

通过上面的图我们可以看到,这是一个典型的用户信息和访问行为的事实表。 在一般星型模型中,用户信息和访问行为一般分别存放在维度表和事实表中。这里我们为了更加方便的解释 Doris 的数据模型,将两部分信息统一存放在一张表中。

这个表我们是按照:user_id,date,city,age,sex 来统计用户最后访问时间、用户总消费、用户最大停留时间、最小停留时间

表中的列按照是否设置了 IndexKeysType 是 AGG_KEYS 表示是聚合模型,分为 Key (维度列) 和 Value(指标列)。

这里我们 Key 列是 true 表示这个字段是 Key 列,false 的表示 Value 列,所有的 value 列我们在建表的时候指定他们的聚合类型(AggregationType)

上面这个对应的建表语句如下:

CREATE TABLE  example_tbl_02
(
    `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"
);

我们插入几条数据:

user_iddatecityagesexlast_visit_datecostmax_dwell_timemin_dwell_time
100002017-10-01北京2002017-10-01 06:00:00201010
100002017-10-01北京2002017-10-01 07:00:001522
100012017-10-01北京3012017-10-01 17:05:4522222
100022017-10-02上海2012017-10-02 12:59:1220055
100032017-10-02广州3202017-10-02 11:20:00301111
100042017-10-01深圳3502017-10-01 10:00:1510033
100042017-10-03深圳3502017-10-03 10:20:221166

上面这个数据中我们可以看到,前两行的数据 Key 是完全一致的,后面 Value 字段应该按照我们建表时候指定的聚合方式进行自动完成数据聚合,我们执行下面的语句插入数据,看看是否和我们预想的一致:

insert into example_tbl_02 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);

通过下图来查看我们最后执行后的数据

聚合模型支持的聚合方式

当我们导入数据时,对于 Key 列相同的行会聚合成一行,而 Value 列会按照设置的 AggregationType 进行聚合。 AggregationType 目前有以下几种聚合方式和agg_state:

  1. SUM:求和,多行的 Value 进行累加。
  2. REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
  3. MAX:保留最大值。
  4. MIN:保留最小值。
  5. REPLACE_IF_NOT_NULL:非空值替换。和 REPLACE 的区别在于对于null值,不做替换,这种聚合方式适用于部分列更新
  6. HLL_UNION:HLL 类型的列的聚合方式,通过 HyperLogLog 算法聚合。
  7. BITMAP_UNION:BIMTAP 类型的列的聚合方式,进行位图的并集聚合。

聚合模型使用场景

我们知道需要再建表的时候指定数据模型,一旦创建表后期不能修改数据模型,如果你建表的时候没有指定数据模型默认是明细模型 (Duplicate Key).

  1. Aggregate 模型可以通过预聚合,极大地降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表类查询场景。但是该模型对 count(*) 查询很不友好。同时因为固定了 Value 列上的聚合方式,在进行其他类型的聚合查询时,需要考虑语意正确性
  2. 在数据分析领域,有很多需要对数据进⾏统计和汇总操作的场景,如需要分析⽹站和 APP 访问流量,统计⽤⼾的访问总时⻓,访问总次数。或者像⼚商需要为⼴告主提供⼴告点击的总流量,展⽰总量,消费统计等指标。在这些不需要保存明细数据的场景,只需要按照固定维度和指标进行聚合后的数据,通常可以使⽤聚合模型。
  3. 另外再部分列更新场景,如果用户既需要唯一主键约束,又需要更新部分列(例如将多张源表导入到一张 doris 表的情形),则可以考虑使用 Aggregate 模型,同时将非主键列的聚合类型设置为 REPLACE_IF_NOT_NULL,不过 Doris 2.0 版本 Unqiue Key 模型开始支持部分列更新,如果你没有开启行存情况下,大数据量表进行部分列更新对你的 CPU 消耗可能会很高,这个时候如果你没有特殊的需求,也可以使用聚合模型。Unique Key 模型其实也是聚合模型的一个特例。
  4. 聚合模型中不保存明细数据,如果你需要保存明细数据,就不适合使用聚合模型,建议选择Unqiue Key 或者 Duplicate Key 模型。

聚合模型限制

在聚合模型中,模型对外展现的,是最终聚合后的数据。也就是说,任何还未聚合的数据(比如说两个不同导入批次的数据),必须通过某种方式,以保证对外展示的一致性,特别是在聚合模型上做count计算,可能会导致结果不准确,针对这种情况我们怎么去解决。

第一种方式:增加一个 count 列,并且导入数据中,该列值恒为 1。则 select count(*) from table; 的结果等价于 select sum(count) from table;。而后者的查询效率将远高于前者。不过这种方式也有使用限制,就是用户需要自行保证,不会重复导入 AGGREGATE KEY 列都相同的行。否则,select sum(count) from table; 只能表述原始导入的行数,而不是 select count(*) from table; 的语义。

另一种方式:就是 将如上的 count 列的聚合类型改为 REPLACE,且依然值恒为 1。那么 select sum(count) from table; 和 select count(*) from table; 的结果将是一致的。并且这种方式,没有导入重复行的限制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值