TiDB PD 组件代码阅读
作者: sunxiaoguang 原文来源:https://tidb.net/blog/a19e33e6
PD (Placement Driver) 是 TiDB 的大脑,它负责全局元数据的存储以及 TiKV 组件的负载均衡调度。
基本名词解释:
Store: 一个 TiKV 存储实例在 PD 中对应一个 Store,PD 为一个 Store 维护了下面的元信息
{
“store”: {
“id”: xx,
“address”: “xx.xx.xx.xx:xxxx”,
“labels”: [
{ "key": "host", "value": "xxx.xxx.xxx" }, { "key": "zone", "value": "xx" }
],
“version”: “2.1.8”,
“state_name”: “Up”
},
“status”: {
“capacity”: “3.6 TiB”,
“available”: “2.3 TiB”,
“leader_count”: 4898,
“leader_weight”: 1,
“leader_score”: 2215540,
“leader_size”: 2215540,
“region_count”: 14750,
“region_weight”: 1,
“region_score”: 6665256,
“region_size”: 6665256,
“start_ts”: “2019-09-27T23:51:53+08:00”,
“last_heartbeat_ts”: “2019-10-10T14:09:03.568260056+08:00”,
“uptime”: “302h17m10.568260056s”
}
}
Region: 负载均衡的最小单元。TiKV 中的全量数据是以有序的方式存储的,PD 根据数据的尺寸将全量数据分割成一系列的 Region,每一个 Region 承载全量数据中一段较小范围的数据。PD 为每一个 Region 维护了下面的元信息
{
“id”: 10001,
“start_key”: “xxx”,
“end_key”: “yyy”,
“epoch”: {
“conf_ver”: 50,
“version”: 268
},
“peers”: [
{
"id": 7820688, "store_id": 7583502
},
{
"id": 9147810, "store_id": 7586672
},
{
"id": 9186477, "store_id": 7584594
}
],
“leader”: {
“id”: 7820688,
“store_id”: 7583502
},
“written_bytes”: 15639,
“read_bytes”: 236,
“approximate_size”: 515,
“approximate_keys”: 4747086
}
Peer: Store 在 Region 上的一次具体绑定
Scheduler: 调度器,目前 master 版本上已有的 scheduler 种类如下
adjacent-region: 将连续的 region 的 leader 分散到不同的 store 上
balance-leader: 将 leader 在所有的 store 上均匀的分散开
balance-region: 将 region 在所有的 store 上均匀的分散开
evict-leader: 将指定 store 上的 leader 全部驱逐走
grant-leader: 将指定 store 负责的所有 region 的 leader 都收集到指定的 store 上
balance-hot-region: 将热点 region 在所有的 store 上均匀的分散开
random-merge: 随机挑选两个连续的 region 进行合并
scatter-range: 将指定 key 范围内的 region 在 store 见均匀的分散开
shuffle-hot-region: 将热点 region 的 leader 调度到一个随机 store 上(测试用)
shuffle-leader: 将 leader 随机的 shuffle 到 store 上
shuffle-region: 将 region 随机的 shuffle 到 store 上
Operator: 调度器实际发出的调度任务,目前 master 版本上已有的 operator 类型如下
TransferLeader
AddPeer
AddLearner
PromoteLearner
RemovePeer
MergeRegion
SplitRegion
AddLightPeer
TSO: 时间戳分配器
Label: 节点标签,用作调度策略的输入数据
Replica: Region 数据副本
Leader: 当前负责读写操作的副本
主体代码目录结构:
client: pd go 语言 client,主要接口如下
// Client is a PD (Placement Driver) client.
// It should not be used after calling Close().
type Client interface {
// GetClusterID gets the cluster ID from PD.
GetClusterID(ctx context.Context) uint64
// GetTS gets a timestamp from PD.
GetTS(ctx context.Context) (int64, int64, error)
// GetTSAsync gets a timestamp from PD, without block the caller.
GetTSAsync(ctx context.Context) TSFuture
// GetRegion gets a region and its leader Peer from PD by key.
// The region may expire after split. Caller is responsible for caching and
// taking care of region change.
// Also it may return nil if PD finds no Region for the key temporarily,
// client should retry later.
GetRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error)
// GetPrevRegion gets the previous region and its leader Peer of the region where the key is located.
GetPrevRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error)
// GetRegionByID gets a region and its leader Peer from PD by id.
GetRegionByID(ctx context.Context, regionID uint64) (*metapb.Region, *metapb.Peer, error)
// ScanRegion gets a list of regions, starts from the region that contains key.
// Limit limits the maximum number of regions returned.
// If a region has no leader, corresponding leader will be placed by a peer
// with empty value (PeerID is 0).
ScanRegions(ctx context.Context, key []byte, limit int) ([]*metapb.Region, []*metapb.Peer, error)
// GetStore gets a store from PD by store id.
// The store may expire later. Caller is responsible for caching and taking care
// of store change.
GetStore(ctx context.Context, storeID uint64) (*metapb.Store, error)
// GetAllStores gets all stores from pd.
// The store may expire later. Caller is responsible for caching and taking care
// of store change.
GetAllStores(ctx context.Context, opts …GetStoreOption) ([]*metapb.Store, error)
// Update GC safe point. TiKV will check it and do GC themselves if necessary.
// If the given safePoint is less than the current one, it will not be updated.
// Returns the new safePoint after updating.
UpdateGCSafePoint(ctx context.Context, safePoint uint64) (uint64, error)
// ScatterRegion scatters the specified region. Should use it for a batch of regions,
// and the distribution of these regions will be dispersed.
ScatterRegion(ctx context.Context, regionID uint64) error
// GetOperator gets the status of operator of the specified region.
GetOperator(ctx context.Context, regionID uint64) (*pdpb.GetOperatorResponse, error)
// Close closes the client.
Close()
}
pkg: utility 功能
server: pd server 的主体代码
api: PD 的 Restful API 相关逻辑
cache: 缓存数据结构,目前用于管理热点统计信息
core: 核心业务逻辑以及集群元数据的持久化
namespace: namespace 功能目前已不推荐使用
placement: 副本放置策略脚本语言功能
region_syncer: 在 PD 副本间同步 region 元数据
schedule: operator 相关代码,各种类型的调度任务实现在 operator*.go 中,辅助实现调度任务的通用逻辑在其他文件中
scheduler: 调度器相关代码,多数调度器是以一个文件一个实现的方式组织的
statistics: 维护 region 和 store 的统计信息
table: TiDB key 编码相关代码,用于实现 namespace 功能(deprecated,不推荐使用)
调度器相关代码
interface: server/schedule/scheduler.go
// Scheduler is an interface to schedule resources.
type Scheduler interface {
GetName() string
// GetType should in accordance with the name passing to schedule.RegisterScheduler()
GetType() string
GetMinInterval() time.Duration
GetNextInterval(interval time.Duration) time.Duration
Prepare(cluster Cluster) error
Cleanup(cluster Cluster)
Schedule(cluster Cluster) []*Operator
IsScheduleAllowed(cluster Cluster) bool
}
各个调度器的主体调度逻辑实现在 Schedule(cluster Cluster) []*Operator 中
adjacent-region: server/scheduler/adjacent_region.go
从上次扫描结束的 key 开始扫描最多 1000 个 region
从扫描到的 region 中查找同一 leader 负责连续多个 region 的情况作为候选缓冲起来
对候选区间进行计算生成最终的调度任务
balance-leader: server/scheduler/balance_leader.go
在所有 store 中挑选出当前 leader score 最高和最低的两个分别作为源和目标
尝试对源 store 创建 leader 迁出的任务,如果无法迁出则尝试对目标 store 创建迁入任务
balance-region: server/scheduler/balance_region.go
从所有的 store 中挑选出来当前 region score 最高的节点作为迁移的源节点
查找是否存在 pending 的 region,有的话随机挑选一个 pending region 调度走
查找是否存在 follower region,有的话随机挑选一个 follower region 调度走
如果前两步选择出的 region 副本数同集群设定副本数不同,说明对应的 region 正在调整过程中,跳过这个 region 重新选择
如果选择的 region 是当前的热点 region,为了避免调度对 region 产生额外的压力跳过这个 region 重新选择
确认选择的 region 有满足集群放置策略的合适迁移目标,如果不存在合适目标则跳过这个 region 重新选择
创建迁移 region 的调度任务
evict-leader: server/scheduler/evict_leader.go
在 store 的 region 中随机挑选一个 leader region
选择对应 region 中的一个 follower 作为新的 leader
创建迁移 leader 的调度任务
grant-leader: server/scheduler/grant_leader.go
在 store 的 region 中随机挑选一个 follower region
创建迁移 leader 到当前 store 的调度任务
hot-region: server/scheduler/hot_region.go
随机决定做读热点调度还是写热点调度
根据 region 的统计信息进行热度打分
根据打分信息挑选一个候选节点进行调度操作
选择迁移的目标创建调度调度任务
label: server/scheduler/label.go
从所有的 store 中筛选出包含 reject-leader 属性的 store
遍历满足筛选条件的 store,在当前 store 上随机挑选一个 leader 并选择目标 store 创建调度任务
random-merge: server/scheduler/random_merge.go
随机挑选一个 store
在 store 上随机挑选一个 leader region 并获取它的相邻 region
创建 region 合并任务
scatter-range: server/scheduler/scatter_range.go
收集指定 key 区间内的 region 信息
创建将区间内的 leader 打散的任务
创建将区间内的 follower 打散的任务
shuffle_hot_region: server/scheduler/shuffle_hot_region.go
随机决定做读热点调度还是写热点调度
根据 region 的统计信息进行热度打分
随机挑选一个候选节点进行调度操作
选择迁移的目标创建调度调度任务
shuffle_leader: server/scheduler/shuffle_leader.go
随机挑选一个 store
在 store 上随机挑选一个 follower region
创建将 follower region 变成 leader 的调度任务
shuffle_region: server/scheduler/shuffle_region.go
随机挑选一个 store
在 store 上随机挑选一个 region
根据策略挑选一个目标 store
创建迁移 region 到目标 store 的调度任务
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/07d98d2a29b56c01fa5b57315】。文章转载请联系作者。
评论