0
0
0
0
博客/.../

TiDB 的 Region:不是书架格子,而是一段会“分裂、搬家、合并”的图书编号区间

 TiDBer_wangwenjing  发表于  2026-09-18

声明:学习过程中的个人解读,不正之处,欢迎各位指正。

很多人第一次接触 TiDB,都会把 Region 想象成图书馆里一个个固定的书架格子。

但这个比喻其实不够准确。

Region 不是一个物理格子,而是 Key 空间中的一段连续区间。

更有意思的是,这段区间还会动态变化:

数据太多了,它会分裂;副本负载不均,它可以搬家;两个相邻的小区间,还可以重新合并。

所以,与其把 Region 想象成一个固定书架格子,不如把它想象成:

一段会“分裂、搬家、合并”的图书编号区间。

01 一个常见的误解

在图书馆里,我们习惯这样找书:

先看图书编号,比如:

A000 ~ B000

然后走到对应的书架,在相应位置找到书。

于是很多人第一次接触 TiDB 时,会自然地产生一个类比:

TiDB 的 Region,是不是就像 TiKV 磁盘上的一个个数据格子?

不是。

Region 不是一个独立的物理存储格子。

它没有一个与自己一一对应的独立磁盘文件,也没有一个专门负责它的进程,更没有一个永远固定的物理位置。

它真正描述的是:

一段连续的 Key 范围 [StartKey, EndKey),以及负责这段范围的多副本 Raft 状态。

因此,更准确地说:

Region 是一个逻辑数据分片,而不是一个独立的物理存储设备。

这里还有一个很容易混淆的地方:

Region 也不是 SQL 层面的 Table。

一张表的数据通常会随着增长被划分到多个 Region 中。

Region 工作在 TiKV 的 KV 层,它关心的是:

“这一段 Key 属于谁?”

而不是:

“这是哪张 SQL 表?”

02 用图书馆比喻,重新理解 Region

我们把这个比喻重新设计一下:

  • Key:每本书的图书编号
  • Region:一段连续的图书编号区间
  • Peer:某个分馆中保存的这段区间的一个副本
  • Leader:当前负责处理请求的那个 Peer
  • Raft Group:保存同一份数据的多个管理员组成的小组
  • TiKV Store:一个分馆
  • RocksDB:分馆的底层书库
  • PD:总调度中心
  • RegionCache:目录系统里缓存的“图书编号 → 区间 → 负责人 → 分馆”信息

比如现在有一本图书编号为:

A123

假设它属于:

[A000, B000)

那么整个过程可以理解成:

  • Peer 1 → TiKV-A
  • Peer 2 → TiKV-B
  • Peer 3 → TiKV-C
  • 图书编号 A123
  • 属于区间 [A000, B000)
  • 这个区间对应 Region 100
  • Region 100 有三个 Peer:
  • 其中一个 Peer 是 Leader

客户端并不需要知道:

“A123 在 TiKV-B 的 RocksDB 哪个 SST 文件里?”

它只需要知道:

A123 属于哪个 Region,以及应该把请求发给哪个 Leader。

至于书最终放在分馆书库里的哪个架子上,是底层存储引擎负责的事情。

所以,Region 更像是一种:

“图书编号区间责任制”。

它关心的是:

“这段 Key 归我管。”

而不是:

“这些数据必须放在某一个固定物理格子里。”

03 Region、Peer、Raft Group,到底是什么关系?

理解 Region,最容易混淆的就是这三个概念:

Region、Peer、Raft Group。

可以把它们理解成三个不同层次。

第一层:Region

Region 描述的是:

哪一段 Key 空间属于这个数据分片。

例如:

  • Region 100
  • StartKey = A000
  • EndKey = B000

它表示:

[A000, B000)

这一段 Key。

第二层:Peer

一个 Region 通常不会只保存一份。

为了高可用,同一个 Region 会有多个副本。

这些副本就是:

Peer。

例如:

  • Peer 1 → TiKV-A
  • Peer 2 → TiKV-B
  • Peer 3 → TiKV-C
  • Region 100 有三个 Peer:

所以不要简单地记成:

Region = 逻辑Peer = 物理

更准确的说法是:

Region 是逻辑数据分片;Peer 是这个 Region 在某个 TiKV Store 上的一个副本。

一个 TiKV Store 上可以同时承载大量不同 Region 的 Peer。

第三层:Raft Group

