分布式merge
1、什么时候会触发merge?
1>、每次写入rename持久化之后会唤醒后台任务将一个个小的part合并 merging_mutating_task_handle->signalReadyToRun()
2>、clickhouse中的alter,主要是update delete操作同写入过程一样同样会唤起merge任务
3>、手动optimize table xx 会发起异步任务去做merge
4>、系统启动会有后台线程尝试merge
2、merge的核心逻辑
核心逻辑都在bool StorageMergeTree::merge()
-
MergeTreeDataMergeMutator.selectPartsToMerge // 选出最适合merge的分区的所有parts
3个原则:跨数据分区的Data Part之间不能合并;合并的Data Parts之间必须是相邻(在上图的有序组织关系中相邻),只能在排序链表中按段合并,不能跳跃;合并的Data Parts之间的mutation状态必须是一致的,如果Data Part A 后续还需要完成mutation-1而Data Part B后续不需要完成mutation-1(数据全部是在mutation命令之后写入或者已经完成mutation-1),则A和B不能进行合并;
2种策略:
TTL数据淘汰策略:TTL数据淘汰策略启用的条件比较苛刻,只有当某个Data Part中存在数据生命周期超时需要淘汰,并且距离上次使用TTL策略达到一定时间间隔(默认1小时)。TTL策略也非常简单,首先挑选出TTL超时最严重Data Part,把这个Data Part所在的数据分区作为要进行数据合并的分区,最后会把这个TTL超时最严重的Data Part前后连续的所有存在TTL过期的Data Part都纳入到merge的范围中。这个策略简单直接,每次保证优先合并掉最老的存在过期数据的Data Part。
常规策略:这里的选举策略就比较复杂,基本逻辑是枚举每个可能合并的Data Parts区间,通过启发式规则判断是否满足合并条件,再有启发式规则进行算分,选取分数最好的区间。启发式判断是否满足合并条件的算法在SimpleMergeSelector.cpp::allow函数中,其中的主要思想分为以下几点:系统默认对合并的区间有一个Data Parts数量的限制要求(每5个Data Parts才能合并);如果当前数据分区中的Data Parts出现了膨胀,则适量放宽合并数量限制要求(最低可以两两merge);如果参与合并的Data Parts中有很久之前写入的Data Part,也适量放宽合并数量限制要求,放宽的程度还取决于要合并的数据量。第一条规则是为了提升写入性能,避免在高速写入时两两merge这种低效的合并方式。最后一条规则则是为了保证随着数据分区中的Data Part老化,老龄化的数据分区内数据全部合并到一个Data Part。中间的规则更多是一种保护手段,防止因为写入和频繁mutation的极端情况下,Data Parts出现膨胀。启发式算法的策略则是优先选择IO开销最小的Data Parts区间完成合并,尽快合并掉小数据量的Data Parts是对在线查询最有利的方式,数据量很大的Data Parts已经有了很较好的数据压缩和索引效率,合并操作对查询带来的性价比较低。
-
FutureMergedMutatedPart.assign // 将所有parts的信息进行合并,初始化将要生成的新part对象
-
part_info.partition_id = parts.front()->info.partition_id; part_info.min_block = parts.front()->info.min_block; //合并block part_info.max_block = parts.back()->info.max_block; part_info.level = max_level + 1; //level + 1 part_info.mutation = max_mutation; //设置mutation版本
-
-
merger_mutator.mergePartsToTemporaryPart()// 落盘到临时目录
-
merger_mutator.renameMergedTemporaryPart(new_part, future_part.parts, nullptr); // mv到正式目录,数据正式可见,此方法不管是merge还是mutation 还是insert 都会复用,后续会介绍哦!
3、ReplicatedMergeTree 同分片各副本间merge流程
同分片下不同副本merge流程通过zookeeper来协调,不然各副本并发merge容易乱套,各个分片merge流程一样,互不影响,并行merge
1>、不管谁接收到请求,都由主副本来发起merge计划,20.5 之后没有主的概念了
2>、把计划推到zookeeper,各个副本监听,一起执行,如果发现自己没有某些part会从别的副本拉取