土法炼钢兴趣小组的算法知识备份

【列存引擎内核】Distributed 引擎与分布式查询路由

文章导航

分类入口
databasedistributed
标签入口
#clickhouse#distributed-table#sharding#global-join#mergetree#cluster#query-routing

目录

单节点 MergeTree 能撑住不少 OLAP 场景,但日志、指标、宽表事实一旦超过单机磁盘或 CPU 上限,就要把 同一张逻辑表 切到多个 shard 上。ClickHouse 的 Distributed 引擎不是「又一种存储格式」,而是 查询与写入的路由层:本地表(MergeTree / ReplicatedMergeTree)才真正持有 Part;Distributed 表只保存集群拓扑与分片函数。

ReplicatedMergeTree(第 08 篇) 解决 副本一致性与 HA;本文解决 水平分片与跨节点查询。读完应能回答:分片键怎么选、INSERT 落到哪、SELECT 如何在 shard 上并行、何时必须 GLOBAL IN,以及和 PostgreSQL Citus 的本质差异。

版本锚定:ClickHouse 24.x LTS。集群协调默认写法以 ClickHouse Keeper(兼容 ZooKeeper 协议)为准;仍使用 ZooKeeper 的集群概念相同,路径前缀不同。


一、三层对象:Cluster、Local、Distributed

典型生产拓扑:

flowchart TB
  subgraph client [客户端]
    CHC[clickhouse-client / JDBC]
  end
  subgraph coord [协调节点 可选]
    DT[Distributed 表 dist.events]
  end
  subgraph shard1 [Shard 1]
    L1[本地表 events_local]
    R1[Replica A / B]
  end
  subgraph shard2 [Shard 2]
    L2[本地表 events_local]
    R2[Replica A / B]
  end
  CHC --> DT
  DT -->|INSERT 按 sharding key| L1
  DT -->|INSERT| L2
  DT -->|SELECT 下发子查询| L1
  DT -->|SELECT| L2
  L1 --- R1
  L2 --- R2
对象 引擎 磁盘数据 典型命名
本地表 ReplicatedMergeTree 有 Part events_local
分布式表 Distributed 无 Part,仅元数据 events
集群定义 配置 <cluster> in config.xml

关键结论Distributed 表是 视图 + 路由器,不是第三套存储。所有列存文件格式仍遵循 MergeTree Part 格式(第 02 篇)

1.1 最小 DDL 示例

-- 在每个 shard 的每个 replica 上创建本地表(简化,省略 ON CLUSTER)
CREATE TABLE events_local ON CLUSTER '{cluster}'
(
    event_date Date,
    user_id    UInt64,
    event_type LowCardinality(String),
    payload    String
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events_local', '{replica}')
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id);

-- 在「入口」库创建 Distributed 表(可在任意节点,常放在统一 query 节点)
CREATE TABLE events ON CLUSTER '{cluster}'
AS events_local
ENGINE = Distributed('{cluster}', currentDatabase(), events_local, cityHash64(user_id));

第三参数 events_local各 shard 上的本地表名;第四参数 cityHash64(user_id)分片键表达式

1.2 宏 {cluster}{shard}{replica}

ON CLUSTERReplicatedMergeTree ZK 路径依赖 macros(通常在 /etc/clickhouse-server/config.d/macros.xml):

<clickhouse>
    <macros>
        <cluster>prod</cluster>
        <shard>01</shard>
        <replica>replica_a</replica>
    </macros>
</clickhouse>

每个物理节点 macros 不同;ON CLUSTER prod 会把 DDL 广播到 remote_serversprod 的所有 host。工程判断:macros 与 remote_servers 不一致是分布式集群最常见的「表存在但路由错 shard」根因。


二、集群配置:remote_servers 与副本权重

config.xml(或 config.d/cluster.xml)定义逻辑 cluster:

<remote_servers>
    <prod>
        <shard>
            <internal_replication>true</internal_replication>
            <replica>
                <host>ch1.example.com</host>
                <port>9000</port>
            </replica>
            <replica>
                <host>ch2.example.com</host>
                <port>9000</port>
            </replica>
        </shard>
        <shard>
            <internal_replication>true</internal_replication>
            <replica>
                <host>ch3.example.com</host>
                <port>9000</port>
            </replica>
            <replica>
                <host>ch4.example.com</host>
                <port>9000</port>
            </replica>
        </shard>
    </prod>