同一个 Region 的多个 Peer,会通过 Raft 保持一致。

因此:

  • Peer 1
  • Peer 2
  • Peer 3
  • Region 100 对应一个 Raft Group
  • 这个 Raft Group 包含:

其中一个 Peer 会承担:Leader角色。

Leader 负责处理该 Region 的主要写请求,并协调 Raft 复制。

所以可以用一句话记住:

Region 决定“管哪段 Key”,Peer 决定“这段数据在哪些 TiKV 上有副本”,Raft 决定“这些副本如何保持一致”。

04 Region 为什么不是一个物理实体?

因为 Region 的职责,本质上是描述和管理:一段 Key 空间。

它并不需要独占某个物理文件。

在 TiKV 中,实际数据最终由底层存储引擎保存,例如 RocksDB。

因此你可以把整个关系理解成:

  • Peer 1 在 TiKV-A
  • Peer 2 在 TiKV-B
  • Peer 3 在 TiKV-C
  • 最上层是 Region,负责 Key 范围 [A000, B000)
  • 这个 Region 有三个 Peer:
  • 每个 TiKV 底层都有自己的 RocksDB
  • 数据最终由 RocksDB 组织存储

这里最重要的一点是:

Region 有自己的逻辑边界,但它并不等于一个独立的物理文件。

底层数据具体如何组织,是 TiKV 存储引擎的事情。

因此,看到:

  • Region 100
  • StartKey = A000
  • EndKey = B000

并不能简单理解成:

“磁盘上一定存在一个叫 Region-100 的文件,里面只存 A000~B000。”

这种理解是不准确的。

05 这么设计,到底高级在哪?

如果 Region 真的是一个固定的物理格子,那么很多操作都会变得非常笨重:

扩容时,需要搬动整个格子;负载不均时,需要搬动整个格子;副本迁移时,需要复制整个格子;数据过大时,需要重新组织物理文件;故障恢复时,也需要围绕物理块进行迁移。

而 TiKV 把:“数据属于哪一段 Key”和“这段数据当前有哪些副本、这些副本在哪里”分开处理。

这就产生了很大的灵活性。

优点一:Region 可以动态 Split

假设:

  • Region 100 的范围是 [A000, Z000)

随着数据不断增长,这个 Region 变得太大。

它可以 Split 成:

  • Region 100:[A000, M000)
  • Region 101:[M000, Z000)

原来一个逻辑数据分片,现在变成两个。

这里需要特别注意:

Region Split 不能简单理解成“只修改一条元数据”。

Split 的核心是:

改变 Region 的 Key 范围;创建新的 Region 状态;更新对应的 Raft/Peer 状态;更新路由信息。

至于底层数据具体如何组织,则由 TiKV 的存储引擎负责。

因此,更准确的理解是:

Split 改变的是逻辑分片边界和对应的 Raft 状态,而不是像搬运一个“物理格子”那样搬走整块磁盘空间。

具体 ID 分配由 TiKV 内部决定,这里只是示意。

优点二:相邻 Region 可以 Merge

如果两个 Region 都很小:

  • Region 100:[A000, M000)
  • Region 101:[M000, Z000)

满足相应的 Merge 条件后,它们可以合并成更大的 Region。

合并后:

  • Region 100:[A000, Z000)

Merge 的目的,是降低过多小 Region 带来的管理和调度开销。

但同样不能简单理解成:

“PD 把两条元数据记录删掉,然后改成一条。”

真正的 Merge 是 TiKV/PD 协同完成的 Region 合并过程,会涉及 Region 状态和 Raft 状态的变化。

通常要求两个 Region 的副本集合相同或先通过调度对齐,然后才能合并。

所以,Region 的逻辑边界可以动态变化,但这并不意味着底层物理存储完全不参与这个过程。

优点三:副本可以“搬家”

再看一个非常重要的场景。

假设:

  • Peer 1 → TiKV-A
  • Peer 2 → TiKV-B
  • Peer 3 → TiKV-C
  • Region 100 有三个 Peer:

现在 TiKV-A 太热,或者集群需要重新平衡。

PD 可以调度这个 Region 的副本,把 Peer 从 A 移到 D。

可以粗略理解为:

原来:

  • TiKV-A:Peer 1
  • TiKV-B:Peer 2
  • TiKV-C:Peer 3

后来:

  • TiKV-B:Peer 2
  • TiKV-C:Peer 3
  • TiKV-D:Peer 4

