TiKV 的 Region 不是“分片大小”,是一个被三股力量拉住的调度粒度
分布式数据库里,“分片大小”通常被当作一个调优参数:设大一点,分片少一些;设小一点,并行度高一些。TiKV 的 Region 也经常被这样理解——默认 96 MiB,v8.5 调到 256 MiB,仿佛只是换了一个数字。但 TiKV 官方文档在 Tune Region Performance 的开头就给出了一个精确的限定:Region size 的推荐范围是 [48 MiB, 256 MiB],不建议超过 1 GiB,避免超过 10 GiB。一个既“不建议超过 1 GiB”又“允许存在到 10 GiB”的参数,说明它不是一个硬上限,而是一条由三股相互拉扯的力量共同划出的舒适带:Region 太小,协调开销淹没并行收益;Region 太大,每一次故障恢复和调度迁移都变成一场缓慢的重型运输。Region size 的边界不在配置项的数值里,在“协调开销与恢复速度能否被运维窗口吸收”这个比值里。
Region 是调度的最小单位,不是存储的容器
TiKV 的 Region 是一段连续 key range 的 Raft group,同步复制通常三个副本,并且是 调度、负载均衡、故障转移和快照传输的最小单位[reference:0]。这意味着 Region size 同时决定了四件事的粒度:一个 leader 管多少数据、一次迁移搬多少数据、一次故障切换影响多大范围、一次快照传输的体量有多大。
把 Region 理解为“存储的容器”会漏掉最关键的一点:Region 的大小直接决定了控制平面每秒钟需要处理多少条心跳和调度消息。每个 Region 都是一个独立的 Raft group,持续向 PD 上报心跳、region size、leader 和 peer 信息、调度统计[reference:1]。Region 数量翻倍,控制平面的消息量大致翻倍;而 Region 数量与 Region size 成反比。这就是为什么 v8.5 把默认 region-split-size 从 96 MiB 提升到 256 MiB、region-max-size 从 144 MiB 提升到 384 MiB 时,官方给出的理由是“avoid the extra overhead caused by too many Regions”。
换句话说,调大 Region size 首先不是在优化存储布局,而是在降低控制平面的消息密度。它用更大的调度粒度,换取了更轻的协调负担。
三股力量:并行度、协调开销、恢复与迁移
TiKV 官方博客把 Region size 的决策描述为三股竞争力量之间的妥协,这个框架比任何单一的“最佳值”都更能解释边界的形状[reference:3]。
第一股力量是并行度。 Region 越小,Raft group 越多,leader 越多,负载可以被打散到更多的并行单元上。这相当于超市多开收银台,只要人手和协调不崩,就能同时服务更多顾客[reference:4]。
第二股力量是协调开销。 每个 Region 都在持续产生心跳、复制日志、维护元数据、参与调度和选举。Region 太多,系统的时间预算被协调动作吃掉,就像每个团队都开自己的周会,最后所有人都在开会[reference:5]。这不是线性增长的成本,因为 PD 的心跳处理和 TiKV 的 Raftstore 线程都有饱和点;超过饱和点之后,增加 Region 的边际收益会迅速转为负值。
第三股力量是恢复与迁移。 Region 是故障转移、再平衡和快照传输的单位。Region 越大,一次迁移需要搬动的数据越多,故障后需要重放或重建的日志区间越长,快照传输的耗时和带宽占用也越大[reference:6]。一个 10 GiB 的 Region 在一次 leader 切换或副本迁移中占用的恢复窗口,与一个 96 MiB 的 Region 不在同一个量级。
这三股力量没有共同的指向。并行度偏好小 Region,协调开销偏好大 Region,恢复速度偏好小 Region。默认值的历史漂移——从 96 MiB 到 256 MiB——不是某一股力量“赢了”,而是协调开销在 Region 数量膨胀的负载下变得比恢复粒度更紧迫,于是舒适带整体向大尺寸偏移了一格。
1 GiB 不是硬墙,10 GiB 不是禁区:阈值之上发生的是“副作用”,不是“拒绝”
官方文档对 Region size 上界的措辞值得逐字阅读:NOT recommended to set the Region size beyond 1 GiB. Avoid setting the size to more than 10 GiB。两句话用了不同的动词:“不建议”和“避免”。TiKV 不会因为你设了 10 GiB 就拒绝创建 Region;它会接受这个配置,然后用一组具体的副作用来结算:性能抖动、大范围查询的查询性能下降、Region 调度变慢[reference:7]。
这与很多系统“超过阈值就拒绝写入”的边界不同。TiKV 的 Region size 上界是一个软边界:越过之后,系统仍然运行,但运行的质量以一种难以在单点指标上归因的方式退化。一次 2 GiB Region 的迁移可能没有报错,只是在迁移期间 PD 的调度队列积压、快照传输占满网络、相关分片的延迟出现尖峰。边界没有以错误码的形式出现,它以运维复杂度的形式出现。
还有一个容易被忽略的外部约束:当使用 TiFlash 或 Dumpling 时,Region size 不应超过 1 GiB;并且增大 Region size 之后,Dumpling 的并发必须相应降低,否则 TiDB 可能内存耗尽[reference:8]。这是 Region size 的边界不由 TiKV 单独决定的直接证据:同一段 key range,在 OLTP 路径上可以承受的尺寸,在分析路径或导出工具上会被一个完全不同的资源预算重新约束。
配置变更不回溯:已经长大的 Region 不会因为阈值调小而自动分裂
Region size 还有一个方向上的不对称:调大阈值,新写入的 Region 会逐渐长到新尺寸;调小阈值,已经存在的超大 Region 不会在重启后自动分裂。
TiKV 有一个公开的 issue 记录了这个行为:把 region max size / split size 设为 1 GB 并灌入约 30 GB 数据,等 Region 数量稳定后,把配置改为 200 MB 并重启 TiKV,预期是重启后 Region 会被分裂,实际结果是 Region 不会被分裂。
这个行为的含义需要精确理解:它不是说“调小阈值完全无效”,而是说 Region 分裂的触发是数据增长驱动的,不是配置变更驱动的。一个已经存在的 Region,只有在后续写入让它继续增长并触达新的阈值时,分裂逻辑才会介入。配置变更本身不构成一次“重新审视存量 Region”的事件。这意味着调小 Region size 的效果是渐进的,作用在新数据上,而不是一次性的全局重整。运维上如果把“调小阈值”当作“立刻获得更细的调度粒度”,会在存量数据上得到一个与预期不符的中间状态。
反向边界:Region 太多本身就是一个性能问题
Region size 的下界一侧,边界以另一种方式出现。当 Region size 偏小、Region 数量过多时,TiKV 面临的不是“并行度不够”,而是资源消耗和性能回退。官方文档对此的措辞很直接:Region size 相对较小时,TiKV 可能拥有太多 Region,导致更多的资源消耗和性能回归[reference:10]。
公开经验给出的粗略上界是单个 TiKV 节点不建议超过约 5 万个 Region[reference:11]。这个数字不是框架强制的,而是协调开销、心跳密度和 Raftstore 处理能力共同收敛出的一个工程经验值。超过它之后,系统不会停止服务,但Region 数量的增长开始以控制平面的饱和来惩罚写入和调度路径。
所以 Region size 的舒适带是两头都被拉住的:太小,Region 数量膨胀,协调开销吃掉并行收益;太大,恢复和迁移变得笨重,查询的并发粒度变粗。[48 MiB, 256 MiB] 是这两条反向退化曲线之间的重叠区,而不是某一个“最优值”的精确点。
边界不在 256 MiB 这个数字里
把三股力量、软上界和配置不回溯放在一起,TiKV 的 Region size 呈现出一个与直觉不同的结构:它不是一个可以单独调优的“分片大小”,而是一个同时决定控制平面消息密度、故障恢复窗口和查询并行粒度的调度参数。调大它,是在用恢复速度换协调开销;调小它,是在用协调开销换并行度;越过 1 GiB,系统不会拒绝,只是把代价从错误码转移到了调度延迟和运维复杂度上;调小阈值,系统不会回溯分裂存量 Region,效果只作用在未来增长的数据上。
判断一个 TiKV 集群的 Region size 是否“合适”,比看默认值或追赶最新版本更有用的,是先确认三件事:Region 总数在单个 TiKV 节点上距离约 5 万的经验上界还有多少余量;一次 Region 迁移或快照传输的体量是否落在网络和恢复窗口能吸收的范围内;以及如果使用 TiFlash 或 Dumpling,对应的 1 GiB 上限和并发预算是否已经压过了 OLTP 路径的偏好。舒适带的位置由这三组约束的交集决定,而不是由任何一个版本号或默认值决定。
- 点赞
- 收藏
- 关注作者
评论(0)