在使用 ClickHouse 的过程中,很多人都会在数据目录下看到类似下面这样的目录名:
20251231_1_3_1
20260101_4_5_1
20260102_6_8_2
这些目录名看起来“像是有规律,但又说不清规律”,尤其是:
MinBlockNum / MaxBlockNum是怎么来的?Level到底表示什么?- 为什么同一天会不断变化?
本文将通过完整写入 + 合并过程示例,把 MergeTree 的分区目录命名规则彻底讲清楚。
一、表结构与前提条件
假设有如下表结构:
CREATE TABLE hits_v1
(
ts Date,
uid UInt64,
url String
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(ts)
ORDER BY uid;
关键点:
- 每天一个分区
分区 ID(PartitionId) =YYYYMMDD(ts) - 每次
INSERT写入都会生成新的 数据块(part) - MergeTree 会在后台 自动 merge(合并)数据块
二、先明确几个核心概念(非常重要)
1️⃣ Partition(分区)
- 由
PARTITION BY决定 - 是数据管理、删除、合并的大粒度单位
- 不同分区之间永不合并
例如:
20251231 ← 一个分区
20260101 ← 另一个分区
2️⃣ BlockNum(块编号)
- 全表级自增编号
- 每生成一个新的 data part,就分配一个新的编号
- 不会因分区而重置
👉 这是很多人第一次容易误解的点:
BlockNum 不是“分区内编号”,而是全局编号
3️⃣ Level(合并层级)
- 表示这个 part 经历过多少次 merge
- 每 merge 一次,
Level + 1 - Level 越大,part 越“成熟”、体积越大、文件越少
三、分区目录命名格式
MergeTree 分区目录的命名规则为:
PartitionId_MinBlockNum_MaxBlockNum_Level
含义是:
某个分区内,由 MinBlockNum 到 MaxBlockNum 这些原始数据块合并而成,当前合并层级为 Level 的数据 part
四、完整写入 & 合并过程示例
📅 第 1 天:2025-12-31
写入第 1 个数据块
20251231_1_1_0
解释:
- PartitionId =
20251231 - MinBlockNum = 1
- MaxBlockNum = 1
- Level = 0(原始数据块,未合并)
写入第 2 个数据块
20251231_1_2_0
说明:
- 新写入块编号为 2
- 分区内现在有两个原始块
- 仍未发生合并
写入第 3 个数据块
20251231_1_3_0
第一次自动 merge
系统将 块 1 + 块 2 合并为一个更大的 part:
20251231_1_3_1
注意这里的含义:
- MinBlockNum = 1
- MaxBlockNum = 3
👉 表示这个 part 覆盖了编号 1~3 的数据来源 - Level = 1
👉 表示经历过一次 merge
⚠️ 这里并不是“块 3 也被合并了”
而是:新 part 的“血缘范围”是 1~3
📅 第 2 天:2026-01-01
写入第 4 个数据块(新分区)
20260101_4_4_0
说明:
- 新分区
- BlockNum 继续全局递增
- Level = 0
写入第 5 个数据块
20260101_4_5_0
第一次 merge
20260101_4_5_1
📅 第 3 天:2026-01-02
连续写入三个块
20260102_6_6_0
20260102_6_7_0
20260102_6_8_0
第一次 merge
20260102_6_8_1
第二次 merge(更大的 part)
20260102_6_8_2
此时:
- Level = 2
- 数据已经高度合并
- 查询时只需读取极少量文件
五、命名规则背后的设计意义
为什么要这样命名?
1️⃣ 快速判断数据血缘
- MinBlockNum / MaxBlockNum 明确这个 part 覆盖了哪些原始数据
2️⃣ 支持高效 merge 调度
- 系统可以根据 Level 判断哪些 part 适合再合并
3️⃣ 查询更快
- Level 越高 → 文件越少 → IO 越少
六、规律总结(速记版)
| 字段 | 含义 |
|---|---|
| PartitionId | 分区键值(如日期) |
| MinBlockNum | 覆盖的最小原始块编号 |
| MaxBlockNum | 覆盖的最大原始块编号 |
| Level | 合并次数 |
整体规律:
- 每天一个分区
- 每次写入 → BlockNum 全局递增
- 每次 merge → Level +1
- 分区目录名 = “数据来源范围 + 合并深度”
七、一句话总结
MergeTree 的分区目录名,本质上是 ClickHouse 用来描述“这个数据块从哪些原始数据合并而来,以及它已经被合并到什么程度”的一种元信息编码。
补充:
❌ 查询时不是“优先选择 Level 最大的 part”
✅ 查询时是:对分区内“当前可见的所有 active part”,全部参与查询