</remote_servers>
字段 含义
shard 水平分片;不同 shard 各持数据子集
replica 同一 shard 内的副本;internal_replication=true 时写入走副本协议
internal_replication true:Distributed INSERT 只写一份到 shard 内一 replica,由副本链复制
weight 可选;非均匀 shard 容量时调整查询/写入权重

与第 08 篇衔接internal_replication=true 时,Distributed 引擎 不向同一 shard 的所有 replica 各写一份——否则 ReplacingMergeTree 去重语义会被打破。写入目标由 insert_replica 或负载策略选择。


三、分片键:数据分布与查询局部性

分片键是 Distributed 表 DDL 的第四个参数(或 SHARDING_KEY 别名语法,视版本文档)。ClickHouse 计算:

\[ \text{shard\_index} = \bigl(\text{hash}(\text{sharding\_expr}) \bmod N_{\text{shards}}\bigr) + 1 \]

常用 hash:cityHash64sipHash64jumpConsistentHash(扩容场景)。

3.1 选型原则

目标 推荐分片键 风险
用户级查询局部性 cityHash64(user_id) 超大用户热点 shard
时间范围 + 实体 (event_date, user_id) 组合 hash 表达式过复杂难调试
近似均匀 高基数 UUID / 雪花 ID 无法按业务键 colocate
日志按天 不推荐toDate(ts) 单日流量打满单 shard

工程判断:分片键首要保证 写入均匀;其次考虑 最常见 WHERE 能否只扫单 shard。ClickHouse 没有 Citus 的「distribution column = 主键必须」硬约束,但选错键的代价一样:跨 shard 聚合、GLOBAL JOIN 爆炸。

3.2 与 PARTITION BY 正交

同一 event_date 的行可以分布在所有 shard(分片键含 user_id 时)。常见误区:把 PARTITION BY toYYYYMM(d) 误当分片键,导致每月一个 shard 热点。

3.3 扩容与 resharding

增加 shard 后,旧数据 不会自动重分片。官方路径:

  1. 新 shard 建空本地表;
  2. 历史数据 INSERT SELECTclickhouse-copier / 第三方工具迁移;
  3. 双写或读路径合并。

jumpConsistentHash 在扩 shard 时比 naive mod 更少迁移行——仍是工程级迁移,无在线透明 resharding(与 Citus rebalance 对比见 §九)。


四、INSERT 路由

INSERT INTO events ... 进入 Distributed 表时,协调节点:

  1. 按 block 计算每行的 sharding expr;
  2. 按 shard 分组;
  3. 向各 shard 发送 sub-insert(internal_replication=true 时每个 shard 选一个 replica)。
sequenceDiagram
  participant C as Client
  participant D as Distributed 协调
  participant S1 as Shard 1 replica
  participant S2 as Shard 2 replica
  C->>D: INSERT block
  D->>D: 按 cityHash64 分组
  D->>S1: INSERT 子 block
  D->>S2: INSERT 子 block
  S1->>S1: ReplicatedMergeTree 副本链
  S2->>S2: ReplicatedMergeTree 副本链

4.1 批量与 async_insert

小 batch 高频 INSERT 会在 每个 shard 各产生 small parts——放大 too many parts(第 15 篇) 风险。缓解:

不在此给出吞吐数字——须在本集群测 system.eventsInsertedRows / InsertedBytes

4.2 insert_distributed_sync

insert_distributed_sync = 1(session 或 profile)时,Distributed INSERT 等待所有 shard 子 insert 完成再返回。默认异步更快但客户端难以及时捕获单 shard 失败。

4.3 直接写本地表

ETL 若已知 target shard,可绕过 Distributed 直写 events_local——须保证 不会写错 shard。运维脚本常用;应用层一般仍写 Distributed。


五、SELECT:子查询下发与二次聚合

SELECT ... FROM events 的典型计划:

  1. 协调节点把查询改写成对 每个 shard 的本地表 的子查询(可能带相同 WHERE);
  2. 各 shard 并行执行(受 max_threads、副本选择影响);
  3. 协调节点对 shard 结果 再聚合SUMCOUNT 等)。

这与 向量化执行(第 04 篇) 的 pipeline 在协调节点再串一层 Merge 算子

5.1 副本读:load_balancing

remote_servers 里 replica 的读取策略由 settings 控制,例如:

只读副本readonly 用户 + 副本延迟监控)可把 analytics 流量从 leader 拉开——延迟见 system.replicas(第 14 篇)。

