3. 部署与配置:单机部署、集群部署与配置项深度解析

部署 ClickHouse,说简单也简单,说复杂也复杂。单机环境几分钟就能跑起来,但真要上生产集群,坑就多了。我最早接触 ClickHouse 时,觉得它跟 MySQL 差不多,结果在 ZooKeeper 上栽了个大跟头。今天咱们就把部署这件事聊透。

3.1 单机部署:从零到可查询

单机部署是基础。我个人习惯用 RPM 包或 DEB 包,干净利落。当然,Docker 也行,但生产环境我建议走系统包管理。

核心要点:单机部署不需要 ZooKeeper,数据只存一份,没有副本概念。适合开发测试、小规模分析场景。

3.1.1 安装步骤(CentOS 为例)

# 添加官方仓库
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://packages.clickhouse.com/rpm/clickhouse.repo

# 安装服务端和客户端
sudo yum install -y clickhouse-server clickhouse-client

# 启动服务
sudo systemctl start clickhouse-server
sudo systemctl enable clickhouse-server

# 验证连接
clickhouse-client --query "SELECT version()"

安装完第一件事是什么?改密码。默认配置是空密码,这在生产环境等于裸奔。我曾经见过一个团队,上线两周才发现数据被人删了——就是因为没改默认密码。

3.1.2 关键配置文件

单机模式下,你主要关注两个文件:

文件路径 作用
/etc/clickhouse-server/config.xml 主配置,监听端口、数据目录、内存限制等
/etc/clickhouse-server/users.xml 用户权限、密码、资源配额

小技巧:别直接改 config.xml。在 /etc/clickhouse-server/config.d/ 下建一个 .xml 文件,覆盖你需要的配置项。这样升级时不会丢失自定义配置。

3.2 集群部署:基于 ZooKeeper / ClickHouse Keeper

集群部署才是重头戏。ClickHouse 的集群依赖一个分布式协调服务来管理副本状态、DDL 同步、分区挂载等。早期只能用 ZooKeeper,现在有了 ClickHouse Keeper——说白了就是 ClickHouse 自己重写的一个 ZooKeeper 兼容服务。

3.2.1 架构概览

先看一张整体架构图,帮你建立全局认知:

ClickHouse 集群部署架构 应用 / 负载均衡 CH 节点 1 (副本A) CH 节点 2 (副本B) CH 节点 3 (副本C) 分片 1(3 副本) Keeper 实例 1 Keeper 实例 2 Keeper 实例 3 说明: • 每个分片包含多个副本,副本之间数据完全一致。 • Keeper 集群负责管理副本状态、选主、DDL 同步。 • 推荐 Keeper 实例数为奇数(3 或 5),保证选举能达成多数。 • 应用层通过负载均衡器将查询分发到任意 CH 节点。

3.2.2 ZooKeeper vs ClickHouse Keeper

怎么选?我直接给结论:新项目无脑上 ClickHouse Keeper。为什么?

对比项 ZooKeeper ClickHouse Keeper
部署复杂度 需要单独维护 Java 环境 随 ClickHouse 一起安装,无额外依赖
资源消耗 内存占用高,GC 问题多 C++ 实现,内存可控
读写性能 一般,大分区下容易超时 快 2-3 倍,我实测过
运维成本 需要专人维护 ZK 集群 运维 ClickHouse 即可

避坑:如果你已经在用 ZooKeeper,迁移到 Keeper 时要小心。我曾经在迁移过程中遇到版本不兼容的问题——旧版 ClickHouse 的某些 DDL 操作依赖 ZK 的特定行为,Keeper 实现有细微差异。建议先在测试环境跑一周。

3.2.3 集群配置示例

假设你有 3 台机器,每台部署一个 ClickHouse 实例和一个 Keeper 实例。配置如下:

第一步:配置 Keeper(每个节点)

