0
0
1
0
博客/.../

TiDB Placement Rules 深度解析:从工作原理到生产实践

 菩提老祖  发表于  2026-06-29

本文基于 TiDB 6.0+ 的 Placement Rules in SQL 特性,深入剖析其底层调度原理,并针对"旧集群扩容新节点实现业务隔离"的典型场景提供完整操作指南。


一、为什么需要 Placement Rules

在 TiDB 集群中,数据默认以 Region(默认 96MB)为单位自动均衡分布在所有 TiKV 节点上。这种"全自动"模式虽然简化了运维,但也带来了以下痛点:

  • 多业务混部干扰:测试环境多个数据库共用 3 台 TiKV,CPU/IO 互相争抢。
  • 无法物理隔离:不同业务的数据在磁盘层面交织在一起,故障会相互影响。
  • 冷热数据无法分离:历史归档数据和热数据挤在同一批 SSD 上,成本高昂。
  • 跨机房部署不可控:无法指定某份数据必须落在哪个机房、哪批机器。

Placement Rules 的本质是:将 PD 内部的调度能力通过 SQL 暴露给用户,让用户可以基于 TiKV 节点的物理标签(Label)精确控制数据副本的存放位置。


二、核心架构:三个角色的协作

理解 Placement Rules 必须先理解 TiDB 存储层的三个核心角色如何协作:

┌─────────────────────────────────────────────────────────────┐
│                        TiDB 集群                             │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐                │
│  │ TiDB    │    │ TiDB    │    │ TiDB    │   无状态 SQL 层   │
│  │ Server  │    │ Server  │    │ Server  │                │
│  └────┬────┘    └────┬────┘    └────┬────┘                │
│       │              │              │                       │
│       └──────────────┼──────────────┘                       │
│                      │                                        │
│  ┌───────────────────┼───────────────────┐                  │
│  │                   ▼                   │  PD 调度层        │
│  │  ┌─────────────────────────────────┐  │                  │
│  │  │  Placement Driver (PD)          │  │                  │
│  │  │  - 存储集群元数据                 │  │                  │
│  │  │  - 维护 Region 路由表             │  │                  │
│  │  │  - 产生调度指令(副本增减、迁移)   │  │                  │
│  │  │  - 解析 Placement Policy         │  │                  │
│  │  └─────────────────────────────────┘  │                  │
│  └─────────────────────────────────────┘                  │
│                      │                                        │
│  ┌───────────────────┼───────────────────┐                  │
│  │                   ▼                   │  TiKV 存储层      │
│  │  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  │                  │
│  │  │TiKV │  │TiKV │  │TiKV │  │TiKV │  │  多副本持久化    │
│  │  │Node1│  │Node2│  │Node3│  │Node4│  │                  │
│  │  │zone1│  │zone1│  │zone2│  │zone2│  │  Label: zone,host│
│  │  └─────┘  └─────┘  └─────┘  └─────┘  │                  │
│  └─────────────────────────────────────┘                  │
└─────────────────────────────────────────────────────────────┘

2.1 TiKV:带"门牌号"的仓库

每个 TiKV 节点在启动时携带 Labels(标签),这些标签就是它的"门牌号":

# tikv 启动参数
--labels zone=zone1,host=host13-1,disk=ssd
Label 含义 作用
zone 可用区/机房 跨机房容灾调度
host 物理机 单机容灾调度
disk 磁盘类型 冷热分离调度
rack 机架 机架级容灾

重要:TiKV 只负责"上报自己的标签",它不知道这些标签会被如何使用。

2.2 PD:调度大脑与"翻译官"

PD 是 Placement Rules 的核心执行者,它承担两个关键职责:

职责 A:理解拓扑层级(location-labels)

PD 的配置文件 pd.toml 中有一个关键配置:

[replication]
location-labels = "zone,host"
max-replicas = 3

这里的 location-labelsPD 的"翻译字典"。它告诉 PD:

"TiKV 节点上报的众多标签中,zonehost 这两个键名代表物理位置层级。调度副本时必须保证同一 Region 的多个副本分散在不同的 zone 和不同的 host 上。"

为什么必须有这个配置?

假设 TiKV 带了这些标签:zone=zone1,host=host13-1,disk=ssd,version=v7.5.0

PD 收到后,如果不配置 location-labels,它无法区分:

  • disk=ssd 只是磁盘属性,与位置无关
  • zone=zone1 代表机房位置,与容灾强相关

配置了 location-labels = "zone,host" 后,PD 就能构建出集群的拓扑树:

zone1
  ├── host13-1 (TiKV-1)
  ├── host14-1 (TiKV-2)
  └── host15-1 (TiKV-3)
zone2
  ├── host16-1 (TiKV-4)
  ├── host17-1 (TiKV-5)
  └── host18-1 (TiKV-6)

