Doris 与 Clickhouse 对比(一)

时间:2024-01-27 16:53:48

1. 常用引擎

☕️ Doris 表数据模型

  1. duplicate key

???? 场景:适用于数据无需提前聚合的分析业务。

⚠️ 注意点:只指定排序列,相同的行并不会合并。

  1. unique key

???? 场景:适用于有更新需求的业务。

⚠️ 注意点:key相同时,新记录覆盖旧记录。

  1. aggregate key

???? 场景:可以提前聚合数据,适合报表和多维度业务。

⚠️ 注意点:

将会进行聚合操作,目前支持sum,min,max,replace 等

(1)sum:求和,多行的 value 进行累加。

(2)replace:替代,下一批数据中的 value 会替换之前导入过的行中的 value。

(3)max:保留最大值。

(4)min:保留最小值。

欢迎关注,一起学习

☕️ Clickhouse 表引擎

  1. *MergeTree

???? 场景:

(1)支持索引和分区。

(2)支持生命周期TTL(列级 和表级)。

⚠️ 注意点:

(1)主键并不唯一,会建索引。

(2)order by 是必须的,主键、分区非必须。

  1. ReplacingMergeTree

???? 场景:去重功能。

⚠️ 注意点:

(1)数据的去重只会在合并的过程中出现。合并会在未知的时间在后台进行,所以你无法预先作出计划。有一些数据可能仍未被处理。

(2)如果表经过了分区,去重只会在分区内部进行去重,不能执行跨分区的去重。

(3)认定重复的数据保留,版本字段值最大的如果版本字段相同则按插入顺序保留最后一次。

  1. SummingMergeTree

???? 场景:对于不查询明细,只关心以维度进行汇总聚合结果的场景。

⚠️ 注意点:

(1)以 order by 的列为准,作为维度列,其他的列按插入顺序保留第一行。

(2)不在一个分区的数据不会被聚合。

2. Join 方式

  • 查询速度和并发能力,单表性能ClickHouse更好。
  • 多表Join Doris优势更明显,特别是复杂Join和大表Join大表的场景。

首先了解下向量化引擎:核心思想就是一次处理一批数据,从而大大提高数据计算的速度,例如对于一列数据,我们通过向量化技术可以一次处理1000行数据,一次将这1000行数据做比较或者做加减运算,这种处理方式在列存数据库上尤其有效,因为列存数据库通常一列一列的将数据读取处理,在内存中都是以Array的形式存储,这种方式更容易使用向量化方式做计算。

☕️ Doris Join方式

  1. Broadcast Join

???? 说明:默认Join,将小表加载到内存中,形成一张Hash内存表,然后将Hash表广播到大表所在的各个节点。

⚠️ 注意点:

如果小表数据过大,Doris将自动转换为Shuffle join。

  1. Shuffle Join

???? 说明:小表数据无法放入内存则进行shuffle join。

⚠️ 注意点:

每个数据扫描节点将数据扫出来之后进行Partition 分区,然后根据 Partition 分区的结果分别把左右表的数据发送到对应的 Join 计算节点上。

  1. Bucket Shuffle Join

???? 说明:

利用建表时候分桶的特性,当join的时候,join的条件和左表的分桶字段一样的时候,将右表按照左表分桶的规则进行shuffle操作,使右表中需要join的数据落在左表中需要join数据的BE节点上的join。

⚠️ 注意点:

(1)Join 条件为等值的场景才有效。

(2)需要左表的分桶列的类型与右表等值 join 列的类型需要保持一致。

(3)只作用于 Doris 原生的表,其他表比如,ES,MYSQL无效。

  1. Colocation Join

???? 说明:

是将一组拥有相同 CGS 的 Table 组成一个 CG。保证这些 Table 对应的数据分片会落在同一个 BE 节点上。使得当 CG 内的表进行分桶列上的 Join 操作时,可以通过直接进行本地数据 Join。

两个概念:

(1)Colocation Group(CG):位置协同组。

(2)Colocation Group Schema (CGS): CG 中的 Table的元数据信息,比如:分桶列类型,分桶数以及分区的副本数等等信息。

⚠️ 注意点:

(1)建表时两张表的分桶列的类型和数量需要一致,保证多张表的数据分片能够一一对应分布控制。

(2)同一个 CG 内所有表的所有分区的副本数必须一致。如果不一致,可能出现某一个tablet 的某一个副本,在同一个 BE 上没有其他对应的表分片的副本。

(3)同一个 CG 内的表,分区的个数、范围以及分区列的类型不要求一致。