<!-- /etc/clickhouse-server/config.d/keeper.xml -->
<clickhouse>
  <keeper_server>
    <tcp_port>9181</tcp_port>
    <server_id>1</server_id>  <!-- 每个节点不同:1, 2, 3 -->
    <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>trace</raft_logs_level>
    </coordination_settings>
    
    <raft_configuration>
      <server>
        <id>1</id>
        <hostname>node1.example.com</hostname>
        <port>9234</port>
      </server>
      <server>
        <id>2</id>
        <hostname>node2.example.com</hostname>
        <port>9234</port>
      </server>
      <server>
        <id>3</id>
        <hostname>node3.example.com</hostname>
        <port>9234</port>
      </server>
    </raft_configuration>
  </keeper_server>
  
  <!-- 启用 Keeper 替代 ZooKeeper -->
  <zookeeper>
    <node>
      <host>node1.example.com</host>
      <port>9181</port>
    </node>
    <node>
      <host>node2.example.com</host>
      <port>9181</port>
    </node>
    <node>
      <host>node3.example.com</host>
      <port>9181</port>
    </node>
  </zookeeper>
</clickhouse>

第二步:配置集群分片与副本

<!-- /etc/clickhouse-server/config.d/cluster.xml -->
<clickhouse>
  <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>
        <replica>
          <host>node3.example.com</host>
          <port>9000</port>
        </replica>
      </shard>
    </my_cluster>
  </remote_servers>
</clickhouse>

注意:internal_replication 设为 true 表示由 ClickHouse 自己管理副本同步,不需要外部工具。这是推荐做法。

3.3 配置项深度解析

配置项太多了,我挑几个最容易踩坑的讲。

3.3.1 内存相关

配置项 默认值 我的建议
max_memory_usage 10G 设为物理内存的 60%-70%
max_memory_usage_for_all_queries 0(不限制) 设为物理内存的 80%,防止 OOM
max_bytes_before_external_group_by 0 设为 max_memory_usage 的一半,触发磁盘排序

嗯,这里要注意。很多人以为内存设得越大越好。其实不是。ClickHouse 的内存管理是「尽力而为」的——它不会像 JVM 那样主动 GC。一旦内存超限,直接 OOM Kill。我遇到过最惨的一次,一个聚合查询把 128G 内存吃满,整个节点挂了,影响了所有查询。

3.3.2 磁盘与合并策略

<merge_tree>
  <max_bytes_to_merge_at_max_space_in_pool>107374182400</max_bytes_to_merge>  <!-- 100G -->
  <max_bytes_to_merge_at_min_space_in_pool>53687091200</max_bytes_to_merge>   <!-- 50G -->
  <parts_to_throw_insert>300</parts_to_throw_insert>
  <parts_to_delay_insert>150</parts_to_delay_insert>
</merge_tree>

这几个参数控制着分区合并的行为。说白了,ClickHouse 写入时生成小分区,后台慢慢合并成大分区。如果写入太快,分区数暴增,查询性能会断崖式下跌。

我曾经遇到一个客户,每秒写入 10 万行,分区数涨到 5000 多,查询直接超时。后来把 parts_to_throw_insert 从 300 调到 600,同时加大了合并线程数,才稳住。

3.3.3 网络与超时

配置项 说明 推荐值
connect_timeout_with_failover_ms 节点故障时的连接超时 1000(1秒)
receive_timeout 接收数据超时 300(秒)
send_timeout 发送数据超时 300(秒)

避坑:网络超时别设太大。我见过有人设 3600 秒,结果一个节点挂了,所有查询都卡在那等超时,整个集群被拖死。设个合理的值,让查询快速失败,比死等强。

3.4 部署后的验证清单

部署完别急着上线。按这个清单检查一遍:

  1. 节点连通性:用 clickhouse-client --host 测试每个节点
  2. 集群状态:SELECT * FROM system.clusters 确认所有节点可见
  3. 副本同步:建一个复制表,写入数据,检查 system.replicas 的 is_readonly 和 future_parts
  4. Keeper 健康:echo ruok | nc localhost 9181 返回 imok
  5. 权限验证:用非默认用户登录,确认密码生效