ClickHouse的副本协同以及分布式DDL实现

842 阅读7分钟

ClickHouse的副本协同以及分布式查询是借助zk实现的,首先介绍下zk的目录结构

zk目录结构

zk在系统表中,提供了一张名为zookeeper的代理表,需要指定路径去查询。在定义ReplicatedMergeTree时,定义的zk_path路径一般为 /clickhouse/tables/{shard}/table_name ,这也是查询zk的路径。

select name,value from system.zookeeper where path='/clickhouse/'
┌─name───────┬─value─┐
│ tables     │       │
│ task_queue │       │
└────────────┴───────┘
select name,value from system.zookeeper where path='/clickhouse/tables/cdncld_raw_log/01/'
┌─name───────────────────────┬─value──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ metadata                   │ metadata format version: 1
date column: 
sampling expression: 
index granularity: 8192
mode: 0
sign column: 
primary key: msec
data format version: 1
partition key: date
ttl: date + toIntervalDay(7), date TO VOLUME 'hot', date + toIntervalHour(12) TO VOLUME 'cold'
granularity bytes: 10485760
 │
│ temp                       │                                                                                                                                                                                                                                                            │
│ mutations                  │                                                                                                                                                                                                                                                            │
│ log                        │                                                                                                                                                                                                                                                            │
│ leader_election            │                                                                                                                                                                                                                                                            │
│ columns                    │ columns format version: 1
82 columns:
`time_iso8601` String
`msec` Float64
`request_id` String
 │
│ blocks                     │                                                                                                                                                                                                                                                            │
│ nonincrement_block_numbers │                                                                                                                                                                                                                                                            │
│ replicas                   │ last added replica: xxx.com                                                                                                                                                                                                        │
│ quorum                     │                                                                                                                                                                                                                                                            │
│ block_numbers              │                                                                                                                                                                                                                                                            │
└────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

select name,value from system.zookeeper where path='/clickhouse/tables/cdncld_raw_log/01/replicas/xxx.com/'
┌─name────────────────────────┬─value──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
 is_lost                      0                                                                                                                                                                                                                                                          
 metadata                     metadata format version: 1
date column: 
sampling expression: 
index granularity: 8192
mode: 0
sign column: 
primary key: msec
data format version: 1
partition key: date
ttl: date + toIntervalDay(7), date TO VOLUME 'hot', date + toIntervalHour(12) TO VOLUME 'cold'
granularity bytes: 10485760
 
 is_active                    pid: 12005, random: 8517042718945254648                                                                                                                                                                                                                    
 mutation_pointer                                                                                                                                                                                                                                                                        
 columns                      columns format version: 1
82 columns:
`time_iso8601` String
`msec` Float64
`request_id` String
 
 max_processed_insert_time    1696750235                                                                                                                                                                                                                                                 
 flags                                                                                                                                                                                                                                                                                   
 log_pointer                  137394089                                                                                                                                                                                                                                                  
 min_unprocessed_insert_time  1688372142                                                                                                                                                                                                                                                 
 host                         host: xxx.com
port: 9009
tcp_port: 9000
database: default
table: cdncld_raw_log
scheme: http
                                                                                                                                             
 parts                                                                                                                                                                                                                                                                                   
 queue                                                                                                                                                                                                                                                                                   
 metadata_version             0                                                                                                                                                                                                                                                          
└─────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

目录结构解析参考:blog.csdn.net/weixin_3999…

副本协同流程

副本之间的协同,包括:主副本选举,副本状态感知,操作日志分发,任务队列和BlockID去重判断。

操作日志的目录

  • /log: 常规操作日志节点(INSERT、MERGE和DROP PARTITION),它是整个工作机制中最为重要的一环。保存了副本需要执行的任务指令。log使用了Zookeeper的持久顺序型节点,每条指令的名称以log-为前缀递增,利用log-0000000000、log-0000000001等。每一个副本实例都会监听/log节点,当有新的指令加入时,它们会把指令加入副本各自的任务队列,并执行任务。

  • /mutations: MUTATION操作日志节点,作用与log日志类似,当执行ALTER DELETE和ALTER UPDATE查询时,操作指令会被添加到这个节点。mutations同样使用了Zookeeper的持久顺序型节点,但是它的命名没有前缀,每条指令直接以递增数字的形式保存,例如0000000000、0000000001等。

  • /replicas/{replica_name}/*: 每个副本各自的节点下的一组监听节点,用于指导副本在本地执行具体的任务指令,其中较为重要的节点有如下几个:

    • /queue:任务队列节点,用于执行具体的操作任务。当副本从/log或/mutations节点监听到操作指令时,会将执行任务添加到该节点下,并基于队列执行

    • /log_pointer:log日志指针节点,记录了最后一次执行的log日志下标信息,例如log_pointer:4对应log/log-0000000003

    • mutation_pointer:mutations日志指针节点,记录了最后一次执行的mutations日志名称,例如mutation_pointer:0000000000对应了/mutations/0000000000。

INSERT的核心执行流程

image.png 在写入数据时首先完成本地分区的写入:

2023.10.08 16:23:31.460517 [ 2913 ] {} <Trace> system.query_thread_log (3931f3f0-2a3d-4f59-
88bc-fb82d704936f): Renaming temporary part tmp_insert_202310_1838766_1838766_0 to 
202310_1943328_1943328_0.

接着向/blocks节点写入该分区的block_id,这个block_id作为后续去重的判断依据。

分布式DDL

分布式DDL在ck中的存储路径为:/clickhouse/task_queue/ddl,每执行一次分布式查询,就在这个目录下增加一条日志,前缀为query-xxx,序号递增。下面一级目录 /active 存储尚未执行完成此任务的节点host,/finished 存储已经执行完成此任务的节点host。当各个节点监听到有新日志时,就执行操作。
操作流程图如下: image.png

www.jianshu.com/p/cefe513c2…

分布式DDL中的超时问题

image.png 这个语句会执行超时,后续的DDL执行也会卡住。

image.png 此时运维同学可能会执行以下操作;

DDL的原理

cgi-bin_mmwebwx-bin_webwxgetmsgimg_&MsgID=4188786979680784956&skey=@crypt_61099625_d9857bb86414c206671e5a52d9612e26&mmweb_appid=wx_webfilehelper.jpeg 依托zookeeper 将任务从 DDL queue 到 shard 到 replica 一级一级传递下去执行:

  1. 判断是分布式 DDL,在 zookeeper 中创建 ddl task 和子目录并同步到所有的 shard
  • /distributrd_ddl/queue-000xxx
  • /distributed_ddl/queue-000xxx/active
  • /distributed_ddl/queue-000xxx/finished
    目录创建完成后,阻塞 getDistributedDDLStatus 进程等待获取/distributed_ddl/queue-000xxx/finished 目录下的各个节点执行的完成信息,在这里默认是 180 秒超时,如果在 180 秒内没有获取到完成信息,则进入后台指令模式,提示超时。
  1. alter 操作的时候,在当前 shard 下 replica 对应zookeeper 目录下的 log 目录下写入一个更改 matedata 的任务 /{zk_path}/log/log-00000xxx,同时,如果操作涉及数据变更 mutation,会在当前 shard 下 replica 的 zookeeper 目录下的 mutations 下创建一个任务 /{zk_path}/mutations/0000xxx,(以上任务 ID 都为递增 ID,ID 小的先执行,保证任务的顺序性),创建完成任务后,由各个 replica 去主动拉取 zookeeper 中的副本间任务到本地并执行,此时进程阻塞,在 waitForLogEntryToBeProcessedIfNecessary 逻辑中监控 shard 对应 replica 目录下 log_pointer 和 mutation_pointer 是否大于当前提交的指针 log_0000xxx 和 oooooxxx,以及 queue/queue-yyy 是否消失,在都为是的情况下代表 replica 任务执行完成。
  2. 创建副本间任务后,每个 replica 中的 queueUpdatingTask 任务会通过 pullLogsToQueue 逻辑将副本间任务 /{zk_path}/log/log-00000xxx 同步到自己的副本目录 /{zk_path}/replicas/{replica}/queue/queue-0000yyy,移动/{zk_path}/replicas/(replica)/log_pointer,同时将 /{zk_path}/mutations/0000xxx 记录到内存中,当 mutation 积累一定量后,由副本中的 leader 角色节点的 mergeSelectingtask 任务选择 parts 做 merge 或者选择 part 做 mutation,选择完成后,创建副本间任务 /{zk_path}/log/log-00000xxx,然后再通过副本queueUpdatingTask 拉取此任务到/{zk_path}/replicas/{replica}/queue/queue-0000yyy,进入处理队列中,最后由各个副本的后台线程池执行,执行完成删除 /{zk_path}/replicas/{replica}/queue/queue-0000yyy。

阻塞的原因

DDLWorker是串行执行的,因此当前一个任务未执行完成时,后面所有的任务都会堵塞

image.png

image.png 看到当 pool_size 大于 1 的时候 clickhouse 是支持线程池的方式运行 DDL,但是在源码中,pool_size 默认是 1,也即没有开启线程池模式,这是为了保证 DDL 语句在执行时候的顺序性,如果 pool_size 修改为大于 1,这个时候,DDL 不再保持顺序性,执行结果不再可控,或许多个 DDL 语句执行下来,结果并不如预期,只有在对 DDL 顺序执行不关注的业务中,才可以开启 clickhouse 线程池运行 DDL。

如何避免

在 DDL 操作中要明确操作对象和操作范围,减少不必要的 IO 开销,提高 DDL 操作的效率,减少堵塞。

image.png 对该表做如图所示 delete 删除操作时,涉及所有字段,此时涉及到的文件要重新写一遍,io 开销很大;而 drop操作和 update 操作,只涉及到某一列(即文件)的数据,drop 操作(删除 C2 列)只涉及到 c2 列,update 操作(对 C4 中某一个字段做更新)对 C4 列只涉及到 C4 列,其余列文件通过 hard link 继续使用而无需其余操作,IO 开销较小。

运维操作

  1. 监控警报
  • 查询 clickhouse 进程列表:show processlist
  • 查询 clickhouse 的合并信息:select * from system.merges
  • 查询 clickhouse 执行的任务:select * from system.mutations where is_done=0
  • 查询是否有运行时间超过规定时间的 mutation 操作:select * from cluster(‘{cluster}’, system.merges) where database not in ('system','information_schema', 'INFORMATION_SCHEMA') and is_mutation and elapsed > 120
  1. 主动查杀

通过 kill语句将长时间运行的 mutation 任务杀掉:kill mutation where database=‘’ and table=‘’

zhuanlan.zhihu.com/p/590261481