- 1. zookeeper的用途
- 配置管理
- 命名服务
- 分布式锁
- 集群管理
- 2. zookeeper之于clickhouse
- 2.1 为什么要使用zookeeper
- 2.2 zookeeper的使用原理
- 2.2.1 副本表的写入
- 2.2.2 分布式表的DDL *** 作
- 2.3 zookeeper的配置
- 3. 常见问题
- 3.1 Table is in readonly mode
- 4. 后记
对于大多数分布式软件而言,数据的一致性问题是其必须要考虑的内容,zookeeper是最常见的也是使用最广泛的一个解决数据一致性问题的工具,clickhouse在其分布式方案的设计中也引入了zookeeper的使用,并强依赖于此。本文根据最近的一些项目经验,从原理和实践上来简单总结一下zookeeper在clickhouse中的使用吧~~ 1. zookeeper的用途
zookeeper从存储的角度来看类似于一颗文件树,每个节点称之为znode,基本的工作原理类似于消息的发布订阅模式,客户端程序通过watch某个znode,既可以感知znode节点上内容的变化,也可以感知到该znode下子节点数量的变化。基于此衍生出了zookeeper的四大应用场景:
配置管理简单来讲就是利用znode节点来存储分布式软件的配置信息,所有的集群节点监听这些znode,当配置内容发生变更时,所有节点都可以感知到并加载更新后的配置。例如Hbase即是通过这种方式来获取到集群的配置信息。
命名服务命名服务有点儿类似于DNS,即在分布式应用中我们希望通过名称而非IP去访问服务,那么就可以借助zookeeper的znode来存储名称与IP的映射关系了。
分布式锁基于zookeeper实现分布式锁是利用了zookeeper的临时顺序节点的特性:每个微服务应用在访问某个需要加锁的对象的时候,都需要现在该节点下创建一个临时顺序节点,因为是有序的,每个线程可以拿到所有的节点做判断自己是不是最小的那个,如果是则获取锁,使用完之后释放锁(断开连接即可);如果不是则表示锁已经被抢占,则只需监听其上一个节点即可,如果上一个节点释放了锁,则该节点可继续获取到锁。使用网上别人画的一幅图吧,看着就比较清晰了。
集群管理因为可监听子节点数量的变化,因此常用来做集群节点状态的管理,当集群添加或移除节点时,其他节点都可以感知到这种变化。kafka就使用了zookeeper来用作consumer的上下线管理。
2. zookeeper之于clickhouse 2.1 为什么要使用zookeeper众所周知,clickhouse是一款单机性能强悍的数据库,但正所谓“双拳难敌四手,好汉架不住人多”,如果不支持横向扩展的话,那么clichouse相比其他分布式的MPP数据库就没有什么优势了,所以理所当然的,clickhouse引入了分布式集群的工作模式;那么既然是分布式软件,就需要考虑数据的一致性问题,在这种情况下,考虑使用zookeeper也就不足为奇了,尽管它可能不是最优的解决方案,但至少让clickhouse现在具备了“打群架”的能力了~~
2.2 zookeeper的使用原理TIPS:在clickhouse中,zookeeper不参与任何实质性的数据传输。
zookeeper在clickhouse中主要用在副本表数据的同步(ReplicatedMergeTree引擎)以及分布式表(Distributed)的 *** 作上。
2.2.1 副本表的写入以一个分片一个副本举例,每次新建一张表后,clickhouse会在zookeeper上创建一个目录,目录结构如图:
其中test01是库名,test12是表名;每张表下面的数据结构(挑一些主要的):
- metadata:记录的是该表的元数据信息,包括主键,分区键,索引粒度等;
- log:每次 *** 作记录,包括INSERT/MERGE等常规 *** 作
- columns:字段新仙尼
- leader_election:选举出来的leader信息
- replicas:副本信息,每个副本是一个子目录
- mutations:mutation *** 作的记录
以INSERT *** 作为例,假设R1和R2是两个副本名称,在R1节点上执行插入 *** 作,其核心流程如下:
- 在本地执行分区目录的写入,想zk的blocks目录下写入该分区的block_id
- 然后由副本R1向zk上的/log目录推送 *** 作日志,日志的内容如图:
表示 *** 作内容为get下载,需要下载的分区是202110152110_1327_1327_0 - R2会一直监听/log节点,监测到有日志变化,就会从log里读取任务,但不会立刻执行,而是将任务放置到自己目录下的队列里,这样设计时为了避免同时收到多个 *** 作请求的时候处理不过来的情况。
- R2基于/queue队列开始执行任务:向远端副本发起get请求,下载指定分区的数据
- R1相应请求,将分区数据返回给R2
- R2得到数据后在本地完成写入
同样先来看一下zk上的目录结构:
这次是在task_queue目录下,主要节点说明如下:
- active:保存当前集群内状态为active的节点
- finished目录:用于检查任务的完成情况,每个节点执行完毕后就会在该目录下写入一条记录。
以创建表为例说明一下整个流程:
- 在分片shard1上执行 *** 作CREATE TABLE ON CLUSTER,则shard1负责将该语句记录到zk上的ddl目录下,同时由这个节点负责监控任务的执行进度
- 分片shard1和shard2都在监听ddl/query-*的内容,当监听到有变化时,将日志拉取到本地,然后判断各自的host是否在hosts列表(zk上query节点的内容)中,如果在则执行该 *** 作,执行完成后将状态写入finished节点下;如果不包含,则忽略。
- shard1在执行语句后会阻塞等待所有节点都执行完成,如果指定超时时间(默认为180秒)内未完成,则转到后台线程继续等待。
在/etc/clickhouse-server目录下创建一个metrika.xml的配置文件(如果已存在,则直接往里面写入即可),新增内容:
10.10.1.20 2181 10.10.1.21 2181 10.10.1.22 2181
其中10.10.1.20~1.22是三个zk节点。
然后在全局配置config.xml文件中引入metrika.xml,并引用zookeeper配置的定义:
/etc/clickhouse-server/metrika.xml
注意:该文件中的incl里面的名称应该与metrika.xml文件中的节点名称保持一致。
3. 常见问题 3.1 Table is in readonly mode- 问题现象:在笔者的项目环境下,由于存在大数据量的数据写入(200MB/s以上的写入速度),时常会在clickhouse-server的日志中看到Table is in readonly mode的告警信息。
- 问题原因:翻阅资料后发现这其实是很多人都会遇到的一个问题,大体原因当然是和zk的处理性能相关,随着写入数据量的增多,zk的log和snapshot文件会不断膨胀,有时就会出现zk服务不可用的情况,而clickhouse的ReplicatedMergeTree又强依赖于zk,一旦zk不可用,为了保证数据的一致性,系统就会进行“写保护”,出现上述提示。
- 解决办法:
- 方案1:调整use_minimalistic_part_header_in_zookeeper参数; 网上有些朋友说是调整这个参数可以减少写入zk日志的数据量,但因为我们使用的是21的版本,此版本默认这个参数已经是开启了,因此这个方案对我们实际无效。
- 方案2:一个CH集群使用多个zk集群来提供服务; 这属于压力转移的策略,我们最后也无奈使用了这种办法,当然能解决问题,但带来的副作用就是zk集群运维管理成本的上升。
去zookeeper化现在似乎正在成为一股风潮,包括kafka在内的多个重量级分布式软件都在新版本中取消了对zookeeper的依赖,似乎clickhosue也在朝这方面努力,新的版本已经开始在试用clickhouse-keeper,一个用来替代zookeeper的方案,然后笔者注意到CH作者在最新的2022年Roadmap规划中,clickhouse-keeper满足生产环境的需求赫然在列。后面我们会跟进这方面的测试,看看是不是能够更加契合我们的使用场景。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)