5.2 谓词下推与限制

Distributed 会尝试把 WHEREPREWHERE(见 查询读取路径(第 05 篇))下推到 shard 子查询。但:

-- 若 sharding key 含 user_id,且 WHERE user_id = 123 常数,可只打单 shard(视优化器版本)
SET optimize_skip_unused_shards = 1;
SELECT count() FROM events WHERE user_id = 123 AND event_date >= today() - 7;

是否生效以 EXPLAIN / EXPLAIN PLAN 为准——须本地执行,本文不粘贴计划文本。

5.3 GLOBAL 二次聚合边界

shard 本地执行 GROUP BY k 后,协调节点收到 部分聚合状态,可能再 GROUP BY k 合并。若聚合函数非 decomposable(如 quantileExact 中间态不能简单相加),会触发不同 plan 或报错——设计分布式 SQL 时要查函数是否 combinable


六、GLOBAL IN 与 GLOBAL JOIN

6.1 普通 IN 的问题

SELECT * FROM events
WHERE user_id IN (SELECT user_id FROM dim_vip_users);

dim_vip_users仅存在于协调节点 的小表,或未 Distributed 复制,默认 IN 可能在 每个 shard 子查询里本地执行子查询——子表为空或不全,结果错误。

6.2 GLOBAL IN

SELECT * FROM events
WHERE user_id GLOBAL IN (SELECT user_id FROM dim_vip_users);

语义:先在 协调节点 执行子查询,把结果 广播 到每个 shard,再各 shard 过滤。

模式 网络 内存 适用
IN 子表已复制到各 shard
GLOBAL IN 高(广播) 协调节点持全集 小子表、临时过滤集
JOIN + 复制引擎 各 shard 本地 维表定期同步

代价:GLOBAL IN 把协调节点变成 单点内存瓶颈——维表百万行级可能 OOM(max_memory_usage)。

6.3 GLOBAL JOIN

SELECT e.*, d.name
FROM events AS e
GLOBAL INNER JOIN dim_users AS d ON e.user_id = d.id
WHERE e.event_date >= today() - 1;

协调节点先拉 dim_users(或子计划),广播到 shard 与 events_local join。宽维表 + 事实表大 scan 时 极慢

替代架构(工程上更常见):

  1. 维表复制:每个 shard 建相同 ReplicatedMergeTree 维表,定时 INSERT SELECT 同步;查询用普通 JOIN
  2. DictionaryENGINE = Dictionary + dictGet):适合小维表、键值查找。
  3. 预 join 宽表:ETL 阶段打平——牺牲存储换查询简单。

6.4 distributed_product_mode

global / local / allow 控制分布式 JOIN 行为(见官方 Distributed product mode)。误用 local 可能导致 shard 间笛卡尔积——经典事故模式,第 15 篇会列 symptom。


七、Distributed DDL 与 ON CLUSTER

CREATE TABLE metrics_local ON CLUSTER prod (...);
CREATE TABLE metrics ON CLUSTER prod AS metrics_local
ENGINE = Distributed(prod, currentDatabase(), metrics_local, rand());
操作 注意
CREATE ... ON CLUSTER 依赖 DDLWorker + Keeper/ZK 队列;失败会留 system.distributed_ddl_queue
DROP TABLE 先删 Distributed 再删 local,或反之需团队规范
RENAME 24.x 支持有限;生产倾向建新表迁移
ALTER mutation 各 shard 独立 mutation 队列——大表 ALTER 在 N shard 上放大为 N 倍后台压力

检查 DDL 队列:

SELECT entry, query, initiator, status, exception_code, exception_text
FROM system.distributed_ddl_queue
ORDER BY entry DESC
LIMIT 20;

八、源码与实现锚点

ClickHouse 24.x 源码(A 级):

路径 职责
src/Storages/StorageDistributed.cpp Distributed 存储引擎入口
src/Storages/Distributed/DistributedSink.cpp INSERT 路由与发送
src/QueryPipeline/RemoteQueryExecutor.cpp 远程 subquery 执行
src/Interpreters/Cluster.cpp cluster 拓扑解析
src/Interpreters/DDLWorker.cpp ON CLUSTER DDL

阅读 StorageDistributed::readDistributedSink::write 可串起 读写下推 全路径。


九、与 PostgreSQL Citus / 其它分布式边界

