背景说明
1、store 状态
是什么?
- 首先,这里说的
状态
,是store
的状态。即,这里不谈 region 的状态。 - 其次,这里的
store
,可以简单类比为 TiKV 节点。 - 最后,这个
状态
是指整个集群中的store 状态
,存储在 PD(ETCD)中的状态(可能跟 store 真实状态有偏差、不一致)。
分布式集群节点状态,是通过心跳来判断的。心跳依赖网络,所以网络异常导致无法准确判断节点状态。但是,心跳可以展示“正常”状态。
同样的,我们 TiKV 集群的 TiKV(Store)节点状态也是通过心跳判断,存储在 PD(ETCD),有 PD 负责维护。而状态展示也是 PD(ETCD) 中的数据 —— 注意,并不是直接去取节点的实际状态。
分布式集群、状态等是个很大的话题,这里简单说明下,方便后续内容理解
2、工具使用
既然研究 Store 状态
,必然需要经常查看,即需要查询 PD(ETCD) 存储的数据。我们这里有两个工具可以使用
2.1、etcdctl
这个是 etcd 本身直接提供的查询工具。我们这里需要用的命令如下:
ETCDCTL_API=3 ./etcdctl --endpoints $(ip:port) get --prefix=true /pd/
-
ETCDCTL_API: 指定 etcd 的 api 版本。一般用 3(还有就是 2)
-
etcdctl:这个是 etcd 的工具,可以通过 tiup 安装:
tiup ctl:$(version) etcd get
。- 版本不重要,因为 etcd 一般不会经常变
-
endpoints:访问的 api 接口的地址。一般就是 pd 的访问地址(比如 dashboard)
-
get:表示要做的事情。我们这里的测试,get 就够了。
-
“/pd/”:表示要取的数据的“目录”
- Etcd 一般以类似目录格式存放 key。
- 比如 “/” 下可以有多组key,下一层可以是 pd 可以是 cluster。
- 说不清楚,试试就明白了。
-
prefix:表示是否是前缀。
-
即 如果这个设置为 false,那么表示完整的 key 取查询。
-
如果设置为 true,那么表示前缀。比如这里的 “/pd/ ”,表示取 “/pd/*”,或者说取 pd 目录下的索引 key
- 另外,如果设置为 true,是递归访问的。比如这里的 "/pd/",那么 pd 下有还有 目录 x,那么就会继续遍历 目录 x
-
2.2、pd-ctl
这个本质也是访问 etcd 中存储的数据。但是,稍微做了一层封装:从etcd 中取出来的数据,经过处理、组装之后,再返回给客户展示。
我们这里主要查看 store 相关数据。所以,命令主要如下:
tiup ctl:v6.5.3 pd -u 10.2.8.4:32379 store
store 后面可以带的字命令:[delete | cancel-delete | label | weight | remove-tombstone | limit ] <store_id> [--jq="<query string>"]
这里是 官方文档参考
2.3、直接通过 pd 的 api 查询
这个可以到 dashboard 上查看 “高级调试 -> 内部调试数据” 查看具体的 API。这里,store 相关的 api 如下:
http://{ip:port}/pd/api/v1/store/{storeid}
http://{ip:port}/pd/api/v1/stores
理论说明
关于store 状态
相关,可以参考这里 官方文档。这里有部分说明。
各种状态
store 状态
有如下 5 种状态。这是从官方文档摘录。
-
Up:表示当前的 TiKV Store 处于提供服务的状态。
-
Disconnect:当 PD 和 TiKV Store 的心跳信息丢失超过 20 秒后,该 Store 的状态会变为
Disconnect
状态。 -
Down:当 PD 和 TiKV Store 的心跳信息丢失超过 30 分钟后,该 Store 的状态会变为
Down
状态, -
Offline:当对某个 TiKV Store 通过 PD Control 进行手动下线操作,该 Store 会变为 Offline 状态。
-
pd-ctl store delete
-
该状态只是 Store 下线的中间状态,处于该状态的 Store 会将其上的所有 Region 搬离至其它满足搬迁条件的 Up 状态 Store。
- 这个是 PD 发现 store 是 offline,就会触发调度搬迁 region。
-
-
Tombstone:当该 Store 的
leader_count
和region_count
为 0 后,该 Store 变为 Tombstone 状态。- 可以使用
remove-tombstone
接口安全地清理该状态的 TiKV。
- 可以使用
- “搬迁 region” 到底是做啥事情、动作?是先 add peer,然后再 remove peer ?一个 region,怎么才算搬迁成功?
- 如果部分 region 无法完成搬迁,会一直是 offline 状态。那么,应该怎么处理?
综述:
我个人理解,状态可以分为两类:
-
间接状态:需要通过心跳来判定、转换的状态。
- 包括
Disconnected
和Down
。
- 包括
-
直接状态:记录存储到 PD(ETCD) 中的。
- 在正常情况下(即没有丢失状态),那么直接查询到直接展示的状态。包括 UP/Offline/Tombstone
各种状态切换
下图是官方文档上的。
下图是获取 store 状态的时候的源码截图(点这里查看源码)。大约情况是:
- 获取原始数据状态
- 如果状态是 UP,根据 最后一次心跳做判断
根据状态转换和源码截图,可以得到如下信息:
-
如果离最后一次心跳超过20s(不到30分钟),那么就是
Disconnected
-
如果离最后一次心跳超过30分钟,就是
Down
- 即,如果 stop 了 TiKV 节点,那么该 TiKV 在 30分钟以内也是
Disconnected
- 并且,如果 stop 之后 20s 以内查看 TiKV 状态,应该是
Up
状态。
- 即,如果 stop 了 TiKV 节点,那么该 TiKV 在 30分钟以内也是
-
如果用户执行了 store delete,那么就是 Offline
- 即,表示要把该节点从 集群中剔除。
- 但是,还有数据要搬迁(可以类比为“离职员工交接”)。
- 所以,该状态是中间状态 —— 是要把事情处理完了(搬迁完了),才能走。
-
如果 Offline 节点的数据搬迁完了,那么就可以转成 Tombstone。
- 搬迁完,即 leader 和 region count 都是 0.
- 数据搬迁完了(交接完了),那么可以退出集群了——即,该集群不会再跟该节点联系了。
- 可以人工去删除数据。
-
那么,正常情况下,就是 UP 状态咯。
- 除了上述异常情况,那么就是正常情况咯。
各种状态会带来什么
如下截图,据说后续会改成 NodeState,很好的反应出要做啥事情。
-
UP:这种状态下,可以对外提供服务。不会做其它额外的事情。
-
Disconnected:好像不会做啥事情(?)
注意,这会导致 region 的 leader 重新选举,导致业务性能下降一波。
-
Down:这种情况下,会对该 store 上的 region 进行
补副本
。- 注意,是max-store-down-time 来控制的,在变成 down ,之后会任务改 store 不可用,会对 该store 上的 region 补副本。
-
Offline:这种情况下,需要对该 store 上的 region 进行
迁移
。- 迁移的意思是
补副本
+删除 该 store 上的 副本
—— 这里的“删除”,可以仅仅是 PD 中的信息删除。 - 看 NodeState 可以知道,它是正在删除 region 过程中,所以是 Removing。。。
- 迁移的意思是
-
TombStone:这种情况下,与集群没有关系了,所以该节点做啥都无所谓咯。
测试说明
初始化状态
刚刚部署好的集群,仅仅启动 PD ,集群中 store 是啥状态?
1、通过 etcdctl 查看,可以知道没有 store 相关数据。
2、通过 pd-ctl 查看,是报 500 的错误 —— 应该是因为没有数据导致的。
3、通过 tiup 查看集群状态,可以看到 TiKV 节点都是 Down 状态
启动之后状态
基于上面情况,启动整个集群之后,状态如下
stop之后状态(stop -R tikv)
1、立马查看:理论上说是 UP 状态
2、20秒之后查看:理论上说是 Disconnected(持续30分钟)
3、30分钟之后查看:理论上说是 Down
4、删除 Store(scale-in)
实际案例
案例1
集群说明:客户测试环境,k8s集群,3tikv 单副本系统。
问题说明:其中一个 tikv-3 由于某种原因变成了 Offline 状态,并且一直无法正常启动。查看状态可以知道,tikv 上还有9个region(leader)。
- 当前状态
当前 tikv 是 offline,并且无法正常启动。offline 表示已经不在集群中了。而 offline 会导致 region、leader 迁移到其它 tikv 节点。
- 当前问题
由于是单副本,即 tikv-3 的region 只有 tikv-3 有,其它 tikv 是没有的。由于 tikv-3无法正常启动,所以无法把 region 迁移出去,所以无法完成 offline,导致一直卡在这里、恢复正常。
-
解决问题
-
可以考虑想办法把 tikv-3 正常启动 —— 这个比较难,如果可以的话,也不会让其下线了。
-
只能丢失数据,放弃 tikv-3 上的 region。在其它 tikv 上补上这个 9个 region(空的)
- 查看 tikv-3 所有region,然后执行命令"tikv-ctl --db /path/to/tikv-data/db recreate-region --pd <endpoint> -r <region_id>"
- 可惜,执行这个操作之后,是没有效果的(报错)
-
只能重建集群了(还好是测试环境)
-
案例2
背景情况:
- 最后心跳时间是 14:30左右
- 当前状态是 Down
- region/leader count是0
- 查询时间大约在 21:30 +5:30左右
集群是 k8s 集群,在没有人为操作的情况下,在 23:30 +5:30 左右变成了 tombstone
理论上来说,需要执行 “delete”使其状态变成 offline,然后 region/leader count为0 则可以切换为 tombstone。
但是,在至少2个小时 Down中都没有变化,突然就变成了 tombstone。
- 不了解 k8s 的自动切换的原理 —— 即原来的 store 4 由于启动不了,所以替换为了 store xxxx,但是仍然是 tikv-1实例。
- 另外,怀疑是客户手动 delete 了 pod ?
案例3
由于 compaction 导致某个磁盘空间突然飞涨,导致无法启动、运行。
Workaround :磁盘扩容。
案例4
情况汇总: 初始情况:
- 客户1pd + 3tidb + 3tikv(tikv0<store1>/tikv1/tikv2);
第一阶段
- 由于pv 扩容, rollout 导致多了个 pod 运行 tikv0(store-n);现在 store1 和 store-n 同时存在;
- store-1 是有问题,本身启动不了,Down 了;
- 客户执行了 pd-ctl delete store-0 ,但是删除失败
- store-n 和 store-1 都用tikv-0 的地址,所以导致 store-n 注册pd 失败启动不了;
第二阶段:
- 由于 autofailover,导致自动扩容了 tikv-3;
- 但是由于资源不够导致 挂起;
- 客户有发现之后,就手动吧 tikv-3 scale-in 了;导致现在的 tikv-3 也是 down 状态
要恢复集群,就是不不正常的 store 剔除
、让正常的 store 启动起来
。
- 不正常的 store-1,是因为还有 region 残留,可以直接通过 remove peer 来达到清零的效果(有三副本,所以删除了系统自动会补副本)。
- 正常的 store-n 启动起来。原来无法启动,是因为 store-1 占用了 tikv0 的实例(address),导致无法启动。那么,store-1 正常剔除之后,自然就启动正常了。
MoreAndMore:k8s(operators)
一开始打算的时候,没想研究这个:因为太复杂,完全不懂。
但是,在分析案例的时候,发现这几个问题或多或少都跟 k8s 相关:如果是 OP 环境,很容易就知道怎么玩了,但是 k8s 环境就完全不知道。所以,k8s 就自然而然的要研究下。
Restart pod
在 OP 环境,如果 tidb 异常重启(比如 OOM),那么就是进程变化而已。那么,在 k8s 环境,如果 tidb OOM ,会发生什么呢?
本质上来说,Pod 也是一个进程。所以,发生重启的话,Pod变化了(进程 变了),但是其它都没变(磁盘、配置等),所以 tikv(store) 节点是都没有变化的。
Autofailover
Autofailover 本质上来说,就是 scale-out + scale-in ,即
- 异常 Pod 缩容
- 扩容一个新的正常 Pod 来代替
MoreAndMore:offline 迁移到底做了啥
Offline 状态下的迁移 region,主要做了如下事情:
- 先 transfer leader 到非 offline 的节点
- 然后 replica 一个 peer,
- 最后 remove offline 的那个 peer
即,这个过程是跟 offline 的 tikv 无关的,不需要对 offline 的 tikv 做交互。
- Transfer leader 是 raft 协议保证的
- Replica 是从 leader 来做的——但是 leader 不在 offline 的 tikv
- remove offline 的那个 peer,是删除 pd 中的记录就可以了
参考
ETCDCTL_API=3 .tiup/components/ctl/v6.5.3/etcdctl --endpoints http://10.2.8.4:32379 get --prefix=true /pd/