Region 的 Key 范围仍然是:

[A000, B000)

变化的是:

这个 Region 的副本分布。

在实际过程中,通常会先在目标 Store 上创建 Learner,通过 Raft/Snapshot 等机制追赶数据,满足条件后转为 Follower,再删除源 Store 上的旧 Peer。

所以这里有一个非常漂亮的架构分离:

Region 不需要跟着物理节点一起“搬家”。

搬的是Peer,而不是Region 这个逻辑 Key Range。

06 高可用:Region 背后其实是一组 Raft 副本

为什么一个 Region 要有多个 Peer?

因为单副本不具备高可用能力。

例如:

  • Peer 1 → TiKV-A
  • Peer 2 → TiKV-B
  • Peer 3 → TiKV-C
  • Region 100 有三个 Peer:

假设 TiKV-A 宕机:

  • TiKV-A ❌
  • TiKV-B ✓
  • TiKV-C ✓

只要剩余副本满足 Raft 的多数派要求,Region 仍然可以继续提供服务。

如果原来的 Leader 挂掉,那么其他 Peer 可以通过 Raft 选举产生新的 Leader。

如果某个副本长期缺失,PD 会根据集群状态调度新的 Peer,逐渐恢复期望的副本数量和分布。

因此Region 是逻辑分片,而 Raft Group 为这个分片提供了复制和一致性能力。

这两者结合起来,才构成 TiKV 的高可用数据单元。

07 RegionEpoch:Region 为什么知道自己的“旧路由”已经过期?

Region 还有一个非常关键的概念:RegionEpoch。

为什么需要它?

因为 Region 并不是永远不变的。

例如最开始:

  • Region 100:[A000, Z000)

后来发生 Split:

  • Region 100:[A000, M000)
  • Region 101:[M000, Z000)

那么客户端之前缓存的:

[A000, Z000)

显然已经过时。

如果客户端还拿着旧信息访问 TiKV,就必须有办法让 TiKV 告诉它:

“你手里的 Region 信息已经不是最新的了。”

RegionEpoch 就承担了这个作用。

它可以理解为:Region 拓扑的版本信息。

其中:ConfVer 主要反映副本配置变化;Version 主要反映 Region 范围变化,例如 Split、Merge。

因此RegionEpoch 不是普通意义上的“软件版本号”,而更像 Region 拓扑变化的版本标记。

当客户端携带的 Region 信息已经过期,

TiKV 可能返回:EpochNotMatch

客户端随后刷新 RegionCache,再重新定位请求。

如果 Leader 发生变化,还可能遇到:NotLeader

客户端同样可以根据返回信息重新寻找正确的 Leader。

这就是为什么Region 可以动态 Split、Merge、迁移,而上层客户端仍然能够继续工作。

08 从 SQL 到 TiKV:一次请求到底怎么找到 Region?

把前面的概念串起来,就能理解 TiDB 的请求路径。

假设 SQL 最终需要访问某个 Key:

A123

大致可以理解成:

  • SQL
  • → TiDB Server
  • → 生成 Key
  • → RegionCache
  • → A123 属于哪个 Region?
  • → Region 100 [A000, B000)
  • → Leader 在哪里?
  • → TiKV-A
  • → Raft Group
  • → RocksDB

TiDB Server 并不需要知道:“A123 在 RocksDB 的哪个 SST 文件里?”

它主要需要知道:A123 属于哪个 Region,以及这个 Region 的 Leader 在哪里。

如果本地 RegionCache 没有对应信息,就可以向 PD 查询。

所以可以把整个过程浓缩成:

  • Key
  • → Region
  • → Leader Peer
  • → TiKV
  • → RocksDB

这就是 TiDB 分布式架构非常重要的一层解耦。

读请求同样会与 Raft Group 交互,例如通过 ReadIndex 确认已 apply 的索引,以保证读到最新已提交数据。

09 Region Split 和 Hot Region,不是一回事

这里还要特别区分两个概念。

Region Split 解决的是:一个 Region 太大了。

例如:

  • 一个超大 Region
  • → Split 成多个 Region

它主要改变:数据分片的粒度。

Hot Region 调度解决的是:某些 Region 的访问压力太大,或者负载分布不均。

例如:

  • TiKV-A:████████████████████
  • TiKV-B:█████
  • TiKV-C:████

PD 可以通过 Leader/Peer 调度等方式,让负载更加均衡。

