一、doris是什么
Apache Doris是一个现代化的MPP(大规模并行分析)分析型数据库产品。仅需亚秒级响应时间即可获得查询结果,有效地支持实时数据分析。Apache Doris的分布式架构非常简洁,易于运维,并且可以支持10PB以上的超大数据集。
Apache Doris可以满足多种数据分析需求,例如固定历史报表,实时数据分析,交互式数据分析和探索式数据分析等。令数据分析工作更加简单高效!
二、数据模型
-
Aggregate(聚合模型,提前聚合数据, 适合报表和多维分析业务)
- SUM:求和,多行的 Value 进行累加。
- REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
- MAX:保留最大值。
-
MIN:保留最小值。
CREATE TABLE IF NOT EXISTS example_db.expamle_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`, `timestamp`, `city`, `age`, `sex`) ... /* 省略 Partition 和 Distribution 信息 */ ;
user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 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 在导入上述数据时,最终表里面就只有一行数据:
user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time 10000 2017-10-01 北京 20 0 2017-10-01 07:00:00 35 10 2 其中user_id, date, city age,sex 因为没有聚合模型,因此只有当他们都一样时,才可以发生后面的聚合;假设city不一样就不能聚合;
- 如果业务场景就是需要存入明细,那么一般的做法是列增加时间戳,这样每一列都不一样,就可以保存数据明细;
- 数据在不同时间,可能聚合的程度不一致。比如一批数据刚导入时,可能还未与之前已存在的数据进行聚合。但是对于用户而言,用户只能查询到聚合后的数据。即不同的聚合程度对于用户查询而言是透明的。用户需始终认为数据以最终的完成的聚合程度存在,而不应假设某些聚合还未发生。
-
Uniq模型(保证 Key 的唯一性,适用于有更新需求的分析业务。)
- 其实是Aggregate聚合模型的一个特例,即只有aggregate key 是唯一的,其他列都是replace特性
-
CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `user_id` LARGEINT NOT NULL COMMENT "用户id", `username` VARCHAR(50) NOT NULL COMMENT "用户昵称", `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`, `user_name`)
等同于:
-
CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `user_id` LARGEINT NOT NULL COMMENT "用户id", `username` VARCHAR(50) NOT NULL COMMENT "用户昵称", `city` VARCHAR(20) REPLACE COMMENT "用户所在城市", `age` SMALLINT REPLACE COMMENT "用户年龄", `sex` TINYINT REPLACE COMMENT "用户性别", `phone` LARGEINT REPLACE COMMENT "用户电话", `address` VARCHAR(500) REPLACE COMMENT "用户地址", `register_time` DATETIME REPLACE COMMENT "用户注册时间" ) AGGREGATE KEY(`user_id`, `user_name`)
-
Duplicate模型(只指定排序列,相同的行不会合并。适用于数据无需提前聚合的分析业务。)
这种数据模型区别于 Aggregate 和 Uniq 模型。数据完全按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留。 而在建表语句中指定的 DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序。CREATE TABLE IF NOT EXISTS example_db.expamle_tbl ( `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`)
三、ROLLUP(上面的每个模型产生的其实是一个base表,我们需要在base表的基础上建立其他表,以便于提高查询效率)
Rollup 本质上可以理解为原始表(Base Table)的一个物化索引。建立 Rollup 时可只选取 Base Table 中的部分列作为 Schema。Schema 中的字段顺序也可与 Base Table 不同
表存储数据如下:
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 | 北京 | 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 |
在此基础上,我们创建一个 ROLLUP:
ColumnName |
---|
user_id |
cost |
创建完成后,表里面的数据为
user_id | cost |
---|---|
10000 | 35 |
10001 | 2 |
这个时候我们如果查询SELECT user_id, sum(cost) FROM table GROUP BY user_id; 则doris会自动命中这个 ROLLUP 表,从而只需扫描极少的数据量,即可完成这次聚合查询;
rollup重要特点:
- ROLLUP 是附属于 Base 表的,可以看做是 Base 表的一种辅助数据结构。用户可以在 Base 表的基础上,创建或删除 ROLLUP,但是不能在查询中显式的指定查询某 ROLLUP。是否命中 ROLLUP 完全由 Doris 系统自动决定。
- ROLLUP 的数据是独立物理存储的。因此,创建的 ROLLUP 越多,占用的磁盘空间也就越大。同时对导入速度也会有影响,但是不会降低查询效率;
- ROLLUP 的数据更新与 Base 表示完全同步的;
- 查询能否命中 ROLLUP 的一个必要条件(非充分条件)是,查询所涉及的所有列(包括 select list 和 where 中的查询条件列等)都存在于该 ROLLUP 的列中。否则,查询只能命中 Base 表。
四、join操作(hash join,broadcast join,shuffle join )
Join是数据库查询永远绕不开的话题,传统查询SQL技术总体可以分为简单操作(过滤操作-where、排序操作-limit等),聚合操作-groupBy等以及Join操作等。其中Join操作是其中最复杂、代价最大的操作类型
传统数据库单机模式做Join的场景毕竟有限,也建议尽量减少使用Join。然而大数据领域就完全不同,Join是标配,OLAP业务根本无法离开表与表之间的关联,对Join的支持成熟度一定程度上决定了系统的性能,夸张点说,“得Join者得天下”
Doris会自动尝试进行 Broadcast Join,如果预估小表过大则会自动切换至 Shuffle Join。注意,如果此时显式指定了 Broadcast Join 也会自动切换至 Shuffle Join。
- hash join算法就来自于传统数据库,而shuffle和broadcast是大数据的皮,两者一结合就成了大数据的算法了
- hash join: 适用于至少有一个是小表的场景(hash join基本都只扫描两表一次,可以认为O(a+b),较之最极端的是笛卡尔积运算O(a*b), 小表的原因是在构建Hash Table时,最好可以把数据全部加载到内存中,因为这样效率才最高)
- 两个表,取小表为Build Table, 大表为Probe Table
- 构建Hash Table:依次读取Build Table的数据,对于每一条数据根据Join Key进行hash,hash到对应的bucket中(类似于HashMap的原理),最后会生成一张HashTable,HashTable会缓存在内存中,如果内存放不下会dump到磁盘中;
-
匹配:生成Hash Table后,在依次扫描Probe Table的数据,使用相同的hash函数(在spark中,实际上就是要使用相同的partitioner)在Hash Table中寻找hash(join key)相同的值,如果匹配成功就将两者join在一起。
- broadcast join(将其中一张较小的表通过广播的方式,由driver发送到各个executor,大表正常被分成多个区,每个分区的数据和本地的广播变量进行join(相当于每个executor上都有一份小表的数据,并且这份数据是在内存中的,过来的分区中的数据和这份数据进行join)。broadcast适用于表很小,可以直接被广播的场景;)
- 基表不能被广播,比如left outer join时,只能广播右表
- shuffle join(一旦小表比较大,此时就不适合使用broadcast hash join了。这种情况下,可以对两张表分别进行shuffle,将两张表相同key的数据分到一个分区中,然后分区和分区之间进行join。相当于将两张表都分成了若干小份,小份和小份之间进行hash join,充分利用集群资源。)
五、分区& 分桶
Doris 支持两级分区存储, 第一层为 RANGE 分区(partition), 第二层为 HASH 分桶(bucket)
-
RANGE分区用于将数据划分成不同区间, 逻辑上可以理解为将原始表划分成了多个子表。业务上,多数用户会选择采用按时间进行partition, 让时间进行partition有以下好处:
* 可区分冷热数据
* 可用上Doris分级存储(SSD + SATA)的功能
* 按分区删除数据时,更加迅速
-
根据hash值将数据划分成不同的 bucket。
* 建议采用区分度大的列做分桶, 避免出现数据倾斜 * 为方便数据恢复, 建议单个 bucket 的 size 不要太大, 保持在 10GB 以内, 所以建表或增加 partition 时请合理考虑 bucket 数目, 其中不同 partition 可指定不同的 buckets 数。
六、数仓分层
doris库的表是有几种不同前缀的, 每一种前缀其实是代表了不同的数据分层;
- ods层:最原始的数据,比如日志, 数据保持原有数据格式不变;
- dwd层:对ods层的数据进行清洗,比如去除掉一些脏数据,空值等;
- dws层:在dwd层的基础上,进行聚合,形成宽表,比如续报信息,会有新报、先报、续报等信息;宽表:一张表会涵盖比较多的业务内容,由于其字段较多,因此一般也会称该层的表为宽表,宽表缺点是数据会有大量冗余,而且生成相对比较滞后,查询结果可能并不及时。
- Schema 中字段数比较多, 聚合模型中可能 key 列比较多, 导入过程中需要排序的列会增加。
- 维度信息更新会反应到整张表中,而更新的频率直接影响查询的效率。
- ads层:为各种统计报表提供数据;比如ads_ws_renew就是提供续报的相关数据;
- dim:维表, 比如dim_course_info就是课程信息;维表分为:高基数维度数据:一般是用户资料表、商品资料表类似的资料表,数据量可能是千万级或者上亿级别;低基数维度数据:一般是配置表,比如枚举值对应的中文含义,或者日期维表。数据量可能是个位数或者几千几万