副本和分片类似双胞胎兄弟,提供两种方式区分:
分片之间的数据是不同的,副本之间的数据是完全相同的
。副本的主要目的是防止数据丢失
,增加数据存储的冗余;而使用分片的主要目的是实现数据的水平切分
。(1)只有使用了 ReplicatedMergeTree 复制表系列引擎,才能应用副本的能力。
(2)ReplicatedMergeTree 增加了 Zookeeper 部分,会进一步在 ZooKeeper 内创建一系列的监听节点,并以此实现实例之间的通信。
(3)ZooKeeper 不会设计表数据传输。
ReplicateMergeTree 与 MergeTree 的逻辑关系示意:
ReplicatedMergeTree 的定义方式如下:
ENGINE = ReplicatedMergeTree('zk_path', 'replica_name')
zk_path 用于指定 ZooKeeper 中创建的数据表的路径,一般命名格式为 /clickhouse/tables/{shard}/table_name
,{share} 表示分片编号,table_name 表示数据库表的名称。replica_name 是定义在 ZooKeeper 中创建的副本名称,是区分不同副本实例的唯一标识。
ReplicatedMergeTree 需要依靠 ZooKeeper 的事件监听机制以实现各个副本之间的协同,按照作用的不用,监听节点大致分为如下几类:
(1) 元数据:
metadata
: 保存元数据信息,包括主键、分区键、采样表达式等。columns
:保存列字段信息,包括列名称和数据类型。replicas
: 保存副本名称,对应设置参数中的 replica_name。(2) 判断标识:
leader_election
:用于主副本的选举工作,主副本会主导 MERGE 和 MUTATION 操作(ALTER DELETE 和 ALTER UPDATE)。这些任务在主副本完成之后再借助 ZooKeeper 将消息时间分发至其他副本。blocks
:记录 Block 数据块的 Hash 信息摘要,以及对应的 partition_id。通过 Hash 摘要能够判断 Block 数据块是否重复;通过 partition_id 则能够找到需要同步的数据分区。quorum
:记录 quorum 的数量,当至少有 quorum 数量的副本写入成功之后,整个操作才算成功。quorum 的数量由 insert_quorum 参数控制,默认值为 0.(3) 操作日志:
log
:常规操作日志节点(INSERT、MERGE 和 DROP PARTITION),它是整个工作机制中最为 重要的一环,保存了副本需要执行的任务指令。mutations
:MUTATION 操作日志节点,作用与 log 日志蕾西,当执行 ALTER DELETE 和 ALTER UPDATE 时,操作指令会被添加到这个节点。replicas/(replica_name)
/*: 每个副本各自的节点下的一组监听节点,用于指导副本在本地执行具体的任务指令,其中较为 重要的节点如:queue
:任务队列节点,用于执行具体的操作任务。当副本从/log或/mutations节点监听到操作指令时,会将执行任务添加至该节点下,并基于队列执行。log_pointer
:log日志指针节点,记录了最后一次执行的log日志下标信息。mutation_pointer
:mutations日志指针节点,记录了最后一次执行的mutations日志名称。ReplicatedMergeTree 在 ZooKeeper 中有两组非常重要的父节点,那就 /log 和 /mutations。它们的作用是分发操作指令的信息通道,而发送指令的方式,则是为这些父节点添加子节点。/log 和 /mutations 具体实现对象为 LogEntry 和 MutationEntry。
(1) LogEntry
source replica:
发送这条 Log 指令的副本来源,对应 replica_name。
type:
操作指令类型,主要有 get、merge 和 mutate 三种,分别对应从远程副本下载分区、合并分区和 MUTATION 操作。
block_id:
当前分区的 BlockID,对应 /blocks 路径下子节点的名称。
partition_name:
当前分区目录的名称。
(2) MutationEntry
source replica:
发送这条 MUTATION 指令的副本来源,对应 replica_name。
commands:
操作指令,主要有 ALTER DELETE 和 ALTER UPDATE。
mutation_id:
MUTATION 操作的版本号。
partition_id:
当前分区目录的 ID。
副本协同的核心流程主要有 INSERT、MERGE、MUTATION 和 ALTER 四种,分别对应了数据写入、分区合并、数据修改和元数据修改。使用 ReplicatedMergeTree 实现一张拥有 1 分片、1 副本的数据表,来了解相应的工作原理。
主要的过程分为三步:
(1)由执行了 INSERT 操作的副本向 /log 节点推送操作日志
(2)副本会一直监听 /log 节点变化,拉取 LogEntry,将其转为任务对象放至 /queue 队列
(3)基于 /queue 队列开始执行任务,会选择一个远端副本作为数据的下载来源。选择拥有最大的 log_pointer(执行日志最多),/queue 子节点数量最少(该副本目前的任务执行负担较小)。然后建立起连接开始下载。
无论 MERGE 操作从哪个副本发起,其合并计划都会由主副本来执行(zk 副本选举产生)。
主要步骤如下:
(1)执行 OPTIMIZE 触发 MERGE 合并
(2)创建远程连接进行主副本通信
(3)主副本制定 MERGE 计划并推送 Log 日志
(4)各个副本监听 /log 日志的推送,分别拉取知道到本地,并推送到各自的 /queue 任务队列
(5)各个副本分别在本地执行 MERGE
特别注意:
在 MERGE 的合并过程中,ZooKeeper 也不会进行任何实质性的数据传输,所有的合并操作,最终都是由各个副本在本地完成的。
无论 MUTATION 操作从哪个副本发起,其合并计划都会由主副本来执行(zk 副本选举产生)。当进行 ALTER DELETE 或者 ALTER UPDATE 操作的时候会进入 MUTATION 部分的逻辑。
主要步骤如下:
(1)执行 DELETE(UPDATE 效果与此相同) 触发 MUTATION 操作
(2)所有副本实例各自监听 MUTATION 日志
(3)由主副本 实例响应 MUTATION 日志并推送 Log 日志
(4)各个副本监听 /log 日志的推送,分别拉取知道到本地,并推送到各自的 /queue 任务队列
(5)各个副本分别在本地执行 MUTATION
ALTER 操作进行元数据修改的时候,即会进入 ALTER 部分的逻辑,例如增加、删除表字段等。
(1)修改共享元数据
(2)监听共享元数据变更并各自执行本地修改
(3)确认所有副本完成修改
语法形式如下:
CREATE/DROP/RENAME/ALTER TABLE ON CLUSTER cluster_name
其中,cluster_name 对应了配置文件中的集群名称,ClickHouse 会根据集群的配置信息分别去各个节点执行 DDL 语句。
/clickhouse/task_queue/ddl
(1)推送 DDL 日志:谁执行谁负责推送
(2)拉取日志并执行:拉取日志到本地。
(3)确认执行进度:步骤 1 执行后,客户端会阻塞180秒,等待所有 host 执行完毕。
ENGINE = Distributed(cluster, database, table [,shaeding_key])
建表语句示例:
CREATE TABLE test_shard_2_all ON CLUSTER sharding_simple ()
ENGINE = Distributed(sharding_simple, defalult, test_shard_2_local, rand())
多副本路由:randon(默认负载均衡算法)、nearest_hostname(错误最少 replica)、in_order(错误最少 replica 中的按定义逐个选择)、first_or_random(错误最少首选第一个 replica) 四种方式
使用Global优化分布式子查询
(1) 使用本地表的问题
SELECT uniq(id) FROM test_query_all WHERE repo = 100 AND id IN (SELECT id FROM test_query_local WHERE repo = 200)
(2)扫的本地表里刚好没有这个数据,有希望在全局里找
(3)使用分布式表又会有查询放大的问题,每次扫all都是全局广播,就会变成指数增长
SELECT uniq(id) FROM test_query_all WEHRE repo = 100 AND id IN (SELECT id FROM test_query_all WHERE repo = 200)
(4)所以有一个 GLOBAL
关键字,可以将中间过程缓存