同步写入
过程分解
把数据写入到本地临时Data Part中
从zookeeper上申请自增的block number序列号
commit临时Data Part
前置检查
检查本地表的meta版本是否已经落后于zookeeper上的状态
上传一个GET_PART类型的Log到Shard对应的Zookeeper目录下的log znode下
其他副本通过观察zookeeper来异步拷贝写入的Data Part
性能问题
一次Batch写入的过程和zookeeper交互的次数不下10此,要是Batch数据跨10个数据分区的话就是100次
使用Clickhouse时一定要做Batch写入并且按照数据分区提前聚合
一致性保证
望Zookeeper提交GET_PART Log时Zk session断开或者超时
本地的Data Part会继续commit,并跑错给用户重试写入数据,同时把Data Part丢到一个异步检查线程的任务队列中
异步检查线程会等待重连Zookeeper,检查本地的Data Part是否注册到Zookeeper上
如果没有则会移除本地的Data Part
优化参数
use_minimalistic_part_header_in_zookeeper
降低zookeeper压力配置
每个新写入的Data Part不再注册自己的columns信息和checksums到Zookeeper上
而是压缩成Hash值写到Data Part的Znode data中
insert_quorum
写入链路检查数据同步的副本数达到要求才能够成功返回
写入节点在Commit Data Part时还会创建一个Shard级别的quorum/status Znode,其他节点同步完数据之后需要更新到quorum/status,写入节点这边通过Watch机制收到通知再返回客户的写入请求
insert_deduplicate
对每次收到的批量写入数据计算一个Hash Value,然后注册到Zookeeper上。后续如果出现完全重复的一批数据,写入链路上会出现Zookeeper创建重复节点异常,用户就会收到重复写入反馈
异步Task
StorageReplicatedMergeTree::queueUpdatingTask
同步Zookeeper中Shard级别下的Data Part Log任务队列数据到自己的Znode任务队列中
在自己的Znode下维护更新当前正在处理的log_pointer(当前已经拷贝过的最大log Id)和min_unprocessed_insert_time(近似评估写入的延迟时间)信息
把任务放到节点的RAM队列中
StorageReplicatedMergeTree::mutationsUpdatingTask
从Zookeeper的Shard级别下的Mutation任务队列同步数据到节点的RAM状态中
是依赖Zookeeper的Watch机制来通知ClickHouse的BackgroundSchedulePool调度起工作Task,包括上一个queueUpdatingTask也是相同机制被调度
StorageReplicatedMergeTree::queueTask
负责从RAM任务队列中消费执行具体的操作,并且会有多个后台线程被调度起并行执行多个任务
ClickHouse在RAM状态中追踪了所有正在执行的任务即将产生和依赖的Data Part,可以保证有数据依赖关系的任务串行化执行
对于"GET_PART"类型的任务,Task执行逻辑会尝试从远端节点下载数据到本地,同时如果有quorum数量要求的话更新quorum统计信息
对于"MERGE_PARTS","MUTATE_PART"的任务,节点首先会尝试在本地进行实际的merge或者mutation动作,但是当本地的Input Data Part存在缺失或者损坏时,ClickHouse可以采用保守策略
尝试从远端下载merge完成的Data Part
每次merge、mutation的开销都是非常大的,配置只选择主副本完成merge、mutation任务,而让其他副本直接从远程下载可以大幅减轻集群的负载
当一些极端场景出现,远端的结果Data Part N也无法下载时(一般是这个任务对应的远端Data Part N再次发生了数据变更变成了Data Part M),节点会把当前这个任务放回到任务队列的尾端,让它延迟执行
StorageReplicatedMergeTree::mergeSelectingTask
这个Task的只有主副本节点会调度,它负责不断选择下一次要进行merge / mutation的Data Parts,把具体的merge / mutation的任务日志发布到Zookeeper的任务队列上
新写入的Data Part只有同步到全部副本节点后才可以参与merge
StorageReplicatedMergeTree::movePartsTask
这个异步Task主要是配合ClickHouse的存储分层设计
当高性能(SDD)的存储空间快用满时,它会不断自动地把数据往更低级(HDD)的存储上去迁移
StorageReplicatedMergeTree::mutationsFinalizingTask**
这个Task的作用是异步去更新当前副本的mutation任务队列执行进度