因此Split 是“把一个大区间切小”;调度是“把副本和 Leader 分布得更合理”。

两者经常一起出现,但解决的问题并不一样。

10 Region 什么时候 Split?

TiKV 会持续统计 Region 的相关信息,例如:

Region 数据大小;Key 数量;与 Region 负载相关的统计信息。

当 Region 达到相应 Split 条件时,就可能触发分裂。

具体阈值和机制会受到 TiDB/TiKV 版本及相关配置影响,因此不要把某一个固定数字当成所有版本的默认值。

最核心的认知只有一句:

Region Split 的本质,是把一个过大的 Key Range 切成多个更合适的数据分片。

11 Region 什么时候 Merge?

反过来,如果集群里出现大量非常小的 Region,就会增加:

Region 元数据;Raft Group 管理成本;调度开销;RegionCache 等相关管理成本。

因此 PD 会根据相应的 Merge 条件寻找可以合并的 Region。

通常会关注:

Region 是否相邻;Region 大小是否合适;是否满足相关调度条件;是否满足 Split/Merge 的时间间隔等约束。

最终:

  • [A000, B000)
  • 加上 [B000, C000)

可能重新变成:

  • [A000, C000)

所以 Region 并不是:“一旦创建,就永远固定不变。”

恰恰相反Region 的边界本身就是动态的。

12 真正理解 Region,要记住这四句话

如果把整篇文章压缩成四句话,我建议记住:

Region 决定“哪一段 Key 归谁管”。

Peer 决定“这段数据在哪些 TiKV 上有副本”。

Raft 决定“这些副本如何保持一致”。

PD 决定“这些副本应该如何分布和调度”。

再加上一层:

RegionCache 让客户端不用每次都去问 PD,而是可以缓存 Key → Region → Leader 的路由信息。

这样,整个架构就串起来了。

13 回到最开始的图书馆

现在再回头看图书馆的比喻。

Region 不是:

❌ 一个固定的书架格子。

它更像:

✅ 一段图书编号区间。

比如:

[A000, B000)

这段区间由一个 Raft Group 负责。

这个 Raft Group 有多个 Peer:

  • Peer 1 → 分馆 A
  • Peer 2 → 分馆 B
  • Peer 3 → 分馆 C

其中一个 Peer 是 Leader。

如果数据太多:

  • [A000, B000)
  • → Split
  • → [A000, A500) + [A500, B000)

如果副本分布不合理:

  • Peer
  • → 搬到另一个 TiKV

如果两个 Region 太小:

  • [A000, A500) + [A500, B000)
  • → Merge
  • → [A000, B000)

所以它真正有趣的地方就在这里:

它不是一个固定格子,而是一套可以动态调整“责任边界”和“副本位置”的机制。

14 总结:Region 的“虚”,恰恰是它的力量

Region 是 TiDB/TiKV 中非常核心的逻辑数据分片。

它描述的是:

一段连续的 Key Range + 这段 Range 对应的 Raft 副本状态

它不是一个独立磁盘文件,也不是一个固定的物理格子。

这种设计带来了非常强的弹性:

可分裂:大 Region 可以 Split 成多个小 Region;可合并:相邻的小 Region 可以 Merge;可迁移:Peer 可以在 TiKV Store 之间重新分布;高可用:多个 Peer 通过 Raft 保持一致;可路由:TiDB 根据 RegionCache 定位 Key 对应的 Region 和 Leader;可演进:RegionEpoch 帮助客户端识别已经过期的 Region 信息。

所以,下次有人问你:

“TiDB 的 Region 到底是什么?”

你可以这样回答:

Region 不是 TiKV 磁盘上的一个物理格子,而是 Key 空间中的一个逻辑数据分片:它代表一段连续的 Key 区间,并由多个 Peer 通过 Raft 共同维护。这个区间可以 Split、Merge,它的 Peer 也可以被 PD 调度到不同 TiKV 上。

换句话说,Region 就是一段会“分裂、搬家、合并”的图书编号区间。

而正是这种逻辑分片与物理存储、复制、调度之间的解耦,让 TiDB 能够在分布式环境中实现弹性扩展、高可用和自动负载均衡。

一句话版:

Region 管的是 Key Range,Peer 管的是副本,Raft 管一致性,PD 管调度。Region 不“住”在某个固定物理格子里,但它确实对应着 TiKV 中真实存在的数据和副本状态。

0
0
0
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论