维度 ClickHouse Distributed Citus
定位 OLAP 分片 + 路由 PG 扩展,OLTP+HTAP
分片单位 Part(列存) 行 / 分片表
跨 shard JOIN GLOBAL / 复制维表 Colocated / repartition
事务 无跨 shard 单事务 有限分布式事务
Reshard 离线迁移为主 rebalance_table_shards
协调节点 任意能访问 cluster 的 CH 节点 Coordinator

PostgreSQL 内核系列 的 B-Tree、MVCC 语义 不迁移 到 ClickHouse——用 Citus 做 PG 水平扩展,用 ClickHouse 做分析副本或独立 OLAP,是常见分工(第 13 篇 展开选型)。

不写 Spark/Flink 全链路——PLAN 边界外。


十、设计模式与反模式

10.1 推荐模式

星型 + 复制维表

专用集群入口

与 Kafka + MV 配合

10.2 反模式

反模式 后果
分片键 rand() 且无业务过滤 任何 GROUP BY 全 cluster 扫描
大维表 GLOBAL JOIN 协调节点 OOM、查询超时
每行 INSERT N shard × small parts → merge 崩溃
忽略 internal_replication 双写 replica 重复数据、副本冲突
Distributed 当唯一备份 无 Part 文件,恢复从 local 表

十一、排查清单

  1. 数据倾斜system.partshostName() / 表统计各 shard 行数(需集群级监控或逐 shard 查)。
  2. 查询慢system.query_logread_rowsProfileEventsDistributedConnection*
  3. INSERT 失败单 shardinsert_distributed_sync=1 复现;查目标 shard system.errors
  4. DDL 卡住system.distributed_ddl_queue + Keeper 连通。
  5. 结果不一致:检查 IN vs GLOBAL IN、维表是否全 shard 同步。

详细系统表见 监控(第 14 篇)


十二、实验框架(须本地执行)

以下命令框架 未在本环境执行——读者在测试集群验证。

12.1 验证分片分布

-- 在每个 shard 的 events_local 上分别执行,对比 count()
SELECT count() FROM events_local;

-- 或通过 Distributed 查 system 表(若配置允许)
SELECT hostName(), count()
FROM cluster('prod', currentDatabase(), events_local)
GROUP BY hostName();

12.2 对比 IN 与 GLOBAL IN 计划

EXPLAIN PLAN
SELECT count() FROM events
WHERE user_id IN (SELECT user_id FROM dim WHERE tier = 'vip');

EXPLAIN PLAN
SELECT count() FROM events
WHERE user_id GLOBAL IN (SELECT user_id FROM dim WHERE tier = 'vip');

对比 Expression 节点是否含 GLOBAL 广播——以本地输出为准

12.3 制造 skew 观察

-- 测试库:故意用常量分片键观察单 shard 膨胀(勿在生产)
CREATE TABLE skew_test_local (...) ENGINE = MergeTree ...;
CREATE TABLE skew_test AS skew_test_local
ENGINE = Distributed('test_cluster', currentDatabase(), skew_test_local, 1);  -- 常数键

十三、配置项速查

Setting 作用
optimize_skip_unused_shards 常量分片键过滤时跳过无关 shard
optimize_skip_unused_shards_limit 跳过优化的 shard 数上限
insert_distributed_sync 同步等待各 shard insert
distributed_product_mode JOIN 分布式行为
max_query_size 大 IN 列表解析
distributed_group_by_no_merge 高级:控制二次 GROUP BY
prefer_localhost_replica 协调节点本地 replica 优先

完整容量相关见 配置与容量(第 16 篇)


十四、小结

问题 答案
Distributed 存数据吗? 否,只路由
分片键影响什么? INSERT 目标 shard、部分查询剪枝
GLOBAL IN 何时用? 小子表未复制到各 shard
与副本关系? internal_replication=true 时 INSERT 不双写 replica
扩容? 离线 resharding,无透明 rebalance

附录 A、GLOBAL JOIN 改写清单

原写法 问题 改写
GLOBAL JOIN 大维表 协调节点 OOM 每 shard 复制维表 + 普通 JOIN
IN (SELECT ...) 协调库小表 shard 子查询为空 GLOBAL IN 或复制
两 fact 表 JOIN 跨 shard _shuffle 预聚合 / 同 sharding key colocate
DISTINCT 子查询过滤 广播大 set JOIN 复制表或 Dictionary

