ClickHouse副本与分片
副本
使用副本的主要目的是防止数据丢失,增加数据存储的冗余,而使用分片的目的是实现数据的水平切分。
一个数据分区从开始创建到全部完成会经过两类存储:
- 内存:数据线写入内存缓冲区
- 本地磁盘:数据会被写入tmp临时目录分区,待全部完成后再重命名为正式分区
ReplicatedMergeTree原理
ReplicatedMergeTree在增加了ZooKeeper的部分,会在zk内创建一系列的监听节点,以此来实现多个实例之间的通信,整个过程ZK都不会涉及表的传输。
ZK下ReplicatedMergeTree会创建几类节点,其中比较重要的是/log和/mutations和/replicas
log
常规操作日志(INSERT/MERGE/DROP PARTITION),保存了副本需要执行的任务指令,每个副本实例都会监听/log节点,当有新指令加入时,会将该指令加入到副本各自的任务队列并执行任务。
mutations
MUTATION操作日志节点,与log类似,当执行ALTER/DELETE/ALTER UPDATE时,操作指令会被添加到这个节点
replicas/{replica_name}
每个副本各自的节点下的一组监听节点,用于指导副本在本地执行具体的任务指令
- /queue,任务队列节点,用于执行具体的操作任务,当副本从/log或/mutations下监听到操作指令时,会将执行任务添加到该节点下,并基于队列执行
- /log_pointer,记录当前最后一次执行的log日志信息
- /mutation_pointer,记录当前最后一次执行的mutation日志信息
副本协同流程
由于副本采用了多主架构,所以在每个副本实例都可以作为数据的读写入口,分摊了节点的负载。
ZK选择主副本,主副本会主导MERGE和MUTATION操作(ALTER DELETE和ALTER UPDATE),这些在主副本完成之后再借助ZK将消息事件分发到其他副本。
其他的都遵循多主架构。
INSERT和ALTER查询是分布式执行的,借助ZK的事件通知机制。
其他查询包括SELECT/CREATE/DROP/RENAME/ATTACH是不支持分布式执行的。
INSERT执行流程
CH1节点和CH2节点:
- CH1节点:
1.1 初始化表节点
1.2 注册副本节点
1.3 监听/log
1.4 选举,第一个会是主副本 - CH2节点:
2.1 注册副本节点
2.2 监听/log节点
2.3 选举,成本副本 - CH1节点:
3.1 本地创建新分区
3.2 由执行INSERT的副本发送日志
3.3 推送操作日志到/log - CH1节点分发日志
- CH2节点:
5.1 监听到/log新的操作日志
5.2 拉取日志对象
5.3 创建task任务放入/queue队列
5.4 基于/queue队列执行任务
5.5 选择一个远端的比较新的副本,下载数据
5.6 向远端副本发送下载请求,如果失败尝试5次 - CH2节点下载数据
- CH1节点接收到下载请求,返回分区数据
- CH2节点接收数据,在本地创建分区目录
MERGE执行流程
无论MERGE操作是哪个副本发起的,合并计划都会交由主副本来执行,由主副本来制定MERGE计划,并判断哪些分区需要被合并,选定之后将由主副本将LOG日志推送到/log,以此通知所有副本开始合并。
分片
引入数据副本,可以降低丢失数据的风险,提高查询,但是没有解决数据容量的问题,每个副本本身保存的仍然是数据表的全量数据,那么需要进一步切分数据,即数据分片。
ClickHouse的数据分片需要结合Distributed表引擎使用,Distributed表引擎本身不存储任何数据,只是作为分布式表的上层代理,在集群类自动开展数据的写入、分发、查询和路由操作。
Distributed原理
作为数据分片的透明代理,能够自动将数据路由到集群上的各个节点。
查询的分类:
- 作用于本地表的查询,对于INSERT和SELECT查询,Distributed将会以分布式的方式作用于本地表
- 只会影响Distributed本身,不会作用于本地表的查询,Distributed支持部分元数据操作,包括CREATE/DROP/ALTER等,只会修改Distributed本身。
- 不支持的查询:Distributed不支持任何Mutation操作,包括ALTER DELETE 和ALTER UPDATE。
多副本的路由选择
当查询数据时,如果集群中的某个shard拥有多个replica时,那么Distributed将面临选择副本的问题,会使用负载均衡算法选择具体某个副本,有以下四种:
- random
- nearest_random
- in_order
- first_or_random
为了解决查询放大的问题,可以使用GLOBAL IN 或JOIN进行优化,在使用GLOBAL修饰后,ClickHouse
使用内存表临时保存了IN字句查询到的数据,将其发送到远端分片节点,达到数据共享的目的,避免查询放大的问题。