职责 B:执行 Placement Policy

当用户通过 SQL 创建 Placement Policy 时,TiDB 会将策略转换为规则下发给 PD。PD 结合 location-labels 解析这些规则,并产生具体的调度指令:

  1. Add Learner:在目标节点添加副本(Learner 角色,不参与投票)
  2. Promote Learner:Learner 数据追齐后,提升为 Follower
  3. Remove Replica:从旧节点移除多余副本
  4. Transfer Leader:将 Leader 切换到符合约束的节点

这些调度指令通过心跳机制异步下发给 TiKV 执行。

2.3 TiDB:策略的翻译器与接口层

TiDB 负责将用户友好的 SQL 语句翻译为 PD 能理解的规则格式:

-- 用户输入
CREATE PLACEMENT POLICY p1 CONSTRAINTS="[+zone=zone2]";

-- TiDB 内部转换为 PD 规则(简化示意)
{
  "group_id": "TiDB_DDL",
  "id": "table_t1",
  "start_key": "7480000000000000ff...",
  "end_key": "7480000000000000ff...",
  "constraints": [{"key": "zone", "op": "in", "values": ["zone2"]}],
  "count": 3
}

三、深入工作原理:Region 调度的完整生命周期

3.1 数据分片与 Raft 副本

TiDB 将数据按表/分区划分为多个 Region,每个 Region 默认约 96MB。每个 Region 在 TiKV 层面对应一个 Raft Group,通常包含 3 个副本:

  • 1 个 Leader:负责处理读写请求,强一致性写入需 Leader 确认
  • 2 个 Follower:通过 Raft 日志复制保持与 Leader 的数据一致
Region-100:
  Leader    → TiKV-1 (zone1, host13-1)
  Follower  → TiKV-2 (zone1, host14-1)
  Follower  → TiKV-3 (zone1, host15-1)

3.2 Placement Policy 绑定后的调度流程

当你执行 ALTER DATABASE db_new PLACEMENT POLICY = zone2_policy 时,底层发生了以下事情:

阶段 1:规则下发(秒级)

  1. TiDB 将 db_new 下所有表的 Region 范围与 zone2_policy 关联
  2. 生成新的 Placement Rules 推送到 PD
  3. PD 的规则引擎立即生效,开始评估现有 Region 分布

阶段 2:调度产生(异步后台)

PD 的 replicationCheckerruleChecker 模块对每个 Region 进行检查:

  • 目标状态:Region 的 3 个副本都应在 zone=zone2 的节点上
  • 当前状态:Region 的 3 个副本都在 zone=zone1 的节点上
  • 差异检测:需要产生"迁移调度"

PD 产生的调度计划(简化):

Region-100: 需要从 zone1 → zone2
  Step 1: 在 TiKV-4 (zone2) 添加 Learner 副本
  Step 2: 等待 Learner 数据追齐(Snapshot + Raft log)
  Step 3: 将 Learner 提升为 Follower
  Step 4: 删除 TiKV-1 (zone1) 上的旧副本
  Step 5: 重复直到 zone1 副本数为 0,zone2 副本数为 3

阶段 3:调度执行(TiKV 侧)

TiKV 收到 PD 的调度指令后,执行副本搬迁

  1. Snapshot 传输:新节点(TiKV-4)从 Leader(TiKV-1)拉取全量 Snapshot
  2. Raft 日志追齐:Snapshot 之后的新写入通过 Raft 日志同步
  3. 角色切换:数据追齐后,PD 将 Learner 提升为 Follower
  4. 旧副本清理:新副本确认可用后,旧节点上的副本被安全删除

参考:TiKV 副本搬迁原理

阶段 4:持续均衡

迁移完成后,PD 的 balance-region-scheduler 会继续在 zone2 的 3 台节点之间微调 Region 分布,确保磁盘空间和 QPS 均衡。

3.3 已有数据是否自动迁移?

是的,完全自动。

  • 绑定 Placement Policy 后,PD 会立即对所有相关 Region 产生迁移调度。
  • 迁移速度受 PD 参数控制(如 region-schedule-limitleader-schedule-limit)。
  • 迁移过程中,业务读写不受影响,因为每个 Region 始终保持多数派副本可用。
  • 可以通过监控面板(TiDB Dashboard / Grafana)查看 Region healthRegion pending 等指标观察进度。

3.4 新数据写入去哪里?

  • 如果某张表的 Region 已经完成了迁移,新写入会直接路由到 zone2 的新节点
  • 如果某 Region 还在迁移中(新旧副本并存),新写入仍由 Leader 处理,Leader 可能暂时在旧节点,但写入会通过 Raft 同步到新节点的 Learner。
  • 迁移完成后,PD 会触发 Leader Transfer,将 Leader 也转移到 zone2 节点上(如果策略要求)。

