上一篇【第43篇】ClickHouse时间日期函数与JSON解析
下一篇【第45篇】ClickHouse数据副本_ZooKeeper配置与副本定义
摘要
本文是《Clickhouse从入门到精通》系列博客的第44篇文章,深入介绍ClickHouse分布式架构的两大核心概念:副本(Replica)与分片(Shard)。文章从单机部署的局限性出发,详细阐述副本与分片的设计理念、核心作用、实现机制,以及两者在不同场景下的组合方式。通过对比表格和实战案例,帮助读者理解如何根据具体业务需求规划合理的分布式集群架构,为后续深入副本配置与ReplicatedMergeTree原理打下坚实基础。
关键词:副本、分片、分布式架构、高可用、水平扩展
1. 引言
在前面43篇文章中,我们系统地学习了ClickHouse的方方面面:从基础架构、数据类型、表引擎、查询语法,到 MergeTree 系列引擎的底层原理、数据分区与索引机制、主索引与跳数索引、SET和JOIN引擎、时间日期函数与JSON解析等。所有这些内容,都是建立在单机ClickHouse实例的基础之上的。
然而,在生产环境中,单机部署往往无法满足企业级应用的需求。当数据量突破单机存储上限、当查询并发超过单机处理能力、当硬件故障可能导致服务不可用——这些场景都逼迫我们走向分布式架构。
ClickHouse的分布式架构设计,围绕两个核心概念展开:副本(Replica)和分片(Shard)。理解这两个概念,是掌握ClickHouse分布式部署的前提。
本文作为分布式系列的开篇,将从宏观视角带领读者一览ClickHouse分布式架构的全貌,理解副本与分片各自解决什么问题、如何协同工作,以及如何根据实际业务场景选择合适的架构组合。
2. 单机局限与分布式需求
2.1 单机部署的典型瓶颈
在开发测试环境或数据量较小的场景下,单机ClickHouse实例完全可以胜任。但随着业务增长,单机部署会逐渐暴露出以下瓶颈:
存储容量瓶颈
ClickHouse虽然以高效压缩著称,但单机磁盘容量终究有限。当数据量达到TB甚至PB级别时,单块磁盘或RAID阵列都无法容纳。即便使用大容量磁盘,备份和恢复的时间成本也会急剧上升。
-- 单机场景下,查看当前数据量SELECTdatabase,table,formatReadableSize(sum(bytes_on_disk))ASsize_on_disk,sum(rows)AStotal_rowsFROMsystem.partsWHEREactive=1GROUPBYdatabase,tableORDERBYsum(bytes_on_disk)DESCLIMIT10;计算能力瓶颈
ClickHouse的并行计算能力在单机范围内已经非常出色(利用多个CPU核心和多块磁盘),但当查询并发量大幅上升,或者需要执行极其复杂的聚合查询时,单机CPU和内存资源终究会达到上限。
高可用缺失
单机部署意味着单点故障(Single Point of Failure, SPOF)。一旦服务器宕机、磁盘损坏,或者操作系统出现问题,整个ClickHouse服务将完全不可用,直到故障恢复。对于需要7×24小时可用的生产系统而言,这是不可接受的。
备份与恢复风险
即使有定期备份策略,从备份中恢复TB级数据也需要相当长的时间。在恢复期间,服务处于不可用状态,这对业务造成的影响可能非常严重。
2.2 分布式架构解决的核心问题
ClickHouse的分布式架构设计,旨在通过以下方式解决上述瓶颈:
| 核心需求 | 解决方案 | ClickHouse实现机制 |
|---|---|---|
| 数据量超出单机存储上限 | 数据分片(Sharding) | 将数据水平拆分到多个节点 |
| 高可用与故障切换 | 数据副本(Replication) | 多节点存储相同数据副本 |
| 读查询负载均衡 | 副本读取分摊 | 分布式查询自动路由 |
| 写吞吐扩展 | 分片写入并行化 | 数据按分片键分发 |
| 在线横向扩展 | 增加分片节点 | 弹性扩缩容 |
3. 副本(Replica)核心概念
3.1 副本的作用
副本(Replica),指的是在多台物理服务器上存储相同数据的拷贝。在ClickHouse中,副本机制主要由ReplicatedMergeTree系列表引擎来实现。
副本机制解决的核心问题包括:
高可用性(High Availability)
当某个副本节点发生硬件故障、操作系统崩溃或网络分区时,集群中的其他副本节点可以立即接管服务,保证数据的可读性和可写性。这是生产环境部署的最低要求。
故障切换(Failover)
ClickHouse的副本机制支持自动故障检测和切换。当leader副本不可用时,其他副本会自动选举新的leader,无需人工干预。
读负载均衡(Read Load Balancing)
多个副本都可以对外提供读服务。当查询并发量较高时,可以将读请求分摊到不同的副本节点上,从而提升整体的查询吞吐能力。写请求通常由leader副本协调,但也可以通过配置实现更复杂的写入策略。
-- 查看各副本的数据同步状态SELECTdatabase,table,replica_name,is_leader,is_readonly,total_replicas,active_replicasFROMsystem.replicasORDERBYdatabase,table;3.2 副本实现机制:基于ZooKeeper协调的ReplicatedMergeTree
ClickHouse的副本机制并非通过传统的主从复制(Master-Slave Replication)实现,而是基于ZooKeeper(或使用ClickHouse Keeper作为替代)进行协调的ReplicatedMergeTree引擎。
其核心工作流程如下:
- 写入操作:客户端向任意副本节点发起INSERT请求,该节点作为本次写入的协调者,将操作日志写入ZooKeeper的
/log路径 - 日志分发:其他副本节点监听ZooKeeper中的
/log路径,发现新日志后拉取并执行 - 数据传输:实际的数据文件并不经过ZooKeeper传输,而是通过ClickHouse节点之间的HTTP通信直接传输,保证了数据传输的高效性
- 最终一致性:由于写入是异步同步的,不同副本之间可能存在短暂的数据不一致,但最终会达到一致状态
这种设计的核心优势在于:ZooKeeper只存储元数据和小量的协调信息,不存储实际数据,从而避免了ZooKeeper成为性能瓶颈。
3.3 副本与备份的区别
很多初学者容易混淆"副本"与"备份"的概念,二者有本质区别:
| 对比维度 | 副本(Replica) | 备份(Backup) |
|---|---|---|
| 用途 | 实时高可用、故障切换 | 灾难恢复、数据归档 |
| 数据时效性 | 近实时(秒级延迟) | 取决于备份频率(小时/天级) |
| 自动化程度 | 全自动同步 | 通常需手动或定时任务 |
| 恢复速度 | 秒级(自动切换) | 分钟到小时级 |
| 存储位置 | 通常同机房或近机房 | 可跨地域、跨云存储 |
| 是否参与查询 | 是(读负载均衡) | 否(仅供恢复) |
| ClickHouse机制 | ReplicatedMergeTree + ZK | BACKUP/RESTORE 或文件系统快照 |
重要结论:副本不能替代备份。即便有了多副本,仍然需要定期备份策略来应对误删除、数据损坏、以及机房级别灾难等极端场景。
4. 分片(Shard)核心概念
4.1 分片的作用
分片(Shard),指的是将不同的数据分布到多个物理节点上,每个节点只存储整体数据的一个子集。分片机制解决的核心问题是水平扩展(Horizontal Scaling)。
与副本"复制相同数据"的理念完全不同,分片的核心是"切分不同数据":
突破单机存储上限
通过将数据分散到多个节点,整个集群的总存储容量等于各分片存储容量之和。理论上,分片数量可以无限扩展(实际受协调开销限制),从而实现PB级甚至EB级的数据存储。
突破单机计算上限
查询可以在多个分片上并行执行,每个分片只处理自己持有的数据子集,从而将查询性能与分片数量近似呈线性关系提升。
-- 查看各分片的数据分布情况SELECTshard_num,count()ASparts_count,formatReadableSize(sum(bytes_on_disk))ASsize_on_disk,sum(rows)AStotal_rowsFROMclusterAllReplicas('cluster_name',system,parts)WHEREactive=1GROUPBYshard_numORDERBYshard_num;4.2 分片与分区(PARTITION BY)的区别
这是ClickHouse学习中最容易混淆的概念之一。许多用户误以为"分区"就是"分片",实际上二者完全不同:
| 对比维度 | 分片(Shard) | 分区(Partition) |
|---|---|---|
| 作用范围 | 跨物理节点 | 单节点内部 |
| 目的 | 水平扩展、突破单机上限 | 数据管理、分区裁剪 |
| 数据存储 | 不同节点存不同数据 | 同一节点存不同目录 |
| 查询并行 | 跨节点并行 | 节点内多线程并行 |
| 配置方式 | 集群配置 + 分布式表 | CREATE TABLE时PARTITION BY |
| 失败影响 | 分片故障影响部分数据 | 分区故障仅影响该分区 |
| 典型数量级 | 2-几十个 | 几十-几百个 |
简单记忆:分区是"竖着切"(按时间或其他维度将表内数据分组管理),分片是"横着切"(将数据分布到不同机器上)。
4.3 分片键的设计原则
分片键(Sharding Key)决定了数据行会被分配到哪个分片。合理的分片键设计对集群性能有决定性影响。
分片键的选择原则:
- 均匀分布:分片键的哈希值应尽可能均匀地分布到各个分片,避免出现"数据倾斜"(某些分片数据量远大于其他分片)
- 查询友好:常见的查询条件如果包含分片键,可以实现"分区裁剪"效果,只查询相关分片
- 避免频繁变更:分片键一旦确定,修改成本很高(需要重新分布数据)
- 离散度高:选择基数高的列作为分片键,如用户ID、订单ID等
-- 使用分布式表引擎时指定分片键CREATETABLEdistributed_table(user_id UInt64,event_timeDateTime,event_type String,amount Float64)ENGINE=Distributed('cluster_name','default','local_table',sipHash64(user_id));-- ^^^^^^^^^^^^^^^^-- 分片键:对user_id做哈希常见分片策略对比:
| 分片策略 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 哈希分片(sipHash64) | 通用场景 | 数据分布均匀 | 范围查询需跨所有分片 |
| 取模分片(% shard_count) | 分片数固定 | 简单直观 | 扩缩容需重新分布 |
| 范围分片 | 时间序数据 | 时间范围查询高效 | 易产生数据倾斜 |
| 一致性哈希 | 动态扩缩容 | 扩缩容影响小 | 实现复杂 |
5. 副本与分片的组合方式
5.1 四种基本组合模式
副本和分片是两个正交的概念,可以独立使用,也可以组合使用。根据业务需求的不同,可以采用以下四种基本组合模式:
模式一:单副本单分片(Standalone)
┌─────────────┐ │ 节点 A │ ← 唯一节点,无副本,无分片 │ (所有数据) │ └─────────────┘- 描述:即标准的单机部署,不使用副本也不使用分片
- 优点:部署简单,运维成本低,无额外开销
- 缺点:无高可用,无扩展能力
- 适用场景:开发测试环境、数据量小的非关键业务
模式二:多副本单分片(Replication Only)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 节点 A │ │ 节点 B │ │ 节点 C │ │ (副本1) │←─副本同步──→│ (副本2) │←─副本同步──→│ (副本3) │ │ (所有数据) │ │ (所有数据) │ │ (所有数据) │ └─────────────┘ └─────────────┘ └─────────────┘- 描述:数据在多个节点上各有完整副本,但数据不分片
- 优点:高可用(任意节点故障不影响服务),读负载可分摊
- 缺点:存储容量不扩展(每个节点存全量数据),写性能无提升
- 适用场景:数据量在单机可承受范围内,但对高可用有强需求的场景
模式三:单副本多分片(Sharding Only)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 节点 A │ │ 节点 B │ │ 节点 C │ │ (分片1) │ │ (分片2) │ │ (分片3) │ │ (1/3数据) │ │ (1/3数据) │ │ (1/3数据) │ └─────────────┘ └─────────────┘ └─────────────┘- 描述:数据按分片键分布到多个节点,但每个分片只有一份拷贝
- 优点:存储和计算能力可水平扩展,适合大规模数据存储
- 缺点:无高可用(任何节点故障都会导致部分数据不可用)
- 适用场景:数据可重新生成或从其他源重新导入的场景(如日志分析),对可用性要求不高
模式四:多副本多分片(Production Recommended)
┌──────────────────────────────────────────────────┐ │ 分片1 │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 节点 A │←─副本同步──→│ 节点 B │ │ │ │ (副本1) │ │ (副本2) │ │ │ └─────────────┘ └─────────────┘ │ └──────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────┐ │ 分片2 │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 节点 C │←─副本同步──→│ 节点 D │ │ │ │ (副本1) │ │ (副本2) │ │ │ └─────────────┘ └─────────────┘ │ └──────────────────────────────────────────────────┘- 描述:每个分片配置多个副本,兼顾水平扩展和高可用
- 优点:既能水平扩展存储和计算,又具备高可用能力
- 缺点:硬件成本较高(数据冗余存储),运维复杂度增加
- 适用场景:生产环境的标准配置,尤其是对可用性和扩展性都有要求的场景
5.2 架构组合对比表
| 架构模式 | 高可用 | 水平扩展 | 读负载均衡 | 硬件成本 | 运维复杂度 | 生产推荐 |
|---|---|---|---|---|---|---|
| 单副本单分片 | ❌ | ❌ | ❌ | 低 | 低 | ❌ |
| 多副本单分片 | ✅ | ❌ | ✅ | 中 | 中 | 部分场景 |
| 单副本多分片 | ❌ | ✅ | ⚠️(仅分片内) | 中 | 中 | ❌ |
| 多副本多分片 | ✅ | ✅ | ✅ | 高 | 高 | ✅ |
6. ClickHouse集群架构中的关键组件
6.1 ZooKeeper / ClickHouse Keeper的角色
在ClickHouse分布式架构中,ZooKeeper(或使用ClickHouse内置的ClickHouse Keeper)承担着集群协调的核心职责。它是副本机制正常工作的基础,但本身不存储任何实际数据。
ZooKeeper在ClickHouse集群中的具体职责包括:
- 副本元数据管理:存储每个副本的元数据信息,包括副本名称、所在节点、同步状态等
- 操作日志协调:管理副本间的数据同步日志(
/log路径),确保各副本按相同顺序执行操作 - Leader选举:当当前leader副本不可用时,协调选举新的leader副本
- DDL查询协调:在执行分布式DDL(如
CREATE TABLE ... ON CLUSTER)时,协调各节点执行 - INSERT去重:通过记录数据块哈希,防止重复写入
ClickHouse节点与ZooKeeper的关系: ┌─────────────────────────────────────┐ │ ZooKeeper集群 │ │ (存储元数据、协调信息、日志) │ └──────────┬──────────────────────────┘ │ 监听/写入元数据 ┌───────┴───────┐ ▼ ▼ ┌─────────┐ ┌─────────┐ │ ClickHouse│ │ ClickHouse│ │ 节点 A │ │ 节点 B │ │ (副本1) │ │ (副本2) │ └─────────┘ └─────────┘ │ │ └───────数据同步───────┘ (实际数据通过HTTP传输,不经过ZK)重要提示:随着集群规模增大,ZooKeeper可能成为性能瓶颈。ClickHouse Keeper是ClickHouse官方提供的ZooKeeper替代方案,使用Raft共识算法,性能更好,运维更简单,是新建集群的推荐选择。
6.2 Distributed表引擎
Distributed表引擎是ClickHouse实现分布式查询的核心组件。它本身不存储任何数据,而是作为一个"路由层",将查询转发到对应的本地表,然后汇总结果返回给客户端。
-- Distributed表引擎的建表语法CREATETABLEdistributed_tableENGINE=Distributed('cluster_name',-- 集群名称(在config.xml中定义)'database',-- 本地表所在的数据库'local_table',-- 本地表名称sipHash64(id)-- 分片键(用于写入时数据分发))Distributed表的核心功能:
- 查询路由:接收客户端查询,将查询发送到相关分片,合并结果返回
- 写入分发:接收客户端写入,根据分片键将数据和distribution到对应的分片
- 跨分片聚合:在协调节点上对各个分片返回的部分结果进行二次聚合
重要注意事项:
Distributed表不存储数据,必须配合本地表(通常是ReplicatedMergeTree表)使用- 查询通过
Distributed表时,会在协调节点上产生额外的计算开销(二次聚合) - 写入通过
Distributed表时,如果目标分片不可达,数据可能丢失(需配置合适的写入策略)
6.3 ReplicatedMergeTree系列引擎
ReplicatedMergeTree是支持副本机制的MergeTree家族表引擎。它在普通MergeTree的基础上,增加了基于ZooKeeper的副本协调功能。
ReplicatedMergeTree系列包括:
| 引擎名称 | 对应基础引擎 | 额外功能 |
|---|---|---|
| ReplicatedMergeTree | MergeTree | 基础副本引擎 |
| ReplicatedReplacingMergeTree | ReplacingMergeTree | 副本 + 去重替换 |
| ReplicatedSummingMergeTree | SummingMergeTree | 副本 + 自动汇总 |
| ReplicatedAggregatingMergeTree | AggregatingMergeTree | 副本 + 聚合状态 |
| ReplicatedCollapsingMergeTree | CollapsingMergeTree | 副本 + 行折叠 |
| ReplicatedVersionedCollapsingMergeTree | VersionedCollapsingMergeTree | 副本 + 版本折叠 |
在后续文章中,我们将深入解析ReplicatedMergeTree的工作原理。
7. 生产级集群规划建议
7.1 分片数选择原则
选择分片数量时,需要综合考虑数据量、查询性能、运维复杂度等多个因素:
数据量维度
每个分片承载的数据量不宜过大。根据社区最佳实践,建议每个分片的数据量控制在10TB以内(具体取决于硬件配置)。数据量过大会导致:
- 单次Merge操作耗时过长
- 节点故障恢复时间过久
- 备份恢复窗口过大
分片数 ≈ CEILING(总数据量 / 单分片建议上限) 例如:总数据量50TB,单分片上限10TB → 至少需要5个分片查询性能维度
更多的分片意味着查询可以并行在更多节点上执行,从而提升查询速度。但分片数过多也会带来协调开销的增加。根据经验:
- OLAP场景(大量全表扫描):分片数与CPU核心总数呈近似线性关系
- 点查场景(按主键查询):分片数不宜过多,避免增加网络路由开销
运维复杂度维度
分片数越多,集群的运维复杂度越高。每个分片都可能需要独立的监控、备份策略。建议分片数保持在几十个以内,超过100个分片会带来显著的运维负担。
7.2 副本数选择原则
副本数的黄金法则:奇数副本
与分布式系统的通用实践一致,ClickHouse副本数建议使用奇数(1、3、5)。这是因为:
- 支持法定人数(Quorum)写入确认
- 在
insert_quorum配置下,可容忍(N-1)/2个副本故障而不影响写入
| 副本数 | 可容忍故障数 | 写入确认节点数 | 存储开销 | 适用场景 |
|---|---|---|---|---|
| 1 | 0 | 1 | 1× | 开发测试 |
| 2 | 0(偶数,不推荐) | 2 | 2× | 不推荐 |
| 3 | 1 | 2 | 3× | 生产环境推荐 |
| 5 | 2 | 3 | 5× | 金融级高可用 |
生产环境推荐配置:3副本(即每个分片有3份数据拷贝),可以在容忍1个副本故障的同时,提供良好的读负载均衡能力。
7.3 硬件规划建议
| 组件 | 推荐配置 | 说明 |
|---|---|---|
| CPU | 16-64核 | 核数越多,并行查询性能越好 |
| 内存 | 64-256GB | 大内存可缓存更多热数据 |
| 磁盘 | SSD或NVMe | 避免使用HDD,Merge操作对磁盘IO要求高 |
| 网络 | 10Gbps+ | 副本间数据同步需要高带宽低延迟网络 |
| ZooKeeper节点 | 3或5节点 | 独立部署,不与ClickHouse混布 |
8. 实战案例:规划一个支持百亿级数据的生产分布式集群架构方案
8.1 业务需求分析
假设我们有一个互联网广告投放分析系统,需要满足以下需求:
- 数据规模:每日新增10亿条广告曝光记录,每条约200字节,日增约200GB;保留3年数据,总量约216TB
- 查询场景:以时间范围查询为主,辅以广告主ID、广告计划ID的等值查询;有一定量的多表JOIN操作
- 可用性要求:99.9% SLA,允许每年不超过8.76小时的停机时间
- 写入要求:支持实时写入,端到端延迟不超过10秒
8.2 架构方案设计
基于以上需求,我们设计如下分布式集群架构:
集群拓扑:
总数据量:216TB(压缩后约54TB,按4:1压缩比) 副本策略:每个分片3副本(高可用) 分片数量:6个分片(每分片约9TB数据,在10TB建议上限内) 分片1:节点A(副本1) | 节点B(副本2) | 节点C(副本3) 分片2:节点D(副本1) | 节点E(副本2) | 节点F(副本3) 分片3:节点G(副本1) | 节点H(副本2) | 节点I(副本3) 分片4:节点J(副本1) | 节点K(副本2) | 节点L(副本3) 分片5:节点M(副本1) | 节点N(副本2) | 节点O(副本3) 分片6:节点P(副本1) | 节点Q(副本2) | 节点R(副本3) ZooKeeper集群:3节点(独立部署)节点硬件配置:
| 规格项 | 配置 | 理由 |
|---|---|---|
| CPU | 32核 | 支持高并发查询 |
| 内存 | 128GB | 缓存热数据,加速查询 |
| 磁盘 | 2TB NVMe SSD × 6(RAID10) | 约12TB可用,容纳9TB数据+缓冲 |
| 网络 | 10Gbps | 副本同步带宽保障 |
分片键设计:
考虑到查询以时间范围 + 广告主ID为主,选择sipHash64(advertiser_id)作为分片键:
- 广告主ID基数高,哈希分布均匀
- 同一广告主的数据集中在少数分片,有利于缓存命中
-- 本地表(在每个节点上创建)CREATETABLEad_impressions_local(event_dateDate,event_timeDateTime,advertiser_id UInt64,campaign_id UInt64,impression_id String,cost Float64,-- ... 其他字段)ENGINE=ReplicatedMergeTree('/clickhouse/tables/{shard}/default/ad_impressions','{replica}')PARTITIONBYtoYYYYMM(event_date)ORDERBY(advertiser_id,event_date,event_time);-- 分布式表(用于应用层访问)CREATETABLEad_impressionsENGINE=Distributed('ad_cluster','default','ad_impressions_local',sipHash64(advertiser_id));高可用保障:
- 每个分片3副本,允许1个副本故障不影响服务
- ZooKeeper 3节点部署,允许1个节点故障
- 定期备份到对象存储(如S3),用于灾难恢复
9. 总结与最佳实践
本文从宏观视角系统地介绍了ClickHouse分布式架构的两大支柱——副本与分片。以下是本文的核心要点总结:
核心概念回顾:
- 副本解决高可用问题:多节点存储相同数据,支持故障自动切换和读负载均衡
- 分片解决扩展性问题:数据水平拆分到多节点,突破单机存储和计算上限
- 副本与分片是正交概念,可独立使用,也可组合使用
架构选择建议:
- 生产环境应使用多副本多分片架构,通常每个分片配置3副本
- 分片数根据数据量和查询性能需求综合确定,建议控制在几十个以内
- 每个分片的数据量建议控制在10TB以内
技术选型建议:
- 新集群优先使用ClickHouse Keeper替代ZooKeeper,性能更好且运维更简单
- 使用
ReplicatedMergeTree系列引擎实现副本机制 - 使用
Distributed表引擎作为分布式查询的入口层
下一步学习:
在下一篇文章中,我们将深入讲解ClickHouse数据副本的具体配置方法,包括ZooKeeper/ClickHouse Keeper的详细配置、ReplicatedMergeTree的建表语法、宏变量(macros)的使用,以及完整的2分片2副本集群搭建实战。
上一篇【第43篇】ClickHouse时间日期函数与JSON解析
下一篇【第45篇】ClickHouse数据副本_ZooKeeper配置与副本定义