/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
ClickHouse高可用架构:基于ReplicatedMergeTree与ZooKeeper/Keeper的容灾方案
本文将详细介绍ClickHouse中实现高可用的核心方案——ReplicatedMergeTree引擎结合ZooKeeper或Keeper的副本同步机制,以及如何在故障场景下实现自动恢复。通过学习本文,您将掌握构建稳定可靠的ClickHouse集群的关键技术。
1. ReplicatedMergeTree引擎原理
ReplicatedMergeTree是ClickHouse内置的表引擎,专门为实现数据副本和故障恢复而设计。它通过协调服务(ZooKeeper或Keeper)管理多个副本之间的数据同步,确保数据一致性和高可用性。
1.1 核心概念
ReplicatedMergeTree引擎涉及几个关键概念:
- 会话ID:标识一次复制会话,确保事务性操作的一致性
- 副本ID:集群中每个副本的唯一标识
- ZooKeeper/Keeper路径:存储元数据和复制命令的路径
- 数据分片:按照分键将数据分散存储
- 副本:同一分片的多个实例,保证冗余和可用性
1.2 工作原理
ReplicatedMergeTree的工作流程如下:
这种机制保证了即使主节点故障,从节点也拥有完整的数据副本,可以在故障切换后继续提供服务。
— 创建ReplicatedMergeTree表示例
CREATE TABLE example_table (
id UInt32,
name String,
created DateTime
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/example_table', '{replica}')
PARTITION BY toYYYYMM(created)
ORDER BY id;
在这个示例中,/clickhouse/tables/{shard}/example_table是ZooKeeper/Keeper中的路径,用于协调复制过程;{replica}是当前副本的标识符。
2. 基于ZooKeeper的副本同步机制
ZooKeeper作为成熟的分布式协调服务,被广泛用于ClickHouse的副本同步管理。它提供了可靠的数据存储和事件通知机制,确保副本间的数据一致性。
2.1 ZooKeeper在ClickHouse中的作用
在ClickHouse集群中,ZooKeeper主要负责以下任务:
- 元数据管理:存储表结构、分区信息和复制元数据
- 副本协调:跟踪各副本状态和同步进度
- 主节点选举:在故障场景下自动选择新的主节点
- 分布式锁管理:确保写入操作的原子性
2.2 副本同步流程
ZooKeeper实现的副本同步过程如下:
#publish-mermaid-0-1789523937237{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-0-1789523937237 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-0-1789523937237 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-0-1789523937237 .error-icon{fill:#552222;}#publish-mermaid-0-1789523937237 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-0-1789523937237 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-0-1789523937237 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-0-1789523937237 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-0-1789523937237 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-0-1789523937237 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-0-1789523937237 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-0-1789523937237 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-0-1789523937237 .marker.cross{stroke:#333333;}#publish-mermaid-0-1789523937237 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-0-1789523937237 p{margin:0;}#publish-mermaid-0-1789523937237 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-0-1789523937237 .cluster-label text{fill:#333;}#publish-mermaid-0-1789523937237 .cluster-label span{color:#333;}#publish-mermaid-0-1789523937237 .cluster-label span p{background-color:transparent;}#publish-mermaid-0-1789523937237 .label text,#publish-mermaid-0-1789523937237 span{fill:#333;color:#333;}#publish-mermaid-0-1789523937237 .node rect,#publish-mermaid-0-1789523937237 .node circle,#publish-mermaid-0-1789523937237 .node ellipse,#publish-mermaid-0-1789523937237 .node polygon,#publish-mermaid-0-1789523937237 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-0-1789523937237 .rough-node .label text,#publish-mermaid-0-1789523937237 .node .label text,#publish-mermaid-0-1789523937237 .image-shape .label,#publish-mermaid-0-1789523937237 .icon-shape .label{text-anchor:middle;}#publish-mermaid-0-1789523937237 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-0-1789523937237 .rough-node .label,#publish-mermaid-0-1789523937237 .node .label,#publish-mermaid-0-1789523937237 .image-shape .label,#publish-mermaid-0-1789523937237 .icon-shape .label{text-align:center;}#publish-mermaid-0-1789523937237 .node.clickable{cursor:pointer;}#publish-mermaid-0-1789523937237 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-0-1789523937237 .arrowheadPath{fill:#333333;}#publish-mermaid-0-1789523937237 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-0-1789523937237 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-0-1789523937237 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-0-1789523937237 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-0-1789523937237 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-0-1789523937237 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-0-1789523937237 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-0-1789523937237 .cluster text{fill:#333;}#publish-mermaid-0-1789523937237 .cluster span{color:#333;}#publish-mermaid-0-1789523937237 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-0-1789523937237 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-0-1789523937237 rect.text{fill:none;stroke-width:0;}#publish-mermaid-0-1789523937237 .icon-shape,#publish-mermaid-0-1789523937237 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-0-1789523937237 .icon-shape p,#publish-mermaid-0-1789523937237 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-0-1789523937237 .icon-shape .label rect,#publish-mermaid-0-1789523937237 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-0-1789523937237 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-0-1789523937237 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-0-1789523937237 .node .neo-node{stroke:#9370DB;}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node rect,#publish-mermaid-0-1789523937237 [data-look=\”neo\”].cluster rect,#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789523937237 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789523937237 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
客户端写入数据
主节点接收写入
数据写入本地磁盘
在ZooKeeper中创建任务日志
从节点监听ZooKeeper变化
从节点获取任务日志
从节点执行数据复制
所有副本确认完成
2.3 配置要点
启用ZooKeeper副本同步的配置参数:
<!– clickhouse-server.xml –>
<zookeeper>
<node index="1">
<host>zookeeper1</host>
<port>2181</port>
</node>
<node index="2">
<host>zookeeper2</host>
<port>2181</port>
</node>
<node index="3">
<host>zookeeper3</host>
<port>2181</port>
</node>
</zookeeper>
<replication>
<zk_session_timeout_ms>30000</zk_session_timeout_ms>
<zk_lock_timeout_ms>10000</zk_lock_timeout_ms>
<use_zk_directories>true</use_zk_directories>
<security_manager_update_period_ms>60</security_manager_update_period_ms>
</replication>
关键参数说明:
- zk_session_timeout_ms:ZooKeeper会话超时时间
- zk_lock_timeout_ms:分布式锁超时时间
- use_zk_directories:是否使用ZooKeeper管理目录
- security_manager_update_period_ms:安全管理器更新周期
3. 基于Keeper的新一代协调服务
Keeper是ClickHouse官方推出的新一代协调服务,作为ZooKeeper的分支项目,专为ClickHouse场景优化,提供更好的性能和集成度。
3.1 Keeper与ZooKeeper的对比
| 原生集成 | 需额外配置 | 完全集成 |
| 性能 | 中等 | 高性能优化 |
| 适用规模 | 中小规模 | 大规模集群 |
| 维护复杂度 | 较高 | 较低 |
| 功能丰富度 | 丰富 | 专注副本同步 |
Keeper的优势:
3.2 配置Keeper
启用Keeper服务相对简单,主要修改配置文件:
<!– clickhouse-server.xml –>
<keeper_server>
<tcp_port>9181</tcp_port>
<server_id>1</server_id>
<log_storage_path>/var/lib/clickhouse/keeper/log</log_storage_path>
<snapshot_storage_path>/var/lib/clickhouse/keeper/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>
</keeper_server>
<zookeeper>
<node>
<host>keeper1</host>
<port>9181</port>
</node>
<node>
<host>keeper2</host>
<port>9181</port>
</node>
<node>
<host>keeper3</host>
<port>9181</port>
</node>
</zookeeper>
关键配置项说明:
- keeper_server:Keeper服务配置,端口、存储路径等
- zookeeper:指定Keeper节点地址
- operation_timeout_ms:操作超时时间
- session_timeout_ms:会话超时时间
- raft_logs_level:Raft日志级别
3.3 使用场景
Keeper适用于以下场景:
对于中小规模集群,ZooKeeper仍然是成熟的选择;而对于大规模生产环境,Keeper提供了更好的性能和集成体验。
4. 故障恢复与自动切换实践
ClickHouse的高可用不仅仅是数据复制,还包括故障检测、自动切换和恢复机制。本节将详细介绍这些机制的工作原理和实践方法。
4.1 故障检测机制
ClickHouse通过以下机制检测节点和副本状态:
当检测到节点故障时,ClickHouse会:
- 标记故障节点为"不可用"
- 通知所有节点更新集群状态
- 如果故障的是主节点,触发选举机制
4.2 主节点故障切换
主节点故障切换过程如下:
— 查看副本状态
SELECT * FROM system.replicas;
— 强制手动故障转移(在紧急情况下使用)
ALTER TABLE example_table UPDATE replica_status = 'active' WHERE replica_host = '故障节点';
4.3 从节点恢复
从节点恢复步骤:
— 检查从节点是否与主节点数据一致
SELECT count() FROM example_table WHERE replica_name = '从节点名称';
4.4 监控与告警
建立完善的监控与告警机制对高可用至关重要:
5. 最小示例与注意事项
5.1 最小配置示例
以下是一个三节点的ClickHouse集群高可用配置示例:
<!– clickhouse-server.xml –>
<remote_servers>
<cluster>
<shard>
<replica>
<host>node1</host>
<port>9000</port>
</replica>
<replica>
<host>node2</host>
<port>9000</port>
</replica>
</shard>
</cluster>
</remote_servers>
<zookeeper>
<node>
<host>zookeeper1</host>
<port>2181</port>
</node>
<node>
<host>zookeeper2</host>
<port>2181</port>
</node>
<node>
<host>zookeeper3</host>
<port>2181</port>
</node>
</zookeeper>
<users>
<replica_user>
<password>replica_password</password>
<networks>
<ip>::/0</ip>
</networks>
<profile>replica</profile>
</replica_user>
</users>
<profiles>
<replica>
<readonly>0</readonly>
</replica>
</profiles>
创建副本表的SQL:
— 在node1上创建主节点表
CREATE TABLE example_table ON CLUSTER cluster (
id UInt32,
name String,
created DateTime
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/example_table', '{replica}')
PARTITION BY toYYYYMM(created)
ORDER BY id;
— 在node2上创建从节点表
CREATE TABLE example_table ON CLUSTER cluster (
id UInt32,
name String,
created DateTime
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/example_table', '{replica}')
PARTITION BY toYYYYMM(created)
ORDER BY id;
5.2 注意事项
- 确保节点间网络稳定,低延迟
- 配置合理的超时参数
- 考虑使用专用网络或VLAN
- 为每个节点预留足够的磁盘空间
- 监控磁盘使用率,避免空间不足导致同步失败
- 考虑数据清理策略,定期归档或删除旧数据
- 根据集群规模调整replication_alter_partitions_sync_timeout
- 设置合适的zk_session_timeout_ms和zk_lock_timeout_ms
- 根据数据量调整parts_to_insert_delayed等参数
- 监控system.replicas表了解副本状态
- 查看system.query_log排查同步问题
- 使用EXCEPT语法检查副本间数据一致性
- 定期备份重要数据
- 建立完善的监控和告警机制
- 进行故障演练,验证自动恢复机制
- 记录集群配置变更,便于故障排查
通过以上配置和注意事项,您可以构建一个稳定可靠的ClickHouse高可用集群,确保数据安全和业务连续性。
