# 6. ClickHouse 副本集群
副本的目的主要是保障数据的高可用性,即使一台 ClickHouse 节点宕机,那么也可以从其他服务器获得相同的数据。ClickHouse 的副本集群需要用到其中的一种表引擎 ReplicatedMergeTree
。
副本写入流程如下图所示:
副本虽然能够提高数据的可用性,降低丢失风险,但是每台服务器实际上必须容纳全量 数据,对数据的横向扩容没有解决。
要解决数据水平切分的问题,需要引入分片的概念。通过分片把一份完整的数据进行切分,不同的分片分布到不同的节点上,再通过 Distributed
表引擎把数据拼接起来一同使用。 Distributed
表引擎本身不存储数据,有点类似于 MyCat 之于 MySql,成为一种中间件,通过分布式逻辑表来写入、分发、路由来操作多台节点不同分片的分布式数据。
分片集群写入流程(3 分片 2 副本共 6 个节点):
分片集群读取流程(3 分片 2 副本共 6 个节点):
# 6.1 集群规划
- 3 节点
- 2 分片
- 分片1 有 2 个副本
- 分片 2 只有 1 个副本
- 2 分片
机器1 | 机器2 | 机器3 | |
---|---|---|---|
os | centos 7 | centos 7 | centos 7 |
ip | 172.16.208.160 | 172.16.208.161 | 172.16.208.162 |
zookeeper | 3.7.0 | 3.7.0 | 3.7.0 |
ClickHouse | 21.7.3.14-2 | 21.7.3.14-2 | 21.7.3.14-2 |
作用 | 分片1_副本1 | 分片1_副本2 | 分片2_副本1 |
# 6.2 部署 zookeeper 集群
# 6.3 修改 config.xml 配置文件
分别修改三台机器上 /etc/clickhouse-server/
目录下的 config.xml
配置文件,修改 <remote_servers>
、<zookeeper-services>
和 <macros>
标签的内容,在差不多 700
行的位置:
172.16.208.160
<!-- 分片集群信息 --> <remote_servers> <!--此处省略 clickhouse 自带的测试代码 --> <go2ch_cluster> <!-- 集群名称--> <shard> <!--集群的第一个分片--> <internal_replication>true</internal_replication> <replica><!--该分片的第一个副本--> <host>172.16.208.160</host> <port>9000</port> </replica> <replica><!--该分片的第二个副本--> <host>172.16.208.161</host> <port>9000</port> </replica> </shard> <shard> <!--集群的第二个分片--> <internal_replication>true</internal_replication> <replica> <!--该分片的第一个副本--> <host>172.16.208.162</host> <port>9000</port> </replica> </shard> </go2ch_cluster> </remote_servers> <!-- zk 集群信息 --> <zookeeper> <node> <host>172.16.208.160</host> <port>2181</port> </node> <node> <host>172.16.208.161</host> <port>2181</port> </node> <node> <host>172.16.208.162</host> <port>2181</port> </node> </zookeeper> <macros> <shard>01</shard> <!--不同机器放的分片数不一样--> <replica>rep_go2ch_1_1</replica> <!--不同机器放的副本数不一样--> </macros>
172.16.208.161
<!-- 分片集群信息 --> <remote_servers> <!--此处省略 clickhouse 自带的测试代码 --> <go2ch_cluster> <!-- 集群名称--> <shard> <!--集群的第一个分片--> <internal_replication>true</internal_replication> <replica><!--该分片的第一个副本--> <host>172.16.208.160</host> <port>9000</port> </replica> <replica><!--该分片的第二个副本--> <host>172.16.208.161</host> <port>9000</port> </replica> </shard> <shard> <!--集群的第二个分片--> <internal_replication>true</internal_replication> <replica> <!--该分片的第一个副本--> <host>172.16.208.162</host> <port>9000</port> </replica> </shard> </go2ch_cluster> </remote_servers> <!-- zk 集群信息 --> <zookeeper> <node> <host>172.16.208.160</host> <port>2181</port> </node> <node> <host>172.16.208.161</host> <port>2181</port> </node> <node> <host>172.16.208.162</host> <port>2181</port> </node> </zookeeper> <macros> <shard>01</shard> <!--不同机器放的分片数不一样--> <replica>rep_go2ch_1_2</replica> <!--不同机器放的副本数不一样--> </macros>
172.16.208.162
<!-- 分片集群信息 --> <remote_servers> <!--此处省略 clickhouse 自带的测试代码 --> <go2ch_cluster> <!-- 集群名称--> <shard> <!--集群的第一个分片--> <internal_replication>true</internal_replication> <replica><!--该分片的第一个副本--> <host>172.16.208.160</host> <port>9000</port> </replica> <replica><!--该分片的第二个副本--> <host>172.16.208.161</host> <port>9000</port> </replica> </shard> <shard> <!--集群的第二个分片--> <internal_replication>true</internal_replication> <replica> <!--该分片的第一个副本--> <host>172.16.208.162</host> <port>9000</port> </replica> </shard> </go2ch_cluster> </remote_servers> <!-- zk 集群信息 --> <zookeeper> <node> <host>172.16.208.160</host> <port>2181</port> </node> <node> <host>172.16.208.161</host> <port>2181</port> </node> <node> <host>172.16.208.162</host> <port>2181</port> </node> </zookeeper> <macros> <shard>02</shard> <!--不同机器放的分片数不一样--> <replica>rep_go2ch_2_1</replica> <!--不同机器放的副本数不一样--> </macros>
# 6.4 测试
# 6.4.1 重启 ClickHouse
因为修改了配置文件,所以三台机器都需要重启 ClickHouse:
sudo clickhouse restart
# 6.4.2 查询集群状态
SELECT * FROM system.clusters;
可以看到我们刚刚创建的分片副本集群:
# 6.4.3 建数据表
在 172.16.208.160
上执行建表语句:
- 会自动同步到
172.16.208.161
和172.16.208.162
上 - 集群名字要和配置文件中的一致
- 分片和副本名称从配置文件的宏定义中获取
本示例的具体的建表 SQL 语句如下:
CREATE TABLE shard_example ON CLUSTER go2ch_cluster(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
)
ENGINE=ReplicatedMergeTree('/clickhouse/tables/{shard}/shard_example', '{replica}')
PARTITION BY toYYYYMMDD(create_time)
PRIMARY KEY (id)
ORDER BY (id, sku_id);
ReplicatedMergeTree('zookeeper_name_configured_in_auxiliary_zookeepers:path', 'replica_name')
参数解释:
- 第一个参数是分片信息在 zookeeper 的存储路径,一般按照:/clickhouse/tables/{shard}/{table_name} 的格式,如果只有一个分片就写 01 即可。
- 第二个参数是副本名称,相同的分片副本名称不能相同。
当前是在搭建分片副本集群,因为我们前面修改了 config.xml 配置,这里 ClickHouse 会自动去配置文件中读取 {shard}
和 {replica}
的值,然后自动赋值到各个分片及副本上,为其同步建表。
执行结果如下:
CREATE TABLE shard_example ON CLUSTER go2ch_cluster
(
`id` UInt32,
`sku_id` String,
`total_amount` Decimal(16, 2),
`create_time` Datetime
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/shard_example', '{replica}')
PARTITION BY toYYYYMMDD(create_time)
PRIMARY KEY id
ORDER BY (id, sku_id)
Query id: 12b99e46-fc2b-4023-bdb9-7f13d3e93bde
┌─host───────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ 172.16.208.161 │ 9000 │ 0 │ │ 2 │ 0 │
│ 172.16.208.162 │ 9000 │ 0 │ │ 1 │ 0 │
│ 172.16.208.160 │ 9000 │ 0 │ │ 0 │ 0 │
└────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘
3 rows in set. Elapsed: 0.570 sec.
这个时候我们在 172.16.208.161
和 172.16.208.162
上都可以看到 shard_example
表的:
localhost :) show tables;
SHOW TABLES
Query id: 5070ea08-479a-4f08-8376-8a2d610bcde6
┌─name───────────┐
│ shard_example │
└────────────────┘
10 rows in set. Elapsed: 0.012 sec.
# 6.4.4 创建分布式表
在 172.16.208.160
上创建分布式表:
CREATE TABLE shard_example_all ON CLUSTER go2ch_cluster(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
)
ENGINE=Distributed(go2ch_cluster, default, shard_example, hiveHash(sku_id));
ENGINE=Distributed(...) 参数含义:
- 集群名称
- 数据库名
- 本地表名
- 分片建:分片键必须是整型数字,所以用 hiveHash() 函数转换,也可以 rand()
执行结果:
CREATE TABLE shard_example_all ON CLUSTER go2ch_cluster
(
`id` UInt32,
`sku_id` String,
`total_amount` Decimal(16, 2),
`create_time` Datetime
)
ENGINE = Distributed(go2ch_cluster, default, shard_example, hiveHash(sku_id))
Query id: f1e9afaa-f24e-4f07-93c7-51e983588154
┌─host───────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ 172.16.208.161 │ 9000 │ 0 │ │ 2 │ 0 │
│ 172.16.208.162 │ 9000 │ 0 │ │ 1 │ 0 │
│ 172.16.208.160 │ 9000 │ 0 │ │ 0 │ 0 │
└────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘
3 rows in set. Elapsed: 0.456 sec.
# 6.4.5 插入数据
在 172.16.208.160
上执行 insert
语句,注意 insert
的 表要写分布式表的名称:
INSERT INTO shard_example_all VALUES
(101,'sku_001',1000.00,'2020-06-01 12:00:00'),
(102,'sku_002',2000.00,'2020-06-01 12:00:00'),
(103,'sku_004',2500.00,'2020-06-01 12:00:00'),
(104,'sku_002',2000.00,'2020-06-01 12:00:00'),
(105,'sku_003',600.00,'2020-06-02 12:00:00');
# 6.4.6 查询数据
SELECT * FROM shard_example_all;
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 102 │ sku_002 │ 2000.00 │ 2020-06-01 12:00:00 │ │ 103 │ sku_004 │ 2500.00 │ 2020-06-01 12:00:00 │ │ 104 │ sku_002 │ 2000.00 │ 2020-06-01 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘ ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 101 │ sku_001 │ 1000.00 │ 2020-06-01 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘ ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 105 │ sku_003 │ 600.00 │ 2020-06-02 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘
172.16.208.160 上 SELECT * FROM shard_example;
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 102 │ sku_002 │ 2000.00 │ 2020-06-01 12:00:00 │ │ 103 │ sku_004 │ 2500.00 │ 2020-06-01 12:00:00 │ │ 104 │ sku_002 │ 2000.00 │ 2020-06-01 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘
172.16.208.161 上 SELECT * FROM shard_example;
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 102 │ sku_002 │ 2000.00 │ 2020-06-01 12:00:00 │ │ 103 │ sku_004 │ 2500.00 │ 2020-06-01 12:00:00 │ │ 104 │ sku_002 │ 2000.00 │ 2020-06-01 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘
172.16.208.162 上 SELECT * FROM shard_example;
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 101 │ sku_001 │ 1000.00 │ 2020-06-01 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘ ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐ │ 105 │ sku_003 │ 600.00 │ 2020-06-02 12:00:00 │ └─────┴─────────┴──────────────┴─────────────────────┘
可以看到 160 和 161 同属同一分片,互为副本,162 单属另外一个分片。
# 6.5 常见问题
# 6.5.1 副本不生效
需要在 config.xml 配置文件中打开 <interserver_http_port>
和 <interserver_http_host>
标签,这里是用来副本间传数据的地址和端口,端口默认配好了是 9009,防火墙需要放开这个端口,地址需要配置下指定当前主机具体的地址,不能是127.0.0.1 ,localhost 之类的,不然别的主机读不到这台机。
<interserver_http_port>9009</interserver_http_port>
<interserver_http_host>192.168.x.x</interserver_http_host>