作者: Aunt-Shirly
作为一个分布式数据库,扩缩容是 TiDB 集群最常见的运维操作之一。本系列文章,我们将基于 v7.5.0 具体介绍扩缩容操作的具体原理、相关配置及常见问题。 扩缩容从集群视角考虑,主要需要考虑的是扩缩容完成后,集群数据通过调度,让所有在线 tikv 的资源使用到达一个均衡的状态。在这个过程中,主要涉及以下两个关键步骤:
- 调度产生速度
- 调度执行速度
本系列文章我们将围绕以上两个逻辑,重点介绍扩缩容过程中的核心模块及常见问题,分为以下几个部分:
- TiDB 扩容原理及常见问题
- 扩容过程中调度生成原理及常见问题
- 缩容过程中调度生成原理及常见问题
- 扩缩容过程调度执行(TiKV 副本搬迁)的原理及常见问题
本文我们重点介绍扩缩容过程中,TiKV 侧调度消费的原理及常见问题。对于任何需要在 TiKV 间搬迁数据的调度消费原理,都可以参考本文。
TiKV 副本搬迁原理及常见问题
TiKV 之间的副本搬迁一般出现在:
- 扩容新节点,需要将数据在新老 tikv 节点做 balance, 老节点上的数据会往新节点搬迁。在 tikv 数量 VS 新节点数量比较大的情况下,新节点的写入压力最可能成为瓶颈
- 下线节点,下线过程中需要将下线 kv 上的数据搬迁到现有 tikv 中
- 热点调度及正常的 balance-region 调度等
- Placement-rule 突然发生变更(副本规则发生变更)
副本搬迁的完整步骤在第一章已经介绍过,下面我们重点介绍一下副本搬迁的 add learner 和 remove learner 操作。
Add learner 步骤概览
接下来,我们从 TiKV 中线程池的视角,看一下 add learner peer
具体是怎么操作的:
- 首先 tikv 由
pd-worker
处理 来自 PD 的 hearbeat, PD 在收到add learner peer
请求后,将这个消息交给了raftstore
- Raftstore 收到
add learner
操作的请求后,作为 region 的 leader 主要做了以下两件事情:
- 在
raft-group
里 广播add learner peer
的基本信息
- 要加
learner
的 tikv 节点也会在本地将这个 region 的元数据创建出来
- 交给 apply 线程池 apply 这条
add learner
的 admin 消息
- Apply 线程池在收到
add learner
这个消息后,更新本地 region 元数据信息后,开始生成snapshot
并发送给目标 tikv, 这里主要涉及到以下几个线程池:
-
region-worker
处理 apply 线程池发过来的Task::Gen
任务消息,将任务发给snap-generator
去生成 snapshot.
- Apply 收到
snap-generator
生成 snapshot 完成的通知后,准备Task::Send
消息给snap-handler
,snap-handler
处理该任务消息,最终发给snap-sender
线程池,让其发送snapshot
给leaner
节点 - Learner 节点所在 TiKV 的 GRPC 线程池收到 snapshot 的消息后,准备
Task::REcv
任务给snap-handler
线程池,snap-hander
处理该任务消息,最终将收 snapshot 的工作交接给snap-sender
执行。 -
snap-sender
收取完 snapshot 数据后,通知 apply 线程池 snapshot 文件收集完毕 - Apply 线程发送给
region-worker
, 让其去将 snapshot apply 到 rocksdb 里面。 -
region-worker
处理 apply-snapshot 消息,让 rocksdb 去 apply snapshot 到本地。
以上步骤中,只要任何一个步骤卡住,都会造成 add learner 操作耗时上升。一般的,当我们发现 add-learner 超时时,可以将涉及数据搬迁的关键节点日志拿出来分析。
Add leaner 时 Leader 所在节点日志示例
Step 1: receive AddLeader operator from PD
Step 2: broadcast config change
Step 3: prepare snapshot
Step 3.1: generate snapshot
Step 3.2: send snapshot
Add learner 时 Learner 所在节点日志示例
Step 1: receive AddLeader raftmessage from leader:create empty peer
Step 2: receive snapshot file from leader
Step 3: restore snapshot file from leader
Step 4: apply snapshot to rocksdb
常见问题
Add leaner 卡住最常见的原因一般有以下几个:
- CPU 瓶颈:过程中涉及的线程池出现瓶颈
- IO 和网络瓶颈:snapshot 搬迁过程中涉及 io 和 网络开销
- Rocksdb apply snapshot 卡住,往往在 L0 文件堆积较多时,apply snapshot 就会卡住。从对应日志中可以看到最后一步日志迟迟没有出现或者完全卡死。
Add learner 陷入循环
Add learner 卡住后可能导致 tikv 这边进入死循环而导致副本搬迁陷入僵局,以下线为例来说:
- PD rule checker 执行时,fix-unhealthy peer 时生成
replace-rule-offline(leader)-peer
- PD 下发
add learner peer
- TiKV 开始执行
add learner peer
- PD 20(根据 region size 估算) 分钟后依旧没有收到
add learner peer
成功的消息,认为该任务超时,取消当前replace-rule-offline-peer
operator 。 - TiKV 在 30 分钟后
add learner
成功 ,TiKV 上报心跳给 PD,当前这个 region 会有一个 learner 节点。 - PD rule-cheker 执行时,先检查
orphan-peer
, 就把之前添加成功的 learner 删除了 - PD rule checker 再次执行时,回到步骤 1 , 至此进入循环。
线程池相关问题
region_worker
线程池相关问题
Region worker 主要职责为处理 snapshot generate/apply/destroy 消息
- 线程名:
region-worker
- Pending-tasks-label:"snapshot-worker"
- 线程数:1(不可配置)
- 处理任务:(
Task<EK::Snapshot>
)
-
Task::Gen
: 生成 snapshot, 会转发给snap-generator
线程池处理 -
Task::Apply
: apply snapshot,会让 rocksdb 将已经收到的 snapshot 文件 apply 到 rocksdb 中。‘ -
Task::Destroy
删除 snapshot, 主要是删除一段连续的范围,也就是删除 leaner 时使用。
- 常见问题:
region-worker
CPU 成为瓶颈:
- 现象:
- 从 CPU 粒度看 region worker CPU 已经跑满(目前没有单独的监控,可以用 tikv->CPU 下面选一个面板编辑公式得到)
snap-generator
线程池相关问题
该线程池主要用于生成具体的 snapshot,主要需要注意下面两个瓶颈
- CPU ,线程数:1~16,可通过 snap-generator-pool-size (default 2)调节
- IO 使用限制,通过 snap-io-max-bytes-per-sec(100MB) 控制
因此,当注意到以上两个地方遇到瓶颈时,可以通过修改对应配置项进行调整。如何判断以上两个到达性能瓶颈:
- CPU:与看 region-worker CPU 类似的方式,编辑公式后查看
snap-generator
CPU 的使用情况 - IO:通过 tikv-details->snapshot-> snapshot actions/snapshot size 估算得出,或者根据 snapshot transaport speed 估算。
可以通过 online config 直接修改,无需重启 tikv 实例:
snap-handler
&snap_sender
线程池相关问题
snap-handler
线程池主要用于分发调度 send/receive snapshot 相关的任务。而真正的发送和接收 snapshot 任务,则由 snap_sender
这个子线程池来完成。
snap-handler
线程数 1,写死不可编辑snap-handler
主要处理的任务有三种类型:
Task::Recv
— from: kv_service.snap_scheduler
- process by
snap_sender
pool
Task::Send
— from: AsyncRaftSender.snap_scheduler
- process by
snap_sender
pool
Task::RefreshConfigEvent
— from: server.snap_scheduler
- 控制
snapshot
发送和接收的并发:
- concurrent-send-snap-limit 32 by default
- concurrent-recv-snap-limit 32 by default
- 子线程池
snap_sender
线程数 4,也是写死在代码里无法配置。 - 相关监控:tikv-details->snapshot->snapshot transport speed & snapshot state count
summary
目前 tikv 侧线程池相关可用参数有且只有下面四个,遇到问题时根据上文所述的常见问题特征进行调整:
- snap-generator-pool-size (default 2), used by snap-generator
- snap-io-max-bytes-per-sec (100MB by default),used by snap-generator
- concurrent-send-snap-limit(32 by default) used by snap_handler.snap_sender
- concurrent-recv-snap-limit (32 by default) used by snap_handler.snap_sender
Ingest sst 导致 Apply wait duration 上涨
- 现象:apply wait duration 升高,但是 apply 不高且 apply CPU 不忙且增大 apply-pool-size 无用。
- 原因: ingest sst 会使用到 rocksdb 的 global mutex tikv#5911 会导致 apply wait 上涨。从老 tikv 将数据搬迁到新 tikv 时,删除老的 region 走的也是 ingest sst 的方式 tikv#7794。
- Workaround: 业务高峰期降低数据搬迁的速度,必要时 store limit 调整至 0.000001。目前这方面优化还在进行中。