「ClickHouse系列」Replication机制详解

Hi,我是王知无,一个大数据领域的原创作者。 放心关注我,获取更多行业的一手消息。
在阅读本文之前你一定要先看看这个:
Clickhouse - Replication机制
1. Replication引擎族
Replication仅对于MergeTree引擎族提供支持, 两者是正交的:
ReplicatedMergeTree ReplicatedSummingMergeTree ReplicatedReplacingMergeTree ReplicatedAggregatingMergeTree ReplicatedCollapsingMergeTree ReplicatedVersionedCollapsingMergeTree ReplicatedGraphiteMergeTree

2. 工作机制
在Clickhouse中, Replication的机制工作在表级别, 而不是库, 或者是节点层级. 一个节点可以同时存储使用Replication引擎的表以及不使用Replication引擎的表.
Replication机制不依赖于分片(shard), 每个分片有自己独立的Replication机制. 夸分片的话, 就算是表名一致, 表引擎一致, 数据也不会进行同步.
在同一个节点上, Create,Drop, Attach, Detach以及Rename等查询并不会触发Replication机制.
当创建表语句( CREATE TABLE)在一个节点上被执行时, 会创建一个新的Replication表. 如果在该集群中其他节点上已经存在该表, 那会添加一个新的副本.删除表语句( DROP TABLE)会删除当前节点上的副本表.重命名表语句( RENAME TABLE)会重命名当前节点的表名, 但不会修改该集群其他节点的表名. 即Replicated表在不同的副本上可以有不同的名字.
2.1. 依赖Zookeeper
Clickhouse使用zookeeper来存储副本的元数据信息(zk版本要求3.4.5或者更高). 当表引擎被指定为Replication引擎族时, Clickhouse将使用设定zk集群的一个目录作为自己元数据的存储地点(这个目录可以通过具体的表引擎语句来指定).
需要注意的是, 如果在clickhouse的配置文件中没有配置zk集群信息, 将不能创建使用Replication引擎族的表, 并且已存在的使用Replication引擎族的表将会被锁定在只读状态(read-only).
当查询表引擎为Replication引擎族的本地表时, Zookeeper集群并不会工作, SELECT查询语句的执行性能和不使用Replication引擎族的表是一样快的. 当查询表引擎为Replication引擎族的分布式表时, SELECT查询语句的执行由max_replica_delay_for_distributed_queries和fallback_to_stale_replicas_for_distributed_queries这两个参数控制, 合理的设置这两个参数可以有效提升查询分布式的速度.
对于INSERT插入语句而言, 所有被插入的数据会根据一定的规则划分为几个数据块Block, 每个数据块的相关元数据信息会通过事务来被提交到Zookeeper(一个数据块具体可以最多容纳多少条数据是由参数max_insert_block_size决定的). 这个机制导致了数据插入使用了Replication引擎的表的延迟会比使用了其他引擎的表稍高一些, 合理的数据写入机制能缓解这个延迟的影响.
如果Clickhouse集群的规模非常大, 可以为不同的分片(shard)使用不同的Zookeeper集群, 即一套Clickhouse集群搭配多套Zookeeper集群.
2.2. 数据同步
Replication副本机制是异步且多Master的(多Master指在同一个集群上, 随意节点都可以, 无需在master节点上).INSERT查询(ALTER查询也是)可以被发送到当前集群的所有正常工作的节点上. 数据会被插入到运行INSERT语句的节点上, 然后被同步到同一分片的其他节点上. 因为这个数据同步操作是异步执行的, 其他节点上的数据会存在一定的延迟时间(这个时间由不同节点间传输压缩数据块的时间决定). 同步时如果遇到部分节点不可用(挂掉了), 会当这些节点重新可用时, 再写入. 对复制表进行数据同步时所使用的线程数可以通过background_schedule_pool_size参数来设置.
默认情况下, INSERT查询会等待写入当前副本的结果. 如果数据成功写入到当前副本但是这个副本所在的服务挂掉了, 那么写入的数据将会丢失. 如果INSERT查询想要确认写入该分片的所有副本的结果, 可以使用insert_quorum选项.
INSERT的原子性
一个INSERT插入不一定是原子性的, 这需要考虑到这个ISNERT查询被分成了多少个数据块, 但对于数据块来说, 是可以保证原子性的. 换句话说, 如果一个INSERT插入的条数小于max_insert_block_size设定的值, 那么这个INSERT查询就是原子性的.
INSERT的幂等性
数据块是遵循重复删除原则的, 对于同一数据块(大小相同, 条数相同, 数据的顺序相同)的多次写入, 实际只会被写入一次, 多余的次数会被丢弃. 这样做的原因是当由于网络故障, 客户端不知道数据是否成功被写入到Clickhouse中, 可以不考虑其他, 直接重复执行INSERT操作(就算多写了一次, 也会因为相同数据块而实际上没有写入到CK中). 这样做就保证了多次INSERT操作的幂等性.
2.3. 数据保障
在replication机制执行过程中, 只有被插入的源数据在各个节点间传输. 进一步的数据转换(Merge操作)是以同样的方式在所有的副本上协调和执行的. 这最大限度的降低了对于网络传输的压力, 意味着当Clickhouse集群跨越了多个数据中心时, 可以最大程度的提升replication机制的执行效率.
您可以对同一数据进行任意数量的复制。Yandex.Metrica在生产中使用双重复制。每台服务器使用RAID-5或RAID-6,在某些情况下使用RAID-10。这是一个相对可靠和方便的解决方案。
Clickhouse系统会监控每个副本上的数据一致性, 在发生故障后能够自动回复. 在不同副本数据差异较小的情况下, 故障转移是自动或半自动的, 但如果数据差异过大(例如表结构不同), 那么就需要去手动处理了.