附录 B、分片键评审问卷

  1. 写入 QPS 与 batch 大小?小 batch 则先优化 ingest 再谈分片数。
  2. Top 10 查询是否带 sharding key 等值过滤?
  3. 是否存在「大用户」skew?是否需要 salt(如 cityHash64(user_id, bucket_id))?
  4. 扩容计划:是否接受 offline copy?
  5. 维表大小:是否适合每 shard 全量复制?

附录 C、网络与防火墙

Distributed 查询在 shard 间建立 大量 outbound 连接(端口通常 9000/native 或 9440/TLS)。协调节点到各 shard、shard 间 interserver_http(复制)需放通。跨 AZ 部署时 RTT 增加会放大 scatter-gather 延迟——无量化延迟,但架构上应同 AZ 放 query 与 data。

附录 D、术语表

术语 含义
Scatter-gather 协调节点下发子查询再合并
Sharding key Distributed 第四参数表达式
Global subquery 协调执行后广播到 shard

附录 E、Distributed 查询计划解读要点

阅读 EXPLAIN PLANEXPLAIN 时关注:Distributed 节点子计划是否含 GLOBALReadFromStorage 是否只在 subset shard;聚合是否两层 Aggregating。若见 Double aggregation,确认 combinator 正确。协调节点 limit 下推失败会导致各 shard 返回全量再 truncate——高 limit 也危险。

附录 F、双集群 DR

Active-passive 两 cluster 靠对象存储或 replication queue 同步——非 Distributed 内建。DR 演练测 RPO/RTO 用业务指标,本文不写 SLA 数字。

附录 G、负载均衡 insert

多入口节点写同一 Distributed 表时,各入口独立路由——无 central coordinator。入口前 L4 LB 即可;避免 sticky 导致单入口热点,除非 cache 亲和需要。

附录 · 深度补充(系列交叉索引)

深度 1

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 2

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 3

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 4

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 5

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 6

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 7

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 8

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 9

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 10

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 11

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 12

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 13

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 14

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 15

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 16

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 17

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 18

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 19

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 20

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 21

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 22

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 23

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。

深度 24

第 09 章与系列其它篇章的交叉:读路径见 第 05 篇;merge 见 第 06 篇;索引见 第 07 篇;副本见 第 08 篇;Distributed 见 第 09 篇;监控见 第 14 篇;故障见 第 15 篇;配置见 第 16 篇。 实施任何 setting 变更前,在 staging 用 system.parts / query_log 建立 24h 基线,变更后再对比——避免无证据调参。


上一篇ReplicatedMergeTree

下一篇物化视图与增量管道

参考资料

  1. ClickHouse Documentation, Distributed table engine, clickhouse.com/docs, 24.x
  2. ClickHouse Documentation, Distributed DDL, GLOBAL IN/JOIN
  3. ClickHouse Source, release-24.x, src/Storages/StorageDistributed.cpp
  4. ClickHouse Source, src/Storages/Distributed/DistributedSink.cpp
  5. Citus Documentation, Distributed Tables — 对照边界

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-18 · database / distributed

【列存引擎内核】ReplicatedMergeTree

ReplicatedMergeTree 副本协调:Log entry、ClickHouse Keeper/ZooKeeper 路径、副本同步与 recovery。双节点实验步骤(本环境未部署)。24.x LTS 默认推荐 Keeper。

2026-06-18 · database / storage

【列存引擎内核】ClickHouse 与 DuckDB 源码级拆解

主选 ClickHouse 拆解 MergeTree 存储格式、向量化执行与分布式协调;DuckDB 作为嵌入式 OLAP 对照。覆盖列存文件布局、merge 机制、跳数索引与生产故障模式,面向数据平台工程师与从 PG/MySQL 转 OLAP 的 DBA。

2026-06-18 · database / storage

【列存引擎内核】列存基础与 ClickHouse 架构

行存 vs 列存的带宽、压缩与向量化三角;ClickHouse Server 进程模型、线程池与 MergeTree 引擎家族地图;src/Storages 与 src/Processors 源码入口。对照 PG 行存与 LSM 写优化路径,版本锚定 ClickHouse 24.x LTS。

2026-06-18 · database / storage

【列存引擎内核】MergeTree Part 文件格式

ClickHouse MergeTree Part 目录结构:columns.txt、checksums.txt、.bin、.mrk2、primary.idx 语义,Granule 与 Mark 的定位作用,Wide/Compact 布局与 MergeTreeDataPart 源码入口。版本锚定 24.x LTS。


By .