四、关键配置深度解析

4.1 常规放置选项(适合简单场景)

选项 说明 底层含义
PRIMARY_REGION 指定 Raft Leader 放在哪个 region 对 Leader 添加 +region=xxx 约束
REGIONS 指定 Follower 可放在哪些 region 对 Follower 添加 +region=xxx 约束
SCHEDULE Follower 分布策略 EVEN 均匀分布 / MAJORITY_IN_PRIMARY 主 region 多数派
FOLLOWERS Follower 数量 FOLLOWERS=2 = 3 副本(1 Leader + 2 Follower)

限制PRIMARY_REGION/REGIONS/SCHEDULE 不能与 CONSTRAINTS 同时混用,因为它们底层逻辑冲突。

4.2 高级放置选项(适合复杂场景)

选项 说明 示例
CONSTRAINTS 适用于所有角色(Leader + Follower)的约束 CONSTRAINTS="[+zone=zone2]"
LEADER_CONSTRAINTS 仅约束 Leader 的位置 LEADER_CONSTRAINTS="[+zone=zone2]"
FOLLOWER_CONSTRAINTS 仅约束 Follower 的位置,支持字典格式指定副本数 FOLLOWER_CONSTRAINTS="{+zone=zone2: 2}"
LEARNER_CONSTRAINTS 仅约束 Learner 的位置 LEARNER_CONSTRAINTS="[+zone=zone1]"
LEARNERS Learner 数量 LEARNERS=1(额外加 1 个只读副本)
SURVIVAL_PREFERENCE 副本容灾优先级 SURVIVAL_PREFERENCE="[region, zone, host]"

4.3 约束语法详解

-- 列表格式:所有副本放在 zone2
CONSTRAINTS="[+zone=zone2]"

-- 列表格式:数据放在 SSD 上,同时排除故障节点
CONSTRAINTS="[+disk=ssd, -host=host13-1]"

-- 字典格式:在 zone2 放 2 个 Follower,在 zone1 放 1 个 Follower
FOLLOWER_CONSTRAINTS="{+zone=zone2: 2, +zone=zone1: 1}"

-- 冒号后必须有空格,否则 YAML 解析可能出错!
-- 错误:"{+zone=zone2:1}"  → 可能解析为 {"+zone=zone2:1": null}
-- 正确:"{+zone=zone2: 1}"  → 解析为 {"+zone=zone2": 1}
符号 含义
+ 必须满足(必须在该标签的节点上)
- 必须排除(不能在该标签的节点上)

五、用户场景实战:旧集群 + 新节点隔离

5.1 场景描述

  • 现有集群:3 台 TiKV(host13-1, host14-1, host15-1),label 均为 zone=zone1
  • 新增节点:3 台 TiKV(host16-1, host17-1, host18-1),计划 label 为 zone=zone2
  • 目标:将数据库 db_new 的所有数据迁移到新节点,后续新数据也写入新节点,实现与旧集群的物理隔离

5.2 前置条件检查

检查 1:确认 PD 已配置 location-labels

# 使用 pd-ctl 或 tiup ctl 查看
$ tiup ctl:v7.5.0 pd config show | grep location-labels
  "location-labels": "zone,host"

如果未配置,需修改 pd.toml 并重启 PD(生产环境建议滚动重启):

[replication]
location-labels = "zone,host"
max-replicas = 3

核心理解:PD 本身不存储数据,不需要 label。它只需要 location-labels理解 TiKV 标签的语义层级。没有这个配置,PD 把 zone 当普通字符串,不会按机房/机器维度做容灾隔离。

检查 2:确认新 TiKV 已正确配置 Label 并加入集群

新 TiKV 启动参数:

# host16-1
tikv-server --labels zone=zone2,host=host16-1

# host17-1
tikv-server --labels zone=zone2,host=host17-1

# host18-1
tikv-server --labels zone=zone2,host=host18-1

验证节点状态:

-- 查看 TiKV 可用标签
SHOW PLACEMENT LABELS;

-- 期望输出
+------+----------------------------------+
| Key  | Values                           |
+------+----------------------------------+
| host | ["host13-1","host14-1","host15-1","host16-1","host17-1","host18-1"] |
| zone | ["zone1", "zone2"]               |
+------+----------------------------------+

5.3 创建 Placement Policy

-- 创建 zone2 专用策略:所有副本(1 Leader + 2 Follower)都放在 zone2
CREATE PLACEMENT POLICY zone2_isolation
  CONSTRAINTS = "[+zone=zone2]";

-- 验证策略创建
SHOW CREATE PLACEMENT POLICY zone2_isolation\G

底层发生了什么?

  • TiDB 在 mysql.placement_policies 系统表中保存了策略定义
  • 同时向 PD 注册了一个名为 zone2_isolation 的规则模板
  • PD 的规则引擎现在知道:当某个 Region 被标记为使用 zone2_isolation 时,其副本必须满足 zone=zone2

