3. ClickHouse安装与部署
部署 ClickHouse,说白了就是两件事:选对模式,配好参数。我见过不少团队,单机跑得飞起,一上集群就各种玄学问题。其实大部分坑,都是 ZooKeeper 和配置细节没处理好。
这一章,我们从头捋一遍。从单机到分片副本集群,再到 ZooKeeper/Keeper 的选型与调优。最后我会把常见的部署问题列出来,都是我在项目中踩过的坑。
3.1 单机部署:最简方案
单机部署没什么难度。但有几个细节,我建议你注意一下。
3.1.1 安装方式选择
| 方式 | 适用场景 | 我的建议 |
|---|---|---|
| RPM/DEB 包 | 生产环境,CentOS/Ubuntu | 首选。systemd 管理,升级方便 |
| Docker | 测试、CI/CD、快速验证 | 别用于生产,除非你懂网络和存储 |
| 二进制 tar.gz | 定制化安装、无 root 权限 | 手动配置多,容易漏 |
| 源码编译 | 二次开发、特殊 CPU 指令集 | 不推荐。编译一次半小时起步 |
我个人习惯用 RPM 包。一条命令装完,服务自动注册到 systemd,省心。
核心配置:/etc/clickhouse-server/config.xml
单机部署,你只需要改两个地方:
<listen_host>::</listen_host>— 允许外部访问。默认只监听 localhost。<path>/var/lib/clickhouse/</path>— 数据目录。建议挂载独立磁盘,别用系统盘。
3.1.2 启动与验证
# 启动服务
sudo systemctl start clickhouse-server
sudo systemctl enable clickhouse-server
# 检查状态
sudo systemctl status clickhouse-server
# 连接客户端
clickhouse-client --host 127.0.0.1 --port 9000
# 验证
SELECT version(), uptime();
嗯,这里要注意:如果连不上,先看防火墙。9000 端口是 native 协议,8123 是 HTTP 协议。别搞混了。
3.2 集群部署:分片 + 副本
单机搞定了,我们上集群。ClickHouse 的集群模式,核心是两个概念:分片(Shard) 和 副本(Replica)。
- 分片:把数据水平切分,每个分片存一部分数据。解决单机容量瓶颈。
- 副本:每个分片的数据存多份。解决高可用问题。
说白了,分片解决「装不下」,副本解决「挂不掉」。两者通常一起用。
3.2.1 集群拓扑设计
我见过最常用的方案是 2 分片 2 副本,也就是 4 台机器。每台机器既是一个分片的主节点,又是另一个分片的副本节点。
我的经验:别一上来就搞 8 分片 16 副本。从 2 分片 2 副本起步,跑通了再扩。集群规模越大,ZooKeeper 的压力越大。
下面这张图,展示了 2 分片 2 副本的架构:
3.2.2 配置文件详解
集群配置主要在 /etc/clickhouse-server/config.d/ 目录下。我习惯把集群配置拆成独立文件,方便管理。
第一步:配置 metrika.xml(集群拓扑)
<?xml version="1.0"?>
<yandex>
<remote_servers>
<my_cluster>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node1.example.com</host>
<port>9000</port>
</replica>
<replica>
<host>node2.example.com</host>
<port>9000</port>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node3.example.com</host>
<port>9000</port>
</replica>
<replica>
<host>node4.example.com</host>
<port>9000</port>
</replica>
</shard>
</my_cluster>
</remote_servers>
<zookeeper>
<node>
<host>zk1.example.com</host>
<port>2181</port>
</node>
<node>
<host>zk2.example.com</host>
<port>2181</port>
</node>
<node>
<host>zk3.example.com</host>
<port>2181</port>
</node>
</zookeeper>
<macros>
<shard>01</shard>
<replica>node1</replica>
</macros>
</yandex>
注意:internal_replication 设为 true 时,写入由 ClickHouse 自己管理副本同步。设为 false 的话,你需要外部工具(如 Proxy)来分发写入。我建议始终设为 true。
第二步:配置每个节点的 macros
每个节点的 macros 必须不同。它决定了这个节点属于哪个分片、叫什么名字。写错了,副本同步会乱套。
<!-- 节点 A -->
<macros>
<shard>01</shard>
<replica>node1</replica>
</macros>
<!-- 节点 B -->
<macros>
<shard>01</shard>
<replica>node2</replica>
</macros>
3.2.3 创建分布式表
集群配好了,怎么用?你需要建两张表:本地表和分布式表。
-- 1. 在每台节点上创建本地表
CREATE TABLE my_db.events_local ON CLUSTER my_cluster
(
event_date Date,
event_type String,
value UInt32
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}')
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, event_type);
-- 2. 在任意一台节点上创建分布式表
CREATE TABLE my_db.events_distributed ON CLUSTER my_cluster
AS my_db.events_local
ENGINE = Distributed('my_cluster', 'my_db', 'events_local', rand());
为什么本地表要用 ReplicatedMergeTree?因为只有这个引擎支持副本同步。普通 MergeTree 没有副本能力。
分布式表的最后一个参数 rand() 是分片键。你可以换成 cityHash64(event_type) 之类的,让相同类型的数据落到同一个分片。
3.3 ZooKeeper 与 ClickHouse Keeper
副本同步离不开协调服务。早期只能用 ZooKeeper,现在 ClickHouse 官方推出了 Keeper,兼容 ZooKeeper 协议,但更轻量。
3.3.1 选型对比
| 特性 | ZooKeeper | ClickHouse Keeper |
|---|---|---|
| 部署复杂度 | 中等,需要独立 JVM | 低,ClickHouse 自带 |
| 资源消耗 | 高,Java 进程吃内存 | 低,C++ 实现 |
| 兼容性 | 标准 ZooKeeper 协议 | 兼容 ZooKeeper 协议 |
| 运维成本 | 需要单独维护 | 随 ClickHouse 一起升级 |
| 生产成熟度 | 非常成熟 | 较新,但足够稳定 |
我的建议:新项目直接用 ClickHouse Keeper。少维护一套 Java 环境,少操一份心。老项目如果 ZooKeeper 已经跑得很稳,没必要迁移。
3.3.2 Keeper 配置示例
Keeper 的配置在 /etc/clickhouse-server/config.d/keeper.xml:
<yandex>
<keeper_server>
<tcp_port>9181</tcp_port>
<server_id>1</server_id>
<log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
<snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>
<coordination_settings>
<operation_timeout_ms>10000</operation_timeout_ms>
<session_timeout_ms>30000</session_timeout_ms>
<raft_logs_level>information</raft_logs_level>
</coordination_settings>
<raft_configuration>
<server>
<id>1</id>
<hostname>k1.example.com</hostname>
<port>9444</port>
</server>
<server>
<id>2</id>
<hostname>k2.example.com</hostname>
<port>9444</port>
</server>
<server>
<id>3</id>
<hostname>k3.example.com</hostname>
<port>9444</port>
</server>
</raft_configuration>
</keeper_server>
<!-- 启用 Keeper 替代 ZooKeeper -->
<zookeeper>
<node>
<host>k1.example.com</host>
<port>9181</port>
</node>
<node>
<host>k2.example.com</host>
<port>9181</port>
</node>
<node>
<host>k3.example.com</host>
<port>9181</port>
</node>
</zookeeper>
</yandex>
注意 server_id 每个节点必须不同。Raft 协议需要奇数个节点,最少 3 个。
3.4 常见部署问题排查
这部分是我最想分享的。我在项目中踩过的坑,列出来给你参考。
3.4.1 节点无法加入集群
现象:新节点启动后,日志一直报 Connection refused 或 Timeout。
原因:多半是防火墙、hostname 解析、或者端口没开。
排查步骤:
- 检查
telnet node_ip 9000是否通。 - 检查
/etc/hosts里 hostname 是否解析到正确的 IP。 - 检查
listen_host是否设为::或0.0.0.0。
3.4.2 副本同步延迟
现象:查询分布式表,不同节点返回的数据不一致。
原因:ZooKeeper 压力大,或者网络带宽不够。
我曾经遇到过:一次线上事故,ZooKeeper 的磁盘 IO 被打满,导致副本同步延迟了 10 分钟。后来我把 ZooKeeper 的数据目录换成了 SSD,问题解决。
避坑指南:ZooKeeper/Keeper 的数据目录一定要用 SSD。机械盘扛不住频繁的写操作。
3.4.3 数据目录磁盘写满
现象:ClickHouse 进程挂掉,日志报 No space left on device。
原因:数据量增长超出预期,或者合并线程来不及合并临时数据。
解决方案:
- 配置
max_bytes_before_external_group_by和max_memory_usage,防止内存溢出时写大量临时文件。 - 设置
storage_policy,使用多级存储(热数据在 SSD,冷数据在 HDD)。 - 监控磁盘使用率,设置告警阈值 80%。
3.4.4 分布式查询报错
现象:查询分布式表时报 Table doesn't exist 或 Different schema。
原因:各节点的本地表结构不一致,或者表没建全。
我的习惯:每次建表都用 ON CLUSTER my_cluster,确保所有节点同步执行。别手动一台一台建,容易漏。
3.4.5 ZooKeeper 会话超时
现象:ClickHouse 日志频繁出现 Session expired,然后副本同步中断。
原因:ZooKeeper 的 session_timeout 设置太小,或者 ZooKeeper 本身负载过高。
调整建议:
<!-- 在 config.xml 中调大超时时间 -->
<zookeeper>
<session_timeout_ms>60000</session_timeout_ms>
<operation_timeout_ms>30000</operation_timeout_ms>
</zookeeper>
总结一下:部署 ClickHouse 集群,最核心的三件事:
- 网络通 — 防火墙、hostname、端口。
- ZK/Keeper 稳 — SSD 磁盘、奇数节点、超时时间合理。
- 配置一致 — macros、表结构、集群定义,所有节点对齐。
这三件事做好了,集群基本不会出大问题。