☕️ Clickhouse Join方式

  1. 普通 Join

???? 步骤:假设集群有4个节点,2个分区,2个副本,其中data1、data2 节点为一个分片 shard1,data3、data4 为一个分片 shard2。执行的SQL语句如下:

SELECT l_.a, r_.aFROM left_all as l JOIN right_all as r 
on l_.a = r_.a

(1)Client发送data1节点上面的sql,准备执行

(2)data1节点会把自己本机表分片shard1数据left_local准备好,也即下面sql

当然,同理,data3也会把自己的left_local数据准备好

SELECT l_.a, r_.a FROM left_local as l JOIN right_all as r 
on l_.a = r_.a

(3)当data1与data3 要开始执行第二步中 join的右表,也是一个分布式表。这个时候,data1需要shard2中的右边表数据到本地,同理data3需要shard1中的右边数据到本地。这样这两个节点都有一份全量的right_all_local。

(4)节点data1,data3 left_local数据与right_all_local做计算。

SELECT l_.a, r_.a FROM left_local as l JOIN right_all_local as r 
on l_.a = r_.a

(5)data3执行完成以后,把结果发送到data1。

(6)data1收到数据,然后和自己计算数据做汇总,最后把结果给Client。

  1. Global Join

???? 步骤:

还与普通Join不同的是,比如:

(1)data3节点将右表数据查询出来在data1上汇总为right_all_local。

(2)然后data1把right_all_local发送到data3上执行。

(3)data3的本地left_local数据与right_all_local计算好,发送到data1。

(4)data1收到数据,然后和自己计算数据做汇总,最后把结果给Client。

⚠️ 注意点:

(1)如果分片数为n,Global Join 右表本地表查询次数为n,而普通 Join 右表查询次数为n*n。这样可以减少读数据次数。

(2)数据在节点之间传播,占用部分网络流量。如果数据量较大,同样会带来性能损失。

3. 数据划分

☕️ Doris 数据划分

???? 基本组成:

(1)Row & Column:一张表包括行(Row)和列(Column)。

(2)Tablet:Doris 的存储引擎中,用户数据被水平划分为若干个数据分片(Tablet,也称作数据分桶Bucket)。每个 Tablet 包含若干数据行。各个 Tablet 之间的数据没有交集,并且在物理上是独立存储的。

(3)Partition:多个 Tablet 在逻辑上归属于不同的分区(Partition)。一个 Tablet 只属于一个 Partition。而一个 Partition 包含若干个 Tablet。

???? 数据划分:

Doris 支持两层的数据划分。第一层是 Partition,支持 Range 和 List 的划分方式。第二层是 Bucket(Tablet),支持 Hash 和 Random 的划分方式。

⚠️ 官方给出的注意点:

(1)一个表的 Tablet 总数量等于 (Partition num * Bucket num),在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。

(2)单个 Tablet 的数据量建议在 1G - 10G 的范围内。

(3)一个 Partition 的 Bucket 数量一旦指定,不可更改。所以在确定 Bucket 数量时,需要预先考虑集群扩容的情况。比如当前只有 3 台 host,每台 host 有 1 块盘。如果 Bucket 的数量只设置为 3 或更小,那么后期即使再增加机器,也不能提高并发度。

(4)官方举一些例子:假设在有10台BE,每台BE一块磁盘的情况下。如果一个表总大小为 500MB,则可以考虑4-8个分片。5GB:8-16个分片。50GB:32个分片。500GB:建议分区,每个分区大小在 50GB 左右,每个分区16-32个分片。5TB:建议分区,每个分区大小在 50GB 左右,每个分区16-32个分片。

☕️ Clickhouse 数据划分

???? 数据分片

分片策略:

(1)random随机分片:写入数据会被随机分发到分布式集群中的某个节点上。

(2) constant固定分片:写入数据会被分发到固定一个节点上。

(3) column value分片:按照某一列的值进行hash分片。

(4) 自定义表达式分片:根据表达式的值进行hash分片。

???? 数据分区

分区设计:

(1)不指定分区键,则数据默认不分区,所有数据写到一个默认分区a里面。

(2)如果分区键取值属于整型,则直接按照该整型的字符形式输出作为分区ID的取值。

(3)如果分区键取值属于日期类型,或者是能够转换为YYYYMMDD日期格式的整型,则按照分区表达式逻辑格式化后作为分区ID的取值。

(4)如果分区键取值既不属于整型或日期类型,则通过128位Hash算法取其Hash值作为分区ID的取值。

(5)分区键也可以是表达式的tuple元组。