本文介绍如何在CentOS
系统上安装ClickHouse
以及集群部署。
本文依赖的环境为CentOS-7.4.1708
, 使用clickhouse
的版本为20.9.3.45
。
单节点安装
在线yum安装
clickhouse
的安装包并不在Linux
官方yum
仓库中,所以首先需要添加clickhouse
的yum
镜像:
curl -s https://packagecloud.io/install/repositories/altinity/clickhouse/script.rpm.sh | sudo bash
检查镜像情况:
[root@master ~]# yum list | grep clickhouse
clickhouse-client.x86_64 20.8.3.18-1.el7 Altinity_clickhouse
clickhouse-common-static.x86_64 20.8.3.18-1.el7 Altinity_clickhouse
clickhouse-debuginfo.x86_64 20.1.11.73-1.el7 Altinity_clickhouse
clickhouse-odbc.x86_64 1.1.9-1.el7 Altinity_clickhouse
clickhouse-server.x86_64 20.8.3.18-1.el7 Altinity_clickhouse
clickhouse-server-common.x86_64 20.8.3.18-1.el7 Altinity_clickhouse
clickhouse-test.x86_64 20.8.3.18-1.el7 Altinity_clickhouse
clicktail.x86_64 1.0.20180401-1 Altinity_clickhouse
由此可见,使用yum
在线安装的版本最新到20.8.3.18
。但是实际上clickhouse
已经迭代了很多版本,所以我们不采用此种安装方式,而采用rpm
包的安装方式。
PS: 有了上面的信息,yum
方式安装clickhouse
就很简单了,直接yum install -y clickhouse-server-common.x86_64 clickhouse-server.x86_64 clickhouse-client.x86_64
安装即可,此处就不演示了。
rpm包离线安装
我们可以从官方rpm
镜像仓库找到各种版本的rpm
安装包,官方地址是:https://repo.yandex.ru/clickh... 。
我们选择 20.9.3.45
版本的rpm
包,主要需要以下三个包:
clickhouse-client-20.9.3.45-2.noarch.rpm
clickhouse-server-20.9.3.45-2.noarch.rpm
clickhouse-common-static-20.9.3.45-2.x86_64.rpm
安装命令如下:
rpm -ivh clickhouse-common-static-20.9.3.45-2.x86_64.rpm clickhouse-client-20.9.3.45-2.noarch.rpm clickhouse-server-20.9.3.45-2.noarch.rpm
如下图所示,我们即在node5
这台机器上安装好了clickhouse
。
[root@node5 ck]# rpm -ivh clickhouse-common-static-20.9.3.45-2.x86_64.rpm clickhouse-client-20.9.3.45-2.noarch.rpm clickhouse-server-20.9.3.45-2.noarch.rpm
警告:clickhouse-common-static-20.9.3.45-2.x86_64.rpm: 头V4 RSA/SHA1 Signature, 密钥 ID e0c56bd4: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:clickhouse-common-static-20.9.3.4################################# [ 33%]
2:clickhouse-client-20.9.3.45-2 ################################# [ 67%]
3:clickhouse-server-20.9.3.45-2 ################################# [100%]
Path to data directory in /etc/clickhouse-server/config.xml: /var/lib/clickhouse/
[root@node5 ck]#
验证是否安装成功
如果以上步骤没有报错,则可以通过如下命令开启clickhouse
的服务:
systemctl start clickhouse-server
然后通过clickhouse-client
客户端去连接:
[root@node5 ck]# clickhouse-client
ClickHouse client version 20.9.3.45 (official build).
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 20.9.3 revision 54439.
node5 :) select 1
SELECT 1
┌─1─┐
│ 1 │
└───┘
1 rows in set. Elapsed: 0.003 sec.
node5 :)
如果出现上述界面,则说明单机安装clickhouse
成功。
clickhouse-client CLI工具说明
在上面,我们用到了clickhouse-client
这样一款工具。
clickhouse-client
是一个带命令行的客户端工具,它通过TCP
的9000
端口连接clickhouse-server
,可以在该命令行工具里进行各种操作。
通过 clickhouse-client --help
可以查看工具的帮助文档,可以看到该工具支持很多参数,此处简要说一下一些常用的参数。
参数 | 用法举例 | 说明 | 备注 |
---|---|---|---|
-h [ --host ] |
-h 192.168.0.1 --host=192.168.0.1 |
指定连接clickhouse 服务的host 地址,一般是远程连接其他机器上的clickhouse 服务。 |
clickhouse 要想连接远程服务器,需要将配置文件中 选项开启。 |
--port |
--port=9000 |
指定远程连接clickhouse 服务的端口号,默认为9000 |
这个端口号可通过配置文件配置:
|
-u [ --user ] |
-u ck --user=ck |
指定登录clickhouse 的用户,默认是default |
default 用户是内置的用户。可以通过users.xml 配置文件自定义用户信息。 |
--password |
--password=123456 |
指定登录clickhouse 的密码 |
default 用户是没有密码的。密码可配置明文密码和加密密码 |
-q [ --query ] |
-q "show databases" |
通过命令行直接输入sql ,即时返回,而不需要进入客户端内部 |
|
-m [ --multiline ] |
在客户端内支持多行语句操作,以分号作为结束符 | 如果不指定-m 参数,每句sql 语法后可加分号,也可不加,默认一行就是一个语句 |
|
-n [ --multiquery ] |
命令行直接输入语句时支持多条sql语句,一般和-q 结合使用 |
注意-n 的位置不能在-q 和sql 语句之间 |
|
-f [ --format ] |
-f JSON |
设置输出的显示格式 | 支持JSON 、CSV 、TSV 等,详见https://clickhouse.tech/docs/... |
利用CLI工具导入数据
假设我们在default
数据库下有一张test
表,在/data01
目录下有一个file.csv
的文件,可通过如下方式将数据导入到test
表中。
clickhouse-client --database=test --query="INSERT INTO test FORMAT CSV" < /data01/file.csv
配置文件说明
config.xml
config.xml
文件是clickhouse
默认的配置文件,路径为/etc/clickhouse-server/config.xml
。
config.xml
中可以配置的内容有很多,下面节选一些比较重要的配置项来说明一下:
trace
/var/log/clickhouse-server/clickhouse-server.log
/var/log/clickhouse-server/clickhouse-server.err.log
1000M
10
8123
9000
9004
::
/var/lib/clickhouse/
/var/lib/clickhouse/user_files/
users.xml
default
/metrics
9363
true
true
true
true
/etc/clickhouse-server/metrika.xml
users.xml
123456
::/0
default
default
metrika.xml
metrika.xml
主要是用来服务于集群搭建的。它里面是关于zookeeper
、shard
以及replica
的配置,比较复杂,将在部署集群中详细说明。
部署集群
Clickhouse
的集群是依赖于zookeeper
的。所以在搭建clickhouse
集群之前,首先要有一个zookeeper
集群。
关于zookeeper
集群的搭建,此处不再展开详述,如有不清楚的请自行百度。(本文默认zookeeper
集群已经搭建好)
前面说过,clickhouse
集群的相关配置都在metrika.xml
配置文件里,所以首先我们需要配置好metrika.xml
。metrika.xml
的路径可以在config.xml
中通过
指定。
zookeeper配置
zookeeper
不一定要和ck
的节点安装在同一台机器上,只要ck
的几点能够访问zk即可。
192.168.0.1
2181
192.168.0.2
2181
192.168.0.3
2181
Shard和Replica设置
如下图所示,为一个完整的shard
和Replica
的配置,以下为test
集群配置2
分片2
副本。
true
192.168.0.1
9000
192.168.0.2
9000
true
192.168.0.3
9000
192.168.0.4
9000
一个完整的metrika.xml
如下所示:
192.168.0.1
2181
192.168.0.2
2181
192.168.0.3
2181
true
192.168.0.1
9000
192.168.0.2
9000
true
192.168.0.3
9000
192.168.0.4
9000
集群搭建
首先按照单节点安装的方式,在node1
,node2
,node3
,node4
四个节点上安装好clickhouse
。
修改config.xml
,主要修改内容如下:
0.0.0.0
/data01/clickhouse
/data01/clickhouse/tmp/
/data01/clickhouse/user_files/
/data01/clickhouse/access/
/etc/clickhouse-server/metrika.xml
19000
修改users.xml
,我们增加一个用户,专门用来管理cluster
。
123456
::/0
default
default
创建metrika.xml
文件。
在/etc/clickhouse-server
路径下创建metrika.xml
文件(这个路径为上面config.xml
中配置的include_from
的路径):
192.168.0.1
2181
192.168.0.2
2181
192.168.0.3
2181
192.168.0.1
19000
192.168.0.2
19000
192.168.0.3
19000
192.168.0.4
19000
以上配置在四台机器上同步,然后重启clickhouse
:
systemctl restart clickhouse-server
如果启动成功,就能看到clickhouse-server
的进程已经在运行了:
[root@node2 test]# ps -ef |grep clickhouse
clickho+ 17548 1 1 11:11 ? 00:00:00 /usr/bin/clickhouse-server --config=/etc/clickhouse-server/config.xml --pid-file=/run/clickhouse-server/clickhouse-server.pid
root 17738 10683 0 11:11 pts/0 00:00:00 grep --color=auto clickhouse
我们使用clickhouse-client
工具,登录到其中一台机器:
[root@node4 test]# clickhouse-client -u ck --password=123456 --port=19000 -m
ClickHouse client version 20.9.3.45 (official build).
Connecting to localhost:19000 as user ck.
Connected to ClickHouse server version 20.9.3 revision 54439.
node4 :)
查询system.clusters
表:
node4 :) select * from system.clusters where cluster = 'test';
SELECT *
FROM system.clusters
WHERE cluster = 'test'
┌─cluster─┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─────┬─host_address──┬──port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐
│ test │ 1 │ 1 │ 1 │ 192.168.0.1 │ 192.168.0.1 │ 19000 │ 0 │ default │ │ 0 │ 0 │
│ test │ 1 │ 1 │ 2 │ 192.168.0.2 │ 192.168.0.2 │ 19000 │ 0 │ default │ │ 0 │ 0 │
│ test │ 2 │ 1 │ 1 │ 192.168.0.3 │ 192.168.0.3 │ 19000 │ 0 │ default │ │ 0 │ 0 │
│ test │ 2 │ 1 │ 2 │ 192.168.0.4 │ 192.168.0.4 │ 19000 │ 1 │ default │ │ 0 │ 0 │
└─────────┴───────────┴──────────────┴─────────────┴───────────────┴───────────────┴───────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘
4 rows in set. Elapsed: 0.002 sec.
出现上面所示的结果,说明配置集群成功。
分布式表
接下来就可以在集群里创建分布式表了。
首先在集群的各个节点创建本地表。
CREATE TABLE t_cluster ON CLUSTER test (
id Int16,
name String,
birth Date
)ENGINE = MergeTree()
PARTITION BY toYYYYMM(birth)
ORDER BY id;
登录到任意节点,执行以上sql
,出现如下提示,说明执行成功:
node4 :) CREATE TABLE default.t_cluster ON CLUSTER test ( id Int16, name String, birth Date )ENGINE = MergeTree() PARTITION BY toYYYYMM(birth) ORDER BY id;
CREATE TABLE default.t_cluster ON CLUSTER test
(
`id` Int16,
`name` String,
`birth` Date
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(birth)
ORDER BY id
┌─host──────────┬──port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ 192.168.0.1 │ 19000 │ 0 │ │ 3 │ 0 │
│ 192.168.0.2 │ 19000 │ 0 │ │ 2 │ 0 │
│ 192.168.0.3 │ 19000 │ 0 │ │ 1 │ 0 │
│ 192.168.0.4 │ 19000 │ 0 │ │ 0 │ 0 │
└───────────────┴───────┴────────┴───────┴─────────────────────┴──────────────────┘
4 rows in set. Elapsed: 0.108 sec.
我们可以登录任意一台机器,都能查询到t_cluster
表,这就说明已经在test
集群上所有节点创建了一个t_cluster
本地表。
这个时候如果往t_cluster
表插入数据,仍然是在所在节点的本地表中操作,在其他集群中是无法看见的。
如我们在node1
节点插入如下数据:
node1 :) insert into t_cluster values(1, 'aa', '2021-02-01'), (2, 'bb', '2021-02-02');
INSERT INTO t_cluster VALUES
Ok.
2 rows in set. Elapsed: 0.002 sec.
在node1
节点查询:
node1 :) select * from t_cluster;
SELECT *
FROM t_cluster
┌─id─┬─name─┬──────birth─┐
│ 1 │ aa │ 2021-02-01 │
│ 2 │ bb │ 2021-02-02 │
└────┴──────┴────────────┘
2 rows in set. Elapsed: 0.002 sec.
node2
节点查询:
node2 :) select * from t_cluster;
SELECT *
FROM t_cluster
Ok.
0 rows in set. Elapsed: 0.002 sec.
node3
和node4
查询也是如此。
这就充分说明了如果直接操作t_cluster
表,操作的数据其实只是当前节点的数据。有人可能会有疑惑,我不是明明指定了node1
和node2
互为replica
吗?为什么node1
的数据也没有同步到node2
上面去?
这是因为我们的引擎指定的是MergerTree
,而不是ReplicaMergeTree
,如果指定的是ReplicaMergeTree
,的确是会同步到对应的replica
上面去的,但这里创建的仅仅是MergeTree
,这个引擎本身不具备同步副本的功能,所以node2
上并没有同步数据。
一般在实际应用中,创建分布式表指定的都是
Replica
的表,这里仅仅是做一个例子说明。
那么既然如此,这个集群中的表数据不能互相关联起来,不就失去了集群的意义了吗?
为了解决上面的问题,从而引入了分布式表。
分布式表的创建是这样的:
CREATE TABLE default.dist_t_cluster ON CLUSTER test as t_cluster engine = Distributed(test, default, t_cluster,rand());
Distributed
是一个特殊的引擎,用来创建分布式表。它本身具有四个参数,第一个参数表示集群的名字,第二个参数为数据库的名字,第三个参数为表的名字,第四个参数为sharding key
,它决定最终数据落在哪一个分片上,最后一个参数可以省略,如果省略,则实用配置文件中的weight
分片权重。
上面的语句就是创建一张名为dist_t_cluster
的分布式表,该分布式表是基于test
集群中的default.t_cluster
表。as t_cluster
表示这张表的结构和t_cluster
一模一样。
分布式表本身不存储数据,数据存储其实还是由本地表t_cluster
完成的。这个dist_t_cluster
仅仅做一个代理的作用。
如下所示,表示分布式表创建成功。
node1 :) CREATE TABLE default.dist_t_cluster ON CLUSTER test as t_cluster engine = Distributed(test, default, t_cluster, rand());
CREATE TABLE default.dist_t_cluster ON CLUSTER test AS t_cluster
ENGINE = Distributed(test, default, t_cluster, rand())
┌─host──────────┬──port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ 192.168.0.1 │ 19000 │ 0 │ │ 3 │ 0 │
│ 192.168.0.2 │ 19000 │ 0 │ │ 2 │ 0 │
│ 192.168.0.3 │ 19000 │ 0 │ │ 1 │ 0 │
│ 192.168.0.4 │ 19000 │ 0 │ │ 0 │ 0 │
└───────────────┴───────┴────────┴───────┴─────────────────────┴──────────────────┘
4 rows in set. Elapsed: 0.106 sec.
接下来我们使用分布式表向集群中插入数据,观察现象:
node1 :) insert into dist_t_cluster values(1, 'aaa', '2021-02-01'), (2, 'bbb', '2021-02-02');
INSERT INTO dist_t_cluster VALUES
Ok.
2 rows in set. Elapsed: 0.002 sec.
这时候我们在不同节点上查询dist_t_cluster
表,得到的结果都是一模一样的:
/* node1 查询结果 */
node1 :) select * from dist_t_cluster;
SELECT *
FROM dist_t_cluster
┌─id─┬─name─┬──────birth─┐
│ 2 │ bbb │ 2021-02-02 │
└────┴──────┴────────────┘
┌─id─┬─name─┬──────birth─┐
│ 1 │ aaa │ 2021-02-01 │
└────┴──────┴────────────┘
2 rows in set. Elapsed: 0.005 sec.
/* node2 查询结果 */
node2 :) select * from dist_t_cluster;
SELECT *
FROM dist_t_cluster
┌─id─┬─name─┬──────birth─┐
│ 2 │ bbb │ 2021-02-02 │
└────┴──────┴────────────┘
┌─id─┬─name─┬──────birth─┐
│ 1 │ aaa │ 2021-02-01 │
└────┴──────┴────────────┘
2 rows in set. Elapsed: 0.005 sec.
/* node3 查询结果 */
node3 :) select * from dist_t_cluster;
SELECT *
FROM dist_t_cluster
┌─id─┬─name─┬──────birth─┐
│ 1 │ aaa │ 2021-02-01 │
└────┴──────┴────────────┘
┌─id─┬─name─┬──────birth─┐
│ 2 │ bbb │ 2021-02-02 │
└────┴──────┴────────────┘
2 rows in set. Elapsed: 0.006 sec.
/* node4 查询结果 */
node4 :) select * from dist_t_cluster;
SELECT *
FROM dist_t_cluster
┌─id─┬─name─┬──────birth─┐
│ 1 │ aaa │ 2021-02-01 │
└────┴──────┴────────────┘
┌─id─┬─name─┬──────birth─┐
│ 2 │ bbb │ 2021-02-02 │
└────┴──────┴────────────┘
2 rows in set. Elapsed: 0.005 sec.
我们再去看各个节点的本地表t_cluster
表中数据分布情况:
/* node1 查询结果 */
node1 :) select * from t_cluster;
SELECT *
FROM t_cluster
┌─id─┬─name─┬──────birth─┐
│ 2 │ bbb │ 2021-02-02 │
└────┴──────┴────────────┘
1 rows in set. Elapsed: 0.002 sec.
/* node2 查询结果 */
node2 :) select * from t_cluster;
SELECT *
FROM t_cluster
┌─id─┬─name─┬──────birth─┐
│ 2 │ bbb │ 2021-02-02 │
└────┴──────┴────────────┘
1 rows in set. Elapsed: 0.002 sec.
/* node3 查询结果 */
node3 :) select * from t_cluster;
SELECT *
FROM t_cluster
┌─id─┬─name─┬──────birth─┐
│ 1 │ aaa │ 2021-02-01 │
└────┴──────┴────────────┘
1 rows in set. Elapsed: 0.002 sec.
/* node4 查询结果 */
node4 :) select * from t_cluster;
SELECT *
FROM t_cluster
┌─id─┬─name─┬──────birth─┐
│ 1 │ aaa │ 2021-02-01 │
└────┴──────┴────────────┘
1 rows in set. Elapsed: 0.002 sec.
由以上结果可知,node1
和node2
上面存储的数据一样,node3
和node4
上面存储的数据一样,但是node1
和node3
上面的数据不一样。node1
(node2
)和node3
(node4
)的数据组成了dist_t_cluster
表中的所有数据。
这是因为node1
和node2
、node3
和 node4
互为副本,所以它们的数据会自动同步。而node1
(node2
)和node3
(node4
)属于不同的分片,所以数据按照一定的规则(sharding key
)落在了不同分片。
如果我们打开存储数据的目录,可以看到dist_t_cluster
表中仅仅有一个临时文件夹,并没有实际存储数据,因为数据是存储在t_cluster
本地表中的。
[root@node1 chenyc]# ls -l /data01/clickhouse/data/default/dist_t_cluster/
总用量 0
drwxr-x--- 3 clickhouse clickhouse 17 2月 25 13:57 default@192%2E168%2E21%2E52:19000
drwxr-x--- 3 clickhouse clickhouse 17 2月 25 13:57 default@192%2E168%2E21%2E53:19000
drwxr-x--- 2 clickhouse clickhouse 6 2月 25 13:57 default@192%2E168%2E21%2E54:19000