Clickhouse的集群概念和我们常见的数据库集群有所区别,CK集群的定义是基于节点,但在使用上是表级别,每个表自己定义使用哪个集群。在集群上执行的有些SQL,只会对该节点生效,比如建表,建用户等语句,如果节点比较多,去每个节点上单独执行会比较麻烦,这时候就需要借助分布式DLL on cluster。
分布式DDL
分布式DDL,官方给的定义如下:
默认情况下,CREATE、DROP、ALTER和RENAME查询仅影响执行它们的当前服务器。 在集群设置中,可以使用ON CLUSTER子句以分布式方式运行此类查询。
https://clickhouse.com/docs/zh/sql-reference/distributed-ddl
简单来说,Create, Drop等DDL语句默认只会在执行的本机运行,加上ON CLUSTER cluster_name1 后缀之后,就会在cluster_name1 所定义的所有的节点进行执行。我们来看看这一张图就更容易理解分布式DDL了,以Replicated*MergeTree 表引擎为例,与表相关的操作哪些会replicated:

这个表很明确的给出了哪些SQL语句会自动被CK自动复制到副本节点,可以很明显的发现DML的语句都不需要添加on clutser,他们的数据修改都会自动被CK的副本同步机制复制到集群其他节点,但DDL语句基本都需要添加on cluster 或者手动到副本节点去执行,。
On cluster不仅适用于我们常见的针对table的操作CREATE、DROP、ALTER,RENAME,ATTACH和DETACH, 同样也可以用于针对User和Role的CREATE、DROP等操作。
分布式DDL原理
分布式DDL也是基于Zookeeper进行数据的传递,这一篇文章对其原理进行了详细的解读:
https://blog.csdn.net/qq_42194171/article/details/109703100
分布式DDL的原理其实和副本数据同步原理是类似的:
我们在server1执行分布式DDL语句时,本着谁执行谁负责的原则,server1节点负责创建日志并将日志推送到zookeeper,同时也由serve1节点负责监控任务的执行进度。
其它节点监听到此操作日志后,后判断自己是否在该hosts列表内,如果包含,则进入执行流程。
在第一步客户端执行DDL语句之后,客户端会阻塞等待180秒,以期望所有节点都执行DDL完毕。如果等待时间大于180秒,则会转入后台线程继续等待。
在Zk中的路径,以及其他一些distributed_ddl是在config.xml中配置的:
<distributed_ddl><!-- Path in ZooKeeper to queue with DDL queries --><path>/clickhouse/task_queue/ddl</path><!-- Settings from this profile will be used to execute DDL queries --><!-- <profile>default</profile> --><!-- Controls how much ON CLUSTER queries can be run simultaneously. --><!-- <pool_size>1</pool_size> --><!--Cleanup settings (active tasks will not be removed)--><!-- Controls task TTL (default 1 week) --><!-- <task_max_lifetime>604800</task_max_lifetime> --><!-- Controls how often cleanup should be performed (in seconds) --><!-- <cleanup_delay_period>60</cleanup_delay_period> --><!-- Controls how many tasks could be in the queue --><!-- <max_tasks_in_queue>1000</max_tasks_in_queue> --></distributed_ddl>
clickhouse的分布式ddl是串行执行的,每次将任务存储到zookeeper
的/clickhouse/task_queue/ddl目录,按照FIFO排列,pool_size参数定义了并行度,默认为1,这也会导致可能任意1个ddl卡住了都会阻塞后续任务。
我们可以直接在CK中直接查询到zk里面的DDL队列相关信息:
select * from zookeeper where path='/clickhouse/task_queue/ddl' order by ctime limit 5;│ query-0000000099 │ version: 2query: DROP TABLE LICHI.MAIL_SEND_SHARD ON CLUSTER rtdw NO DELAYhosts: ['46161%2Eck%2Enavrmo%2Eml:9000']initiator: 46161%2Eck%2Enavrmo%2Eml:9000settings: load_balancing = 'random', max_memory_usage = 10000000000│ 159458922159 │ 159458922159 │ 2023-02-03 10:42:55 │ 2023-02-03 10:42:55 │ 0 │ 2 │ 0 │ 0 │ 226 │ 2 │ 159458922165 │ clickhouse/task_queue/ddl │
在system.distributed_ddl_queue中也是可以看到分布式DDL队列的历史记录,这里面记录了SQL,执行的host, 执行结果等信息。
select query, cluster, host, status from distributed_ddl_queue;─query─────────────────────────────────────────────────────┬─cluster─┬─host───────────────┬─status───┐│ CREATE USER testddl3 ON CLUSTER rtdw │ rtdw │ 46152.ck.navrmo.ml │ Finished ││ CREATE USER testddl3 ON CLUSTER rtdw │ rtdw │ 46161.ck.navrmo.ml │ Finished ││ DROP USER testddl2 ON CLUSTER rtdw │ rtdw │ 46152.ck.navrmo.ml │ Finished ││ DROP USER testddl2 ON CLUSTER rtdw │ rtdw │ 46161.ck.navrmo.ml │ Finished │
DDl Inactive Status 问题
我们刚刚新建的集群上, 用分布式DDL一直失败,直接触发180s的timeout执行超时,但是单独在每个节点上不加on cluster都是可以正常执行, 通过distributed_ddl_queue查看SQL的状态都是Inactive:

最后我们从error日志中发现是DNS配置问题, 集群中的一台机器没有在DNS中配置:
2023.02.07 11:13:28.593761 [ 7815 ] {} <Error> DDLWorker: Unexpected error, will try to restart main thread: Code: 198. DB::Exception: Not found address of host: 46152.ck.navrmo.ml. (DNS_ERROR), Stack trace (when copying this message, always include the lines below):
在修复了DNS的配置后,这些DDL都直接自动执行了,状态也变成了finished。
小结
分布式DDL是CK中相对其他数据库集群机制比较特殊的地方,刚刚开始使用的时候一直不清楚什么时候sql需要添加on cluster,遇到问题的时候排错也很困难,在了解了CK副本机制及其原理之后,才能做到知其然知其所以然。




