ClickHouse使用(十三)
Merge 的核心流程
当ReplicatedMergeTree 触发分区合并动作时,即会进入这个人部分的流程
1.(CH6节点)创建远程连接,尝试与主副本通信)创建远程连接,尝试与主副本通信
2.(主副本接收来自(CH6的)通信
3.由主副本(CH5)制定Merge计划并推送Log日志(主副本判断哪些分区需要合并,选定后,将合并计划转换为Log日志)通知所有副本开始合并内容 type:merge 201905_0_0_0 201905_1_1_0 into 201905_0_1_1
4.各个副本分别拉取Log日志
CH5 和 CH6 副本实例分别监听并拉取日志到本地,并推送到各自的 queue 队列
5.各个副本分别在本地执行merge
副本在本地执行自己的任务队列
Mutation的核心执行流程
ReplicatedMergeTree 执行 alter delete 或者 alter update操作的时候,就会执行 mutation操作
1.推送mutation日志
1、创建 mutation id
2、将mutation操作转换成MutationEntry日志 推送到//mutations/000000000000.
MutationEntry 的核心属性如下
source replica :CH5
mutaion_id: 2
partition_id: 201905
commands: DELETE WHERE ID =’\1’
从上面的第一个属性可以知道 该操作是 从 CH6节点发到其他各个节点的
2.所有副本实例各自监听Mutation日志
CH5 和 CH6 都会监听 /mutations 节点,再加入新的日志子节点时,能够实时感知,在同步操作前会判断自己是否为主节点,只有主节点响应mutation日志
3.由主副本实例响应 Mutation日志并推送log日志
将mutation日志转换成logEntry,并推送到 /log 节点,通知每个副本执行具体操作
例如:
/log/log-000000003
source replica: ch5
block_id:
type: mutate
201905_0_1_1
to
201905_0_1_1_2
操作为将201905_0_1_1 分区 修改为 201905_0_1_1_2(201905_0_1_1+"_"+mutation_id)
4.每个副本实例分别拉取log日志
监听 /log/log-0000000003,拉取到本地,并推送到本地的/queue 任务队列
5.各个副本实例分别在本地执行mutation
CH5和CH6基于各自的 /queue 队列开始执行任务
1、执行复制 201905_0_1_1 to temp_clone_2019_0_1_1_2
2、重命名 temp_clone_2019_0_1_1_2 to 2019_0_1_1_2
6.总结
mutation 操作由副本触发,主副本 监听到后,转换成 logEntry,发送到 log 节点;
每个副本监听到log 后,在本地队列中加入mutate 操作;
最后完成执行mutate操作。
alter的核心流程
1.修改共享元数据
例如:alter table replicated_scals_1 add Column id2 String
CH6 节点修改 zk 元数据,共享版本号增长,同时负责监听所有副本的修改情况
2.监听共享元数据变更并各自执行本地修改
CH5 和 CH6 两个副本分别监听共享元数据的变更,比较本地版本号<共享版本号,开始各自执行本地的更新操作
3.确认所有副本完成修改
CH6 确认所有副本完成修改
数据分片
ClickHouse的数据分片需要结合Distributed表引擎一同使用
1.集群的配置方式
1.不包含副本的分片 ch5 9000 ch6 9000说明:shard_2表示自定义的集群名称,全局唯一
node:指定分片节点,不包含副本
host:节点服务器地址
port:端口号
weight:权重默认为1
user:用户 默认 default
password:用户密码
secure:ssl连接的端口,默认9440
compression:是否开启数据压缩功能 默认为true
基于集群实现分布式DDL
1、Create/Drop/rename/alter table on cluster cluster_name(对应配置文件中的集群名称)
2、Create table test1 on cluster shard_2(id uint32)engin=replicatedMergeTree(’/clickhouse/tables/{shared}/test_1’,’{replica}’)
注意:用 {shared} 和 {replica} 两个动态宏变量代替了硬编码
执行 select * from system.macros 可以看到宏变量定义
replica ch5
shard 01
select * from remote(‘ch6.naa.com:9000’,‘system’,‘macros’,‘default’)
replica ch6
shard 02
这些变量是通过配置文件定义的,在config.xml 中的
数据结构
与ReplicatedMergeTree 类似,分布式DDL 需要借助zk
(1)zk内节点结构
config.xml
中的 <distributed_ddl>配置,如/clickhouse/task_queue/ddl
此下面的子节点包含:
/query-[seq] 操作日志:如/query-00000000001
此下面的子节点包含:状态节点
/query-[seq]/active 在任务执行过程中,该节点下会临时保存当前集群内状态为active的节点
/query-[seq]/finishd 在任务执行过程中,每当在一个节点执行完成后,就会在该节点下写入记录
(2)DDLLogEntry 日志对象的数据结构
在 /query-[seq]下记录的日志信息由DDLLogEntry承载
(2.1)记录了查询执行的语句 如:drop table 。。。。on cluster shard_2
(2.2)host 记录了指定集群的主机列表 hosts:[‘ch5.nuaa.com:9000’,‘ch6.nuaa.com:9000’]
(2.3)initiator 记录了初始化host主机的名称 等价于 select host_name form remote (‘ch5.nuaa.com:9000’,‘system’,‘clusters’,‘default’)
where cluster = ‘shard_2’
结果:
host_name
ch5.nuaa.com
ch6.nuaa.com
分布式DDl的核心执行流程
1.推送DDL日志
例如:
(1)在ch5 上执行 create table on cluster,谁执行谁负责,ch5节点负责创建DDLEntry 日志并将日志推送到zk,并监控执行进度
(2)拉取日志,并执行:CH5和CH6两个节点分别监听/ddl/query-00000000001,日志推送
判断host是否在DDLLogEntry的hosts列表中,如果在则执行ddl,不在则直接忽略,执行完毕后,将状态 写入 finished节点。
(3)确认执行进度:在步骤1执行DDL语句之后,客户端会阻塞等待180秒,期待所有的host执行完成,如果等待大于180,则转入后台线程,继续等待(等待时间由distributed_ddl_task_timeout 参数指定 默认180秒)
2.Distributed 原理解析
Distributed表引擎是分布式表的代名词,自身不存储任何数据,作为数据分片的代理,自动路由数据到集群中的各个节点;
Distributed 表引擎需要和其他数据表引擎一起协同工作
组成
本地表:除了Distributed表引擎外的其他任何引擎(1、Table_Local)(2、Table_Local),用于实际存储数据
分布式表:Distributed 表引擎 (Table_All) 与本地表形成1对多的映射关系,之后将通过分布式表代理操作多张本地表
结构上一致,采用的是,查询时检测,即:结构不一致问题在查询时抛出异常。
1.定义形式
Engine=Distributed(cluster,database,table[,sharding_key])
cluster:集群名称,和集群配置中的自定义名称相对应(如:app1_cluster),需要通过名称找到对应的host列表。
database和table:分别对应数据库和表的名称,分布式表使用这组配置映射到本地表。
sharding_key:分片键,选填参数,分布式表会依据分片键的规则,将数据分不到各个host节点的本地列表
例如:
(1)创建Distributed表
create table test_shared_2_all on CLUSTER sharding_simple(
id UInt64
)ENGINE=Distributed(sharding_simple,default,test_shard_2_local,rand())
1、写入会根据rand() 随机函数的取值决定数据写入那个分片;
2、Distributed表运用的是读时检查机制,对创建分布式表和本地表的顺序没有强制要求
3、在集群的每个分片上,都会创建Distributed表,这样可以再任意节点发起对所有分片的读、写请求
(2)创建本地表
create table test_shared_2_local on CLUSTER sharding_simple(
id UInt64
)ENGINE=MergeTree() order by id partition by id
2.查询分类
1会作用于本地表的查询,以分布式的方式作用于local本地表
2.只会影响Distributed自身,不会作用于本地表查询
元数据操作:create,drop,rename,alter,alter不包含分区操作;
attach partition,replace partition 只修改Distributed 表自身,不会修改local 本地表;
如果想彻底删除分布式表和本地表,需要执行两种表的drop 语句。
3.分片规则
分片键要求是一个Int 或者 UInt
例如:具体的整形字段,或者返回整形的表达式
注意:如果不声明分片,则只有一个分片
1、分片权重:weight 默认值为1,可以设置任意值,官方建议尽可能设置较小的值,否则数据会较多的写入到该分片。
2、slot 槽
slot的总数和weight 的总数相等,每个slot和分片通过weight建立对应关系,例如 A,B两个分片;(weight为 10,20),则slot数量为30;
3、选择函数
(1)找出slot的取值 slot = shard_value%sum_weight;shard_value 分片键的取值(整数值)sum_weight(slot的总数/两个分片权重的总数)
(2)找到对应的数据分片;【0,10)【10,30)当slot 为10时,数据分配到第二个区间
4、分布式写入的核心流程
(1)外部计算系统,事先将数据均匀分片,再借助由计算系统直接将数据写入clickHouse集群的各个本地表(主要依靠外部系统)
(2)通过Distributed 表引擎代理写入分片数据
(2.1)分片写入的核心流程
(2.1.1)在第一个分片节点写入本地分片数据。1:根据分片规则划分数据;2:将属于当前分片的数据直接写入本地表
(2.1.2)第一个分片建立远端连接,准备发送远端分片数据。1:根据远端分片的数量,创建并写入属于远端分片的数据到临时文件;2:与远端的分片建立连接
(2.1.3)第一个分片向远端分片发送数据。有一组监听任务,负责监听临时目录下的文件,并将这些文件发送到远端分片
(2.1.4)第二个分片接收数据并写入本地。
(2.1.5)第一个分片确认写入完成
(2.2)副本复制的核心流程
(2.2.1)通过Distributed复制数据。Distributed同时负责分片和副本的数据写入工作,副本数据的写入流程和分片逻辑相同。(会成为单点瓶颈)
(2.2.2)通过ReplcatedMergeTree复制数据。副本数据的同步由ReplicatedMergeTree负责
(2.3)分布式查询的核心流程
数据查询只能通过Distributed表引擎实现,接收到查询请求,会一次查询每个分片数据,再合并汇总返回。
(2.3.1)多副本的路由规则;会根据负载均衡算法选择路由规则。
1.随机random,默认算法;
2.nearest_hostname error_count 错误数量最少的副本,名称最相似的副本
3.in_order 错误数量最少的副本,按照顺序选择副本
4.first_or_random 错误数量最少的副本,选择第一个
(2.3.2)多分片查询的核心流程
分布式查询同样是谁执行谁负责的原则,会由接收select 查询的Distributed表,负责串联整个过程。
(2.3.2.1)针对分布式表的sql,按照分片数量将查询分成多个针对本地表的子查询。然后向分片发起查询,最后汇总查询结果并返回。
(2.3.3)使用Global优化分布式子查询
(2.3.3.1)使用分布式架构查询时In(或者join)子句遇到的问题
1、使用本地表(查询只在但分片执行,数据不全)
2、使用分布式表查询(查询请求会被放大N的平方倍,如10个分片,每个分片的子查询,都会在10个分片上被执行,总共执行100次)
3、使用Global优化 select uniq(id) from test_query_all where repo=100 and id global in (select id from test_query_all where repo=200)
查询过程分为5步骤:1.将in子句单独提出,发起了一次分布式查询。2.将分布式表转local本地表后,分别在本地和远端分片执行查询。3.将in子句查询的结果进行汇总
,并放入一张临时的内存表进行保存。4.将内存表发送到远端分片节点。5.分布式表转为本地表后,执行完整的sql,in子句直接使用临时内存的数据
缺点:临时表会在网络间传输,不能过大,否则网络会成为瓶颈。