TiDB 调度
Placement Driver (PD) 在 TiDB 集群中充当管理者角色,同时负责调度集群中的 Regions。本文介绍 PD 调度组件的设计和核心概念。
调度场景
TiKV 是 TiDB 使用的分布式键值存储引擎。在 TiKV 中,数据被组织为 Regions,这些 Regions 会在多个存储节点上进行复制。在所有副本中,Leader 负责读写操作,Follower 负责复制 Leader 的 Raft 日志。
现在考虑以下场景:
- 为了高效利用存储空间,同一 Region 的多个副本需要根据 Region 大小合理分布在不同节点上;
- 对于多数据中心拓扑结构,一次数据中心故障只会导致所有 Regions 的一个副本失效;
- 当新增 TiKV 存储节点时,可以对数据进行重新平衡;
- 当 TiKV 存储节点发生故障时,PD 需要考虑:
- 失效存储的恢复时间。
- 如果时间短(例如,服务重启),是否需要调度。
- 如果时间长(例如,磁盘故障导致数据丢失),应如何调度。
- 所有 Regions 的副本。
- 如果某些 Regions 的副本数不足,PD 需要补充。
- 如果副本数超过预期(例如,故障存储恢复后重新加入集群),PD 需要删除多余的副本。
- 失效存储的恢复时间。
- 读写操作在 Leader 上进行,不能只在少数几个存储节点上分布;
- 不是所有的 Regions 都是热点,因此需要平衡所有 TiKV 存储节点的负载;
- 在 Regions 进行平衡时,数据传输会消耗大量网络/磁盘流量和 CPU 时间,可能影响线上服务。
这些场景可能同时发生,增加了解决的难度。同时,整个系统在动态变化,因此需要调度器收集关于集群的所有信息,然后进行调整。因此,引入 PD 到 TiDB 集群中。
调度需求
上述场景可以归类为两类:
一个分布式且高可用的存储系统必须满足以下要求:
- 适当的副本数量。
- 副本需要根据不同的拓扑结构分布在不同的机器上。
- 集群能够自动从 TiKV 节点故障中进行灾难恢复。
一个良好的分布式系统需要具备以下优化:
- 所有 Region Leader 在存储节点上均匀分布;
- 所有 TiKV 节点的存储容量平衡;
- 热点负载平衡;
- 负载均衡的速度需要有限制,以确保线上服务的稳定;
- 维护人员可以手动将 Peer 上线/下线。
在满足第一类需求后,系统将具备容错能力;而满足第二类需求后,资源利用会更高效,系统的扩展性也会更好。
为了实现这些目标,PD 需要首先收集信息,例如 Peer 状态、Raft 组信息以及访问 Peer 的统计数据。然后需要为 PD 制定一些策略,使其能根据这些信息和策略制定调度计划。最后,PD 会向 TiKV Peer 分发操作指令,完成调度计划。
基本调度操作
所有调度计划包含三种基本操作:
- 添加新副本
- 删除副本
- 在 Raft 组中的副本之间转移 Region Leader
它们由 Raft 命令 AddReplica
、RemoveReplica
和 TransferLeader
实现。
信息收集
调度依赖于信息的收集。简而言之,PD 调度组件需要了解所有 TiKV Peer 和所有 Regions 的状态。TiKV Peer 会向 PD 上报以下信息:
每个 TiKV Peer 上报的状态信息:
每个 TiKV Peer 定期向 PD 发送心跳。PD 不仅检查存储是否存活,还会在心跳消息中收集
StoreState
。StoreState
包含:总磁盘空间
可用磁盘空间
Regions 数量
读写速度
发送/接收快照的数量(快照可能在副本之间复制)
是否超载
标签(详见 Perception of Topology)
你可以使用 PD 控制命令检查 TiKV 存储的状态,状态可能为 Up、Disconnect、Offline、Down 或 Tombstone。以下是所有状态的描述及其关系。
Up:TiKV 存储正常服务中。
Disconnect:PD 与 TiKV 存储之间的心跳超过 20 秒未收到。如果超过
max-store-down-time
指定的时间,状态由 "Disconnect" 变为 "Down"。Down:PD 与 TiKV 存储之间的心跳超过
max-store-down-time
(默认 30 分钟)。在此状态下,TiKV 存储会开始补充存活存储上的各个 Region 的副本。Offline:通过 PD 控制手动将 TiKV 存储下线。此状态仅为中间状态,存储在此状态下会将所有 Region 转移到符合迁移条件的其他 "Up" 存储。当
leader_count
和region_count
(通过 PD 控制获取)都显示为0
时,存储状态变为 "Tombstone"。在 "Offline" 状态下,不要禁用存储服务或存储所在的物理服务器。在存储下线的过程中,如果集群没有目标存储进行 Region 迁移(例如,存储不足以容纳副本),存储会一直处于 "Offline" 状态。Tombstone:TiKV 存储完全离线。可以使用
remove-tombstone
接口安全清理处于此状态的 TiKV。从 v6.5.0 开始,如果不手动处理,PD 会在节点变为 Tombstone 一个月后自动删除内部存储的 Tombstone 记录。
Region Leader 上报的信息:
每个 Region Leader 定期向 PD 发送心跳,报告
RegionState
,包括:- Leader 自身位置
- 其他副本位置
- 离线副本数量
- 读写速度
PD 通过这两类心跳收集集群信息,然后据此做出决策。
此外,PD 还可以通过扩展接口获取更多信息,以做出更精准的决策。例如,如果存储的心跳中断,PD 无法判断 Peer 是暂时下线还是永久下线。它会等待一段时间(默认 30 分钟),如果仍未收到心跳,则视为存储离线,然后将该存储上的所有 Region 在其他存储上进行平衡。
但有时存储是由维护人员手动设置为离线状态,维护人员可以通过 PD 控制接口告知 PD。这样,PD 可以立即进行所有 Region 的平衡。
调度策略
收集完信息后,PD 需要一些策略来制定调度计划。
Strategy 1: Region 的副本数需要正确
PD 可以通过 Region Leader 的心跳知道某个 Region 的副本数是否正确。如果不正确,PD 可以通过添加/删除副本来调整。副本数不正确的原因可能包括:
- 存储故障,导致某些 Region 的副本数少于预期;
- 故障后存储恢复,导致某些 Region 的副本数多于预期;
max-replicas
被修改。
Strategy 2: Region 的副本需要在不同位置
注意这里的“位置”不同于“机器”。一般 PD 只能确保 Region 的副本不在同一 Peer 上,以避免 Peer 故障导致多个副本丢失。然而在生产环境中,可能有以下需求:
- 多个 TiKV Peer 在同一台机器上;
- TiKV Peer 分布在多个机架上,即使某个机架故障,系统仍应可用;
- TiKV Peer 分布在多个数据中心,即使某个数据中心故障,系统仍应可用;
这些需求的关键在于 Peer 可以具有相同的“位置”,这是容错的最小单元。Region 的副本不能全部在同一单元内。因此,可以为 TiKV Peer 配置 labels,并在 PD 上设置 location-labels,以指定哪些标签用于标记位置。
Strategy 3: 副本需要在存储节点之间平衡
Region 副本的大小是固定的,因此保持副本在存储节点之间的平衡有助于数据大小的均衡。
Strategy 4: Leader 需要在存储节点之间平衡
读写操作在 Leader 上进行,按照 Raft 协议,PD 需要将 Leader 分布到整个集群中,而不是只在少数几个 Peer 上。
Strategy 5: 热点需要在存储节点之间平衡
PD 可以通过存储节点和 Region 的心跳检测热点,从而实现热点的分布。
Strategy 6: 存储容量需要在存储节点之间平衡
启动时,TiKV 存储会报告 capacity
,表示存储空间的限制。PD 在调度时会考虑这一点。
Strategy 7: 调整调度速度以稳定线上服务
调度会消耗 CPU、内存、网络和 I/O 流量。资源利用过度会影响线上服务,因此 PD 需要限制同时进行的调度任务数。默认此策略较为保守,但如果需要更快的调度速度,可以进行调整。
调度实现
PD 通过存储节点和 Region 的心跳收集集群信息,然后根据这些信息和策略制定调度计划。调度计划是一系列基本操作的组合。每次 PD 收到 Region Leader 的 Region 心跳时,会检查该 Region 是否已有待执行的操作。如果需要调度新的操作,PD 会将操作放入心跳响应中,并通过后续的 Region 心跳监控操作的执行情况。
注意,这里的“操作”仅是对 Region Leader 的建议,Region 可以选择跳过。Region 的 Leader 会根据当前状态决定是否跳过调度操作。