复制的注意点
目前只有合并树系列的表 具有复制的功能。
复制的实现原理
clickhouse的复制 是基于 zookeeper来实现的。zookeeper 在clickhouse复制 的实现之中 扮演了 元数据存储、日志框架、分布式协调服务 三重角色。 每个clickhouse 节点都会监视 zookeeper /clickhouse/tables/分片号/数据库名/表名/log 路径下的节点信息。当向一个节点的可复制表写入数据时,节点会向 /clickhouse/tables/分片号/数据库名/表名/log 目录下添加任务节点。另一个副本节点监控到变化,执行fetch操作(在zookeeper之中的数据变化,这里忽略),从对应的节点拉取数据。
fetch
fetch 的定义
复制表的一个副本从另一个副本 通过http协议 克隆数据,这个过程被称为fetch。
fetch 的实现
每个clickhouse 节点 专门维护一个线程池 用以执行fetch操作。fetch的实质 是通过http协议下载数据。
决定 fetch 线程池大小的参数
background_fetches_pool_size
clickhouse 节点 暴露的用于执行fetch的端口是哪个
config.xml 文件之中的配置参数
<interserver_http_port>9009</interserver_http_port>
通过http fetch的时候,接收方的超时时间
replicated_fetches_http_receive_timeout 参数决定,是合并树表级别的配置参数。
通过http fetch的时候,发送方的超时时间
replicated_fetches_http_send_timeout
每个节点的fetch 并发度限制
replicated_max_parallel_fetches_for_host
限制 fetch 收 发 消耗的网络带宽大小
max_replicated_sends_network_bandwidth_for_server: 服务器级别的设置(限制所有表的总量) max_replicated_fetches_network_bandwidth_for_server:服务器级别的设置(限制所有表的总量) max_replicated_fetches_network_bandwidth : 表级别的设置 max_replicated_sends_network_bandwidth : 表级别的设置
fetch 操作会占据的资源
带宽 cpu 内存。 主要需要注意的是带宽。
replica 在 zookeeper 的清理机制
- max_replicated_logs_to_keep:当一个副本节点挂掉的时候,zookeeper的log队列最多保持多少个任务节点。
- min_replicated_logs_to_keep:在log队列之中最少保持多少个任务节点。即使任务节点已经被执行,但是还是需要保持配置数量的节点数。
- cleanup_delay_period:合并树级别的配置参数,决定了后台执行复制表队列的清理周期。 如果发现队列之中的节点数过多的话,可以稍稍调小一下参数,默认的取值为30s。
复制表下的合并
主要思考的是,两个数据相同的副本,是各自进行合并呢,还是一个合并完成了,另一个fetch合并完成之后的数据。配置参数主要决定这个逻辑。
- 1:execute_merges_on_single_replica_time_threshold 参数决定副本开始合并的时间。假设有两个副本,一个副本开始合并,那么另一个副本是需要fetch 合并后的数据呢,还是自己也执行合并。所以就取决于此参数 execute_merges_on_single_replica_time_threshold的值。取值为0,表示每个副本自己触发合并。当取值为某一个整数时,表示为副本A开始执行合并,副本B等待一段时间,如果副本A的合并执行完成,那么副本B通过fetch获取数据,否则副本B自己本地开始执行合并。同时如果节点上的复制执行时间超过了 prefer_fetch_merged_part_time_threshold 参数的取值,并且需要合并的数据超过了 prefer_fetch_merged_part_size_threshold 参数的值,那么clickhouse会用fetch来代替 本地merge。
可复制表的写入
配置参数主要决定 对于一个可复制表的写入操作 在什么标准下才能成功。
- insert_quorum_timeout:一次写入的超时时间。
- insert_quorum: 可复制表写入的数据需要在几个副本存在才能算成功。
- insert_quorum_parallel: 可复制表是否允许并发写入(之前的写入尚未完成的情况下,继续写入)
复制表下的查询
对于复制表而言,由于存在副本,查询的时候选择哪一个副本作为查询的数据来源。配置参数主要决定的是这一段逻辑。
- max_replica_delay_for_distributed_queries:当执行查询的时候,会选择某个副本进行数据查询,这是一个选择的标志,落后多久的副本仍然可以作为查询的数据来源。
- fallback_to_stale_replicas_for_distributed_queries:当最新的副本不可用的时候,强制使用过时的副本进行查询,保证查询可行。
- select_sequential_consistency:只有insert_quorum_parallel被禁用的时候,才有效。查询只会从包含之前所有写入的数据的副本之中选取。
复制表相关的系统表
system.replicas system.replicated_fetches system.replication_queue
复制的监控
- 各节点配置的 fetch 任务队列长度
- 各节点正在执行的fetch任务数
- 各节点正在执行的fetch任务的信息,根据耗时时间进行排序
- 节点后台等待执行的fetch 任务数
- 节点后台等待执行的fetch 任务详细信息,根据等待时间倒序排序
- 重试次数大于 指定限制的 fetch 任务数
- 重试次数大于 指定限制的 fetch 任务的详情
- 后台part fetch 失败的次数
- 查看fetch 的繁忙程度
各节点配置的 fetch 任务队列长度
select
hostName() as hostName,
value
from clusterAllReplicas('default', 'system.settings')
where name = 'background_fetches_pool_size'
order by hostName;
各节点正在执行的fetch任务数
select
hostName,
count(*) as num
from (
select
hostName() as hostName,
database,
table
from
clusterAllReplicas('集群名', 'system.replicated_fetches')
) group by
hostName
各节点正在执行的fetch任务的信息,根据耗时时间进行排序
select
hostName() as hostName,
result_part_name,
source_replica_path,
source_replica_hostname,
source_replica_port,
database,
table,
elapsed,
progress,
total_size_bytes_compressed, --fetch所要读取的所有的数据量
bytes_read_compressed, -- 已经读取的数据量
thread_id,
to_detached
from
clusterAllReplicas('default', 'system.replicated_fetches')
order by elapsed desc
节点后台等待执行的fetch 任务数
select
hostName() as hostName,
count(*) as num
from
clusterAllReplicas('default', 'system.replication_queue')
where
type = 'GET_PART'
and is_currently_executing = 0
order by hostName
节点后台等待执行的fetch 任务详细信息,根据等待时间倒序排序
select
hostName() as hostName,
toUnixTimestamp(now()) - toUnixTimestamp(create_time) as task_wait_time,
source_replica, --来源副本名
replica_name, -- 目标副本名
position, --任务位置
node_name, --任务节点名
database, -- 数据库名
table, -- 表名
new_part_name, -- 生成的副本的名字
create_time, --任务创建时间
num_postponed, -- 推迟次数
postpone_reason, -- 推迟原因
last_postpone_time --上次推迟时间
from
clusterAllReplicas('default', 'system.replication_queue')
where
type = 'GET_PART'
and is_currently_executing = 0
order by task_wait_time desc
重试次数大于 指定限制的 fetch 任务数
select
hostName,
count(*) as num
from (
select
hostName() as hostName,
replica_name
from
clusterAllReplicas('集群名', 'system.replication_queue')
where
num_tries > 设置的重试次数
and type = 'GET_PART'
)
where hostName in (${hostName})
group by
hostName
重试次数大于 指定限制的 fetch 任务的详情
select
*
from (
select
hostName() as hostName,
num_tries,
last_exception,
last_attempt_time,
source_replica,
replica_name,
database,
table,
new_part_name,
node_name,
position,
create_time
from
clusterAllReplicas('集群名', 'system.replication_queue')
where
num_tries > 重试次数
and type = 'GET_PART'
)
where
order by num_tries,create_time desc
limit 30
后台part fetch 失败的次数
select * from system,events where event = 'ReplicatedPartFailedFetches'
查看fetch 的繁忙程度
通过查看任务还未执行的延时时间,如果出现有的任务很久之前已经提交了,但是很久未被执行,可以来反应
select
hostName() as hostName,
toUnixTimestamp(now()) - toUnixTimestamp(create_time) as task_delay,
*
from
clusterAllReplicas('集群名', ' system.replication_queue ')
where is_currently_executing = 0
and type = 'GET_PART'
order by
task_delay desc limit 10