ClickHouse副本同步及分布式DDL的原理
基本上所有的分布式存储系统都有一个共同的特点,将庞大的数据量分成多个小块存储在不同的机器上,通常称为分片,每个分片为了保证它数据不丢失,它们又有各自副本。ClickHouse也不例外,一起来看看ClickHouse是怎么实现的副本同步原理副本同步的原理其实我们在前面的篇幅中我们已经提到过,现在再用一张手画图复习一下简单来说它们的副本同步机制是通过Zookeeper的监听机制实现的,当我们向Node
相信了解大数据的小伙伴们都知道,基本上所有的分布式存储系统都有一个共同的特点,将庞大的数据量分成多个小块存储在不同的机器上,通常称为分片,每个分片为了保证它数据不丢失,它们又有各自副本。ClickHouse也不例外,一起来看看ClickHouse是怎么实现的
副本同步原理
副本同步的原理其实我们在前面的篇幅中我们已经提到过,现在再用一张手画图复习一下
简单来说它们的副本同步机制是通过Zookeeper的监听机制实现的,当我们向Node1发送写入操作请求,Node1会推送操作日志到zookeeper集群中,Node2通过监听发现Node1有新的数据写入,于是从zookeeper上下载操作日志,然后从Node1下载副本到本地。下面我们就动手实验一下,看看具体现象。
实操
创建副本实例
我们在server1下执行以下建表语句
CREATE TABLE user_order
(
`id` UInt32,
`uid` UInt32,
`price` Float64,
`create_time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/user_order', 'server1')
PARTITION BY toYYYYMM(create_time)
ORDER BY id
在server2下执行以下建表语句
CREATE TABLE user_order
(
`id` UInt32,
`uid` UInt32,
`price` Float64,
`create_time` DateTime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/user_order', 'server2')
PARTITION BY toYYYYMM(create_time)
ORDER BY id
解释一下上面执行的建表语句的含义,首先字段名和类型这里就不赘述了大家都懂,这里介绍以下我们上面使用的表引擎是ReplicatedMergeTree,该引擎会自动同步副本,MergeTree的话是ClickHouse的一大特色,它会将具有相同特征的数据进行合并,极大提高查询效率。使用该引擎需要传入两个参数,第一个参数是元数据在zookeeper的存在路径,第二个参数节点的名称。
执行完建表语句后,我们向server1插入一条数据,看看server2是否会自动同步
副本同步验证
在server1节点上插入数据
insert into table user_order values(2,101,3800,'2020-08-07 14:33:28');
登录server2验证同步副本数据:
由图可见,server2已经正常同步了server1的数据。下面我们来看看具体同步的原理。
副本同步原理
当我们在server1执行插入语句,第一个副本实例会推送log日志到zookeeper指定目录下,我们可以到zk上查看具体的日志信息,使用我们上一章节提到的知识点,zk的内容可以直接在ClickHouse查询到
select * from system.zookeeper where path='/clickhouse/tables/01/user_order/log';
可以清晰的看到,我们操作的事件,源副本所在的节点,以及数据所在的文件名,都能准确的呈现。
那么此时server2就监听到了/clickhouse/tables/01/user_order/log该路径下发生变化,便会触发日志的拉取并更新/clickhouse/tables/01/user_order/replicas/server2/log_pointer该路径下指示的日志下标,可以看到这里日志的下标是2,因为我之前插入过一条数据了,所以更新为2
副本选择策略
这里存在一个问题,当副本可以从多个不用节点下载时,会如何选择,这里ClickHouse有一个选择算法:
(1)首先server2会从/clickhouse/tables/01/user_order/replicas获取所有副本节点
(2)遍历所有副本后,选择其中一个。选择的策略是,选取log_pointer下标最大且/queue子节点数量最少的副本。log_pointer越大,说明该副本执行的日志最多,数据最完整;/queue子节点最少,说明该副本最空闲。
在整个插入数据过程中,zookeeper不会进行任何实质性数据传输。本着谁执行谁负责的原则,server1将数据写入本地分区之后,接着推送log日志,然后通知其他副本下载数据,其他副本接收到log日志后,会选择一个最合适的副本,实现点对点下载分区数据。server1接收到server2的调用请求后,server1会根据参数将本地分区202008_0_0_0发送给server2,server2写入本地磁盘。
存在问题
其实我们上面使用的这种方式有点挫,因为我们是在每个节点上手动创建每个副本表,如果我有成百上千个节点,是绝对不可能使用这种方式的,下面就给大家介绍分布式DDL的实操
分布式DDL实操
我们之前在metrika.xml中定义的集群信息在这里就派上用场了。
前置条件
先来看看我们metrika.xml中的集群信息
我们定义了集群名为cluster_1shards_1replicas,其中一个分片下有两个副本,分别是server1和server2。
以及宏变量
server1的宏变量
server2的宏变量
执行分布式DDL语句
有了以上前置条件之后,我们可以执行分布式DDL语句了
CREATE TABLE my_order_local ON CLUSTER cluster_1shards_1replicas(id UInt32,uid UInt32,price Float64,create_time DateTime) engine = ReplicatedMergeTree('/clickhouse/tables/{shard}/my_order','{replica}') PARTITION BY toYYYYMM(create_time) ORDER BY id;
这样我们的分布式DDL操作的执行完成了
原理
推送DDL日志
分布式DDL的原理其实和副本同步是类似的,**当我们在server1执行分布式DDL语句时,本着谁执行谁负责的原则,server1节点负责创建日志并将日志推送到zookeeper,同时也由serve1节点负责监控任务的执行进度。**推送的日志对应zookeeper的以下路径:
拉取日志并执行
看一下操作日志中都有什么内容,zk路径
get /clickhouse/task_queue/ddl/query-0000000001
可以看到这个日志中的信息包含版本号,执行的语句以及需要执行该语句的hosts列表。其它节点监听到此操作日志后,后判断自己是否在该hosts列表内,如果包含,则进入执行流程,执行完毕后写入
/clickhouse/task_queue/ddl/query-0000000001/finished
从上图我们可以看见finished下存在server1和server2,说明它们都已经执行完成了。
确认执行进度
在第一步客户端执行DDL语句之后,客户端会阻塞等待180秒,以期望所有节点都执行DDL完毕。如果等待事件大于180秒,则会转入后台线程继续等待。
这样我们的分布式表就创建成功了,是不是很简单。
今天ClickHouse副本同步及分布式DDL的原理和实操就到这里啦,下一篇讲给大家介绍分片的原理以及实操
微信公众号:喜讯Xicent
更多推荐
所有评论(0)