5.4 绑定策略到数据库

-- 已有数据库迁移到新节点
ALTER DATABASE db_new PLACEMENT POLICY = zone2_isolation;

-- 后续创建新数据库直接指定
CREATE DATABASE db_future PLACEMENT POLICY = zone2_isolation;

绑定后的调度流程

  1. TiDB 扫描 db_new 下的所有表和分区,获取它们的 Region Key Range
  2. 将这些 Key Range 与 zone2_isolation 规则关联,推送给 PD
  3. PD 对每个相关 Region 执行检查:
    • 当前副本分布在 zone1 → 不满足规则
    • 产生迁移调度 → 逐步迁移到 zone2
  4. 后台异步搬迁,业务无感知

5.5 监控迁移进度

-- 查看所有 Placement 绑定状态
SHOW PLACEMENT;

-- 查看具体 Region 的分布(通过 TiDB 系统表)
SELECT 
  p.db_name, p.table_name, p.partition_name,
  p.policy_name, p.scheduling_state
FROM information_schema.placement_policies p;

Grafana 监控面板关注:

  • PD → Region health:观察 pending-peer-region-countmiss-peer-region-count
  • TiKV → Cluster:观察各节点 Region 数量变化
  • TiKV → RocksDB:迁移期间的磁盘 IO 和网络带宽

5.6 验证最终分布

# 使用 pd-ctl 查看某个 Region 的副本分布
$ tiup ctl:v7.5.0 pd region <region_id>

# 期望看到 peers 分布在 zone2 的 3 个 host 上
"peers": [
  {"id": 101, "store_id": 4, "role": "Leader"},   # host16-1
  {"id": 102, "store_id": 5, "role": "Follower"}, # host17-1
  {"id": 103, "store_id": 6, "role": "Follower"}  # host18-1
]

六、常见误区与注意事项

6.1 误区一:PD 也需要配置 label

错误理解:"PD 是不是也要配置 zone=zone1 的标签?"

正确理解:PD 不存储数据副本,它只负责"看懂"TiKV 的 label 并产生调度指令。PD 需要的是 location-labels 配置来建立"拓扑语义",而不是自身的标签。

6.2 误区二:设置 Label 后数据立即迁移

注意

  • Label 设置只是前置条件,不会自动触发迁移。
  • 必须创建并绑定 Placement Policy 后,PD 才会产生调度。
  • 迁移是异步后台过程,大规模数据可能需要数小时甚至更久。

6.3 误区三:FOLLOWERS=2 就一定有 3 副本

注意:如果 CONSTRAINTS 匹配的节点数量不足,实际副本数可能低于预期。例如:

CREATE PLACEMENT POLICY p1 CONSTRAINTS="[+zone=zone2]" FOLLOWERS=2;

如果 zone2 只有 2 台 TiKV,那么最多只能产生 2 个副本(1 Leader + 1 Follower),无法满足 3 副本。因此约束条件必须匹配足够多的节点。

6.4 生产环境建议

建议项 说明
策略数量 单个集群 Placement Policy 不超过 10 个
绑定对象数 绑定策略的表/分区总数不超过 10000 个
复杂度 优先使用简单约束,避免多层嵌套复杂规则
容灾 3 副本分布在 3 台不同 host,建议进一步增加 rack 标签实现跨机架
监控 迁移期间关注 pd_checkertikv_snapshot 相关指标
回退 绑定策略后如需回退,执行 ALTER DATABASE xxx PLACEMENT POLICY = default;

七、总结

概念 一句话解释
TiKV Label TiKV 节点启动时携带的"门牌号",标识自己的物理位置
PD location-labels PD 的"翻译字典",告诉 PD 哪些标签键名代表物理位置层级
Placement Policy 通过 SQL 定义的数据存放规则,约束 Region 副本的位置
调度流程 PD 检查规则 → 产生迁移计划 → TiKV 执行 Snapshot 搬迁 → 异步完成
数据迁移 绑定策略后,已有数据自动迁移,新数据直接写入目标节点

你的场景结论

  1. ✅ 新 3 台 TiKV 直接设置 zone=zone2,host=hostxx-1 的 label,完全可行。
  2. ✅ 创建 CONSTRAINTS="[+zone=zone2]" 的 Placement Policy 并绑定到数据库。
  3. ✅ 已有数据会自动后台迁移,新数据会直接写入 zone2
  4. ⚠️ 确保 PD 已配置 location-labels = "zone,host",否则调度不生效。

如需进一步了解 TiKV 副本搬迁的底层实现(Snapshot 传输、Raft 日志同步、流量控制等),可参考官方资料

0
0
1
0

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

评论
暂无评论