[本文作者:擎创 禹鼎侯。我们有时候叫他“大禹”,是个文理双全的攻城狮]
什么是逻辑集群
ClickHouse
作为一个基于OLAP
场景的数据库,对于集群的支持自然也是理所当然的。我们通常所说的ClickHouse
集群,指的是物理集群。即集群各节点之间被同一个zookeeper
集群管理,数据的各种DDL
操作都是针对整个集群有效的。
与物理集群相对应的,还有一类集群,我们叫它逻辑集群。它是指在物理上没有一定的必然关系的物理集群,彼此又组成了一个逻辑集群。可以用下面这幅图形象地表达物理集群和逻辑集群的关系。
如上图所示,三个物理集群各自是相互独立的,对cluster1
的各种数据操作,cluster2
和cluster3
并无法感知,但是对于logic
集群来说,任何一个物理集群的数据变化,逻辑集群都能通过查询获取到。
为什么需要逻辑集群
那么,既然有了物理集群,为什么还要逻辑集群呢?它能解决用户什么痛点?又能带来哪些好处?
首先,逻辑集群能解决zookeeper压力问题
我们知道,ClickHouse
集群的数据一致性是通过Zookeeper
集群来保证的。当集群的数据量特别大或者操作特别频繁的时候,带来的就是zookeeper
的znode
节点数量多,更新频繁。而zookeeper
的压力是有上限的,如果集群过大,或者一个zookeeper
集群管理的clickhouse
集群过多的话,很容易造成zookeeper
崩溃或者卡死。
正是因为“天下苦zookeeper
久矣”,逻辑集群的出现正好能解决这一问题。
比如,某个物理集群的数据量特别庞大, 庞大到单一的zookeeper
集群无法支撑其元数据管理,这时候我们可以将这个物理集群拆分成多个物理集群,分别使用不同的zookeeper
集群去管理,然后通过逻辑集群去查询数据。这样既分担了zookeeper
的压力,也保留了各个物理集群之间的业务关联性,不影响业务数据的查询。
其次,逻辑集群解决了多数据中心问题
还有一种比较常见的场景是多数据中心。随着企业数据规模的扩张,不可能把所有的数据都存放在一个数据中心,面对这样的多数据中心的场景,如果只建立一个物理集群,那么首先面临的是网络的延迟问题,其次还有高昂的流量费用,这都是需要考虑的问题。
那么比较好的做法是在每一个数据中心都分别建立各自的物理集群,这样无论是数据的插入还是查询,都极大减少了网络上的限制,从而把延迟做到最小。
但是我们考虑这样一个场景:某银行机构在上海和合肥各有一个数据中心,分别建立了两个物理集群,叫做bench_shanghai
和bench_hefei
, 现在总行机构需要综合上海和合肥的两个数据中心的数据做数据分析。那么我们还必须分别查询上海的集群和合肥的集群,然后再进行汇总。如果数据中心比较多,那么查询的次数也就特别多,这样显然是事倍功半的。
如果我们建立一个逻辑集群,将上海的集群和合肥的集群包含在内,我们就叫它bench
集群,这样我们只需要查询bench
集群,就能一次将数据全部查出来,如下图所示:
再次,逻辑集群能节约成本
逻辑集群节约成本是肯定的。当然主要是网络流量方面的成本。尤其是云上的服务器,流量贵得惊人。设想一下,如果上海和合肥两个数据中心只有一个物理集群,而恰好一个shard
中的不同副本又分别位于不同的数据中心,当每天数TB
级别的数据写入数据库,在做副本之间数据同步的时候,产生的流量当有多么可怕。
逻辑集群能做什么
我们不建议在逻辑集群进行mutation
的相关操作。不建议对数据进行插入、删除等。这些操作都应该在物理集群去完成。
事实上,逻辑集群天生也不擅长做上面这些操作。这主要取决于以下这几层限制:
-
逻辑集群可能包含多个物理集群,这些物理集群不一定在同一个
zookeeper
上,因此,不论是建表还是插入数据,一致性是一定不能保证的。 -
即使逻辑集群的多个物理集群使用同一个
zookeeper
,仍然无法创建逻辑集群层面上的分布式表。因为有可能物理集群A使用了副本,创建的本地表引擎是ReplicatedMergeTree
, 但是物理集群B没有使用副本,创建的本地表是MergeTree
,这样即使本地表结构一模一样,也无法在逻辑集群上创建分布式表。 -
再退一步,就算物理集群
A
和B
都使用了副本,建表的schema
一模一样,使用的都是ReplicatedMergeTree
引擎,在逻辑集群上创建分布式表仍然不可避免问题。因为为了防止集群之间相互干扰,我们一般定义zookeeper
的路径时,都会带上集群的名字,比如下面这样:
CREATE TABLE default.t1 ( `@time` DateTime, `@item_guid` String, `@metric_name` LowCardinality(String), `@alg_name` LowCardinality(String) ) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/{cluster}/{shard}/default/t1', '{replica}') PARTITION BY toYYYYMMDD(`@time`) ORDER BY (`@time`, `@item_guid`, `@metric_name`) SETTINGS index_granularity = 8192
因此不同的物理集群在zookeeper
的路径其实是不同的,而分布式表之所以能够使用ON CLUSTER xxx
这种语法创建,主要就是因为在zookeeper
的task queue
中保存了DDL
语句,而不同的集群task queue
的路径不同,就无法做到同步。因此创建分布式表也会失败。
因此,逻辑集群存在的意义主要是用来查询。这也是它唯一能做的事情。
那么,既然逻辑集群在创建分布式表上有这么多的限制(几乎就是不可能创建成功),为什么我们还能使用逻辑集群进行查询呢?这不是自相矛盾的么?
事实上,我们可以在物理集群上分别创建跨逻辑集群的分布式表,只要表的schema
一样,是可以实现查询的。如下面这样的写法:
--上海集群创建: CREATE TABLE default.dist_logic_t123 on cluster bench_shanghai as t123 ENGINE = Distributed('bench', 'default', 't123', rand()); --合肥集群创建 CREATE TABLE default.dist_logic_t123 on cluster bench_hefei as t123 ENGINE = Distributed('bench', 'default', 't123', rand());
这样不论是往上海集群还是合肥集群插入数据,我们都能从dist_logic_t123
表中查询出来。
如何创建一个逻辑集群
创建逻辑集群的方式和普通的物理集群类似,只需要在metrika.xml
配置文件中加入逻辑集群的相关信息即可。如下所示:
上海集群metrika.xml
配置文件:
合肥集群metrika.xml
配置文件:
创建逻辑集群不复杂,复杂的是对逻辑集群的后续运维。
假设我们现在要增加一个成都的数据中心,需要创建一个成都物理集群,并将成都物理集群增加到bench逻辑集群中去,那么还要同步修改上海和合肥集群所有节点的配置文件。
与之类似的操作还有增加节点、删除节点、销毁集群等,有可能是某一个物理集群的微小改动,就要连带着所有逻辑集群的配置文件的刷新,真可谓“牵一发而动全身”。
这也是没有办法的事,世上没有十全十美的事情。既然想要使用逻辑集群带来的便利,就得接受繁琐的配置工作。
ckman对逻辑集群的支持
ckman
是擎创科技自主研发的一款用来管理和监控clickhouse
集群的运维工具。它的全称是"ClickHouse Manager Console
",它通过直观的可视化界面,可以非常方便地对集群进行创建、启停、升级,以及增删节点等操作,运维人员无需关注clickhouse
配置文件变化的细节,一切都由ckman
主动完成。同时它还提供了数据表、会话以及节点级别的监控,可以直观的观察到集群的使用情况。
在最新发布的ckman2.0beta
版中,ckman
加入了对逻辑集群的支持。
诚如上面所说,对逻辑集群的运维动作,比如增删节点、增加和销毁物理集群,都需要同步刷新逻辑集群里所有节点的配置,这项工作不仅费时费力,且容易出错,人工去操作的话,很难保证效率。而ckman
的出现,恰好解决了这一问题。我们只需要在界面上进行简单的点击,所有的配置文件刷新操作都会由ckman
去自主完成,从而大大简化了操作步骤。
创建逻辑集群
使用ckman
创建逻辑集群非常简单,只需要在"逻辑名称"输入框中输入逻辑集群的名称即可。
逻辑集群创建成功后,会在首先的集群列表中展示出来。
同时,这些信息会持久化到硬盘中,在conf/clusters.json
文件中,有如下选项:
"logic_clusters": { "bench": [ "bench_shanghai", "bench_hefei" ] }
它的意思是说bench
逻辑集群由bench_shanghai
和bench_hefei
的物理集群组成,该逻辑集群的节点也就是其物理集群的所有节点。
其他运维操作
ckman
支持逻辑集群的增加节点、删除节点、升级集群以及销毁集群等操作。虽然这些操作是针对物理集群的,但是如果设置了逻辑集群,也会同步刷新逻辑集群所有节点的metrika.xml
配置,从而保证逻辑集群的节点实时同步。
创建跨逻辑集群的分布式表
前面提到过,我们可以在每个物理集群创建跨逻辑集群的分布式表,但是当逻辑集群内的物理集群比较多时,上述的建表操作还是很繁琐的,ckman
提供了一个dist_table
的API
,这是一个POST
接口。
URL
:/api/v1/ck/dist_table
METHOD
:POST
BODY
:
{ "database":"default", "logic_name":"bench", "table_name":"t123" }
该接口会在逻辑集群的每个物理集群上创建跨逻辑集群的分布式表,使用该分布式表,就可以实现在逻辑集群上进行数据查询。
写在最后
本文简单介绍了逻辑集群的玩法,不可否认的是,逻辑集群的引入,确实能解决不少客户的痛点,但同样也带来了不小的运维阻力。虽然有类似于ckman
这样的运维工具可以帮助解决,但某些高端的玩法,比如由多个逻辑集群组成的父级逻辑集群,也是ckman
不能支持的。但随着clickhouse
周边生态的原来越完善,相信会有更多优秀的集群玩法诞生,也许下一个亮眼的点子就是你提出的,谁说不是呢?