0
14
14
12
专栏/.../

TiDB多数派节点故障恢复指南

 这里介绍不了我  发表于  2024-05-29

1. 背景


no-alt

(1) 我们所使用的 TiDB 目前采用 3 副本的部署方式,其中一份数据同时存储在多个节点中。然而,如果一个 Region 的多数或全部副本在短时间内全部下线,该 Region 将无法进行读写操作,对业务造成严重影响。

(2)在实际生产过程中,我们也曾遇到同一台交换机发生灾难性故障,如机房断电等情况,其下的多个 TIKV 宕机,也会造成多副本或全部副本丢失,导致业务不可用的情况。

no-alt

(3)社区当前多数文章只针对某一个版本在该场景下进行了故障恢复。而在 V6.1.0 正式引入 Online Unsafe Recovery 功能,所以本文将整合 V6.1.0 版本及其之前和之后的两个版本在该场景下故障解决方案。

2. 原理介绍

对于 PD 来说,是如何获取某个 Region 的状态?

对于每个 Raft Group 的 Leader 和 PD 之间都存在心跳包,用于汇报这个 Region 的状态。心跳包中主要包括:

  • Leader 的位置
  • Followers 的位置
  • 掉线副本的个数
  • 数据写入/读取的速度

除此之外,PD 还会收集每个 TiKV 节点(Store)发送的心跳包。通过这两种心跳包得到的信息,PD 会通过一些策略来制定具体的调度计划。

而调度的基本操作大致分为:

  • 增加一个副本(addReplica)
  • 删除一个副本(RemoveReplica)
  • 将 Leader 角色在一个 Raft Group 的不同副本之间进行迁移(TransferLeader)

假设我们现在有若干个 Region 分布在 5 个 Store 上,并且每个 Region 有3个副本,我们进行实验,手动挂掉3个 Store 。若剩余副本数等于 2,PD 可以通过调度进行 Leader 的重新选举以及副本的补充。但是若只剩余 1 个副本,那么就需要人工介入进行恢复。

3. 实验模拟

首先,使用 tiup-bench 工具来准备一些测试数据:

tiup bench tpcc prepare --warehouses 4 --db xxx -H xxxx -P 4000 -pxxx

3.1 V6.1.0 以下版本(3副本丢失2副本)

集群 store 情况:

id   address
1 IP1
2 IP2
3 IP3
10 IP10
11 IP11

这里用 IP+store id 代替真实 IP 地址。

Region 分布情况:

表名 Region id Region leader Region分布 Region leader 所在地址
customer 34768 11 [11,10,2] IP11
customer 34780 1 [11,1,3] IP1
customer 34772 10 [10,2,1] IP10
customer 34776 11 [11,2,1] IP11
district 34756 10 [10,2,3] IP10
district 34764 1 [11,1,10] IP1
district 34752 10 [1,10,2] IP10
district 34760 1 [1,10,2] IP1
history 34792 10 [10,3,2] IP10
history 34799 1 [1,2,3] IP1
history 34784 1 [1,10,2] IP1
history 34788 2 [11,2,1] IP2
item 70 10 [11,10,2] IP10
new_order 34813 1 [1,10,2] IP1
new_order 34826 11 [11,3,1] IP11
new_order 34804 10 [10,3,1] IP10
new_order 34808 2 [2,3,1] IP2
order_line 34863 10 [10,3,11] IP10
order_line 34859 11 [11,10,3] IP11
order_line 34851 10 [10,3,1] IP10
order_line 34855 1 [11,1,10] IP1
orders 34839 1 [11,1,10] IP1
orders 34830 11 [11,3,1] IP11
orders 34834 2 [11,2,3] IP2
orders 34847 1 [1,3,11] IP1
stock 34867 1 [1,2,11] IP1
stock 34879 1 [1,2,11] IP1
stock 34871 1 [11,1,10] IP1
stock 34875 1 [1,10,11] IP1
warehouse 34748 10 [10,2,3] IP10
warehouse 34743 3 [3,1,11] IP3
warehouse 34733 3 [1,3,10] IP3
warehouse 34738 3 [3,1,10] IP3

基于上述 Region 分布情况,假设我们关闭了 store 1 和 store 11 ,那么以下表将无法访问:customer、district、history、new_order、order_line、orders、stock 和 warehouse。而 item 表不受影响。

实验过程:

(1)集群正常状态

no-alt

所有表均可访问

(2)在 store 1 和 11 上 mv data文件夹

移动后,store 1 和 11 两个 TiKV 节点状态变为 Disconnected。

no-alt

(3)停止 store 1 和 11 的服务

这里要将 store 1 和 11 TiKV 服务不可用才能无法访问上述表。否则就会出现还能访问的情况。

no-alt

TiKV 服务的配置文件一般在 /etc/systemd/system/tikv-端口号.service 中。

需要将 Restart 置为 no,因为 Restart=always: 只要不是通过 systemctl stop 来停止服务,任何情况下都必须要重启服务。

再将 TiKV 进程 KIll 掉。

(4)访问对应表

no-alt

no-alt

符合我们的预期,3 副本状态下挂掉 2 个副本,访问 item 表不受影响,访问其他表会报错 : Region is unavailable

(5)待修复 Region

核心原理在于使用单副本数据进行多副本补充,如下表所示:

Region id Region分布 可用Store
34867 [1,2,11] 2
34879 [1,2,11] 2
34788 [11,2,1] 2
34776 [11,2,1] 2
34780 [11,1,3] 3
34847 [1,3,11] 3
34743 [3,1,11] 3
34826 [11,3,1] 3
34830 [11,3,1] 3
34764 [11,1,10] 10
34855 [11,1,10] 10
34839 [11,1,10] 10
34871 [11,1,10] 10
34875 [1,10,11] 10

所以我们需要在正常的Store上对副本进行补充:

在 Store 2 上修复 Region 34867、34879、34788 和 34776。

在 Store 3 上修复 Region 34780、34847、34743、34826 和 34830。

在 Store 10 上修复 Region 34764、34855、34839、34871 和 34875。

(6)开始修复

停止 TiKV 角色

tiup cluster stop 集群名称 -R tikv

关闭 PD 调度

config set region-schedule-limit 0

config set replica-schedule-limit 0

config set leader-schedule-limit 0

config set merge-schedule-limit 0

修复 Region

这里使用 tikv-ctl 工具,需要到对应的 TiKV 节点去执行命令进行修复,如果是使用 TiUP 部署的集群,tikv-ctl 工具在 ~/.tiup/components/ctl/{VERSION}/ 目录下,拷贝到对应的TiKV节点(Store)上。

-s 是宕机的 TiKV 节点的 store id,-r 是我们要移除的 Region id(多个请以逗号分隔)。

Store 2:

tikv-ctl --data-dir /data/tidb/data unsafe-recover remove-fail-stores -s 1,11 -r 34867,34879,34788,34776

Store 3:

tikv-ctl --data-dir /data/tidb/data unsafe-recover remove-fail-stores -s 1,11 -r 34780,34847,34743,34826,34830

Store 10:

tikv-ctl --data-dir /data/tidb/data unsafe-recover remove-fail-stores -s 1,11 -r 34764,34855,34839,34871,34875

执行成功会显示

no-alt

(7)启动集群,校验数据

tiup cluster start 集群名 -R tikv

no-alt

这里注意,store 1 和 11 是无法启动的,因为数据目录已经被 mv 了。

no-alt

经过修复,上述表可以正常访问。

(8) 节点下线

最后可以将 store 4、7 对应的 TiKV 节点强制下线

tiup cluster scale-in cluster-name -N IP1:20260,IP11:20260 --force

tiup cluster prune cluster-name


3.2 V6.1.0 以上版本(3副本丢失2副本)

在 V6.1.0 版本后,推出了在线有损恢复 Online Unsafe Recovery,现在,我们将使用这种方法进行故障恢复。

集群 store 情况:

id address
1 IP1
2 IP2
3 IP3
10 IP10
11 IP11

同上,这里用 IP+store id 代替真实 IP 地址。

Region 分布情况:

表名 Region id Region leader Region分布 Region leader 所在地址
customer 355 10 [10,11,1] IP10
customer 367 10 [10,3,1] IP10
customer 363 3 [10,3,1] IP3
customer 359 2 [10,2,1] IP2
district 339 3 [10,3,1] IP3
district 343 10 [10,1,3] IP10
district 351 2 [10,2,3] IP2
district 347 10 [10,3,1] IP10
history 371 10 [10,3,11] IP10
history 383 10 [10,3,11] IP10
history 379 10 [10,3,11] IP10
history 375 10 [10,11,1] IP10
item 30 2 [10,2,3] IP2
new_order 387 2 [2,3,1] IP2
new_order 391 10 [10,3,1] IP10
new_order 395 10 [10,1,3] IP10
new_order 399 2 [10,2,3] IP2
order_line 427 2 [10,2,3] IP2
order_line 431 2 [2,1,11] IP2
order_line 423 2 [2,3,1] IP2
order_line 419 2 [10,2,1] IP2
orders 407 2 [10,2,1] IP2
orders 415 2 [2,11,10] IP2
orders 411 2 [10,2,1] IP2
orders 403 2 [10,2,1] IP2
stock 447 2 [10,2,11] IP2
stock 435 2 [2,3,10] IP2
stock 439 2 [10,2,3] IP2
stock 443 2 [2,3,1] IP2
warehouse 331 10 [10,3,11] IP10
warehouse 323 3 [10,3,1] IP3
warehouse 327 10 [10,3,1] IP10
warehouse 335 10 [10,1,3] IP10

基于上述 Region 分布情况,假设我们关闭了 store 2 和 store 3 ,那么以下表将无法访问:district、item、new_order、order_line 和 stock。而不受影响的表包括:customer、history、orders 和 warehouse。

实验过程:

(1)集群正常状态

no-alt

所有表均可访问

(2)在 store 2 和 3 上 mv data文件夹

no-alt


(3)访问表(在线有损恢复,不需要停止 store 2 和 3 的服务,这里要注意)

no-alt

(4)执行恢复命令

unsafe remove-failed-stores 2,3

执行成功如下:

no-alt


(5)查看恢复进度

unsafe remove-failed-stores show
[
  {
    "info": "Unsafe recovery enters collect report stage",
    "time": "2024-04-08 16:58:23.847",
    "details": [
      "Failed stores 2, 3"
    ]
  },
  {
    "info": "Unsafe recovery enters force leader stage",
    "time": "2024-04-08 16:58:24.157",
    "actions": {
      "store 1": [
        "force leader on regions: 70, 351, 411"
      ],
      "store 10": [
        "force leader on regions: 30"
      ],
      "store 11": [
        "force leader on regions: 435"
      ]
    }
  },
  {
    "info": "Unsafe recovery enters demote Failed voter stage",
    "time": "2024-04-08 16:58:44.664",
    "actions": {
      "store 1": [
        "region 70 demotes peers { id:263 store_id:2 }, { id:296 store_id:3 }",
        "region 351 demotes peers { id:353 store_id:2 }, { id:675 store_id:3 }",
        "region 411 demotes peers { id:413 store_id:2 }, { id:680 store_id:3 }"
      ],
      "store 10": [
        "region 30 demotes peers { id:264 store_id:2 }, { id:622 store_id:3 }"
      ],
      "store 11": [
        "region 435 demotes peers { id:437 store_id:2 }, { id:438 store_id:3 }"
      ]
    }
  },
  {
    "info": "Unsafe recovery Finished",
    "time": "2024-04-08 16:58:55.671",
    "details": [
      "affected table ids: 40, 114, 137, 147, 152",
      "no newly created empty regions"
    ]
  }
]

等待最后输出 "info": "Unsafe recovery Finished" ,即为恢复成功。

no-alt

经查询,上述表可以进行访问。

(6) 检查数据索引一致性

即使表进行正常读写,但不代表数据没有丢失。

"affected table ids: 40, 114, 137, 147, 152",恢复完受影响的表id,我们根据表 id 进行查询:

SELECT TABLE_SCHEMA, TABLE_NAME, TIDB_TABLE_ID FROM INFORMATION_SCHEMA.TABLES WHERE TIDB_TABLE_ID IN (40, 114, 137, 147, 152);

现在我们上述进行检查:

admin check table 表名;

若有不一致的索引,通过以下步骤进行修复:

  • 重命名索引:alter table 表名 rename 索引名 to 临时索引名
  • 创建新索引:alter table 表名 add index 索引名(列名)
  • 删除旧索引:alter table 表名 drop index 临时索引名

(7) 宕机的节点下线

tiup cluster scale-in cluster-name -N IP2:20260,IP3:20260 --force

tiup cluster prune cluster-name


3.3 V6.1.0以上版本 (3副本全部丢失)

经过在线有损恢复后,使用的感受非常良好。现在我们思考,如果3个副本全部丢失,是否可以使用这种方式来恢复我们的数据?

接下来一起测试下。

老方法,我们首先使用 tiup-bench prepare 数据:

no-alt

集群 store 情况:

id address
4 IP2
1 IP1
2 IP7
10 IP8
11 IP9

Region 分布情况:

表名 Region id Region leader Region分布 Region leader 所在地址
orders 400 1 [1, 10, 11] IP1
order_line 412 1 [1, 10, 11] IP1
stock 430 1 [1, 2, 10] IP1
customer 348 2 [2, 10, 4] IP2
item 22 4 [4, 10, 2] IP4
warehouse 314 4 [1, 4, 2] IP4
history 361 4 [4, 11, 10] IP4
new_order 369 4 [4, 10, 11] IP4
new_order 377 4 [4, 10, 11] IP4
orders 385 4 [4, 11, 10] IP4
order_line 404 4 [4, 2, 1] IP4
order_line 418 4 [4, 10, 11] IP4
stock 422 4 [4, 11, 2] IP4
stock 426 4 [1, 4, 2] IP4
stock 434 4 [4, 10, 2] IP4
warehouse 302 10 [10, 2, 4] IP10
warehouse 306 10 [1, 10, 4] IP10
warehouse 310 10 [10, 11, 4] IP10
district 318 10 [10, 2, 4] IP10
district 322 10 [10, 2, 11] IP10
district 326 10 [10, 2, 11] IP10
district 330 10 [10, 11, 4] IP10
customer 334 10 [1, 10, 11] IP10
customer 338 10 [10, 2, 11] IP10
customer 342 10 [10, 11, 4] IP10
history 352 10 [10, 11, 2] IP10
history 356 10 [10, 11, 1] IP10
history 365 10 [10, 11, 2] IP10
new_order 373 10 [10, 11, 2] IP10
new_order 381 10 [1, 10, 2] IP10
orders 389 10 [10, 2, 11] IP10
orders 393 10 [10, 11, 1] IP10

现在我们 mv 4,10,11 对应TiKV的 data文件夹,并打开防火墙 (关闭和 pd 的通信) 以及中控机 tiup 的 ssh 端口模拟宕机。

#!/bin/bash

mv /data_dir /data_dir_delete

iptables -A OUTPUT -d pd_host1  -j DROP # (pd)
iptables -A OUTPUT -d pd_host2  -j DROP # (pd)
iptables -A OUTPUT -d pd_host3  -j DROP # (pd)
iptables -A OUTPUT -d tiup_host -j DROP # (tiup)
iptables -A INPUT -s pd_host1   -j DROP # (pd)
iptables -A INPUT -s pd_host2   -j DROP # (pd)
iptables -A INPUT -s pd_host3   -j DROP # (pd)
iptables -A INPUT -s tiup_host  -j DROP # (tiup)

之后观察报错情况:

  • leader 和 region 节点监控消失,store size 为丢失了 3 副本

no-alt

no-alt

  • 业务异常,日志报错: “9005: Region is unavailable”

no-alt

  • server report failure 出现错误报告

no-alt


下面就到了激动人心的恢复时间了:

unsafe remove-faileds-stores 4,10,11

no-alt

unsafe remove-faileds-stores show

最后一段输出信息:

 {
    "info": "Unsafe recovery Finished",
    "time": "2024-03-27 10:29:41.471",
    "details": [
      "affected table ids: 147, 152, 119, 10, 26, 36, 123, 125, 135, 14, 62, 96, 138, 88, 50, 120, 126, 46, 64, 105, 106, 130, 107, 48, 117, 118, 281474976710649, 40, 42, 76, 74, 24, 54, 68, 28, 129, 86, 124, 136, 144, 20, 82, 114, 113, 131, 16, 66, 90, 4, 112, 281474976710653, 143, 150, 142, 22, 56, 94, 111, 137, 6, 60, 92, 100, 34, 70, 281474976710652",
      "newly created empty regions: 568, 562, 564, 572, 582, 560, 574, 576, 578, 566, 570, 580, 584"
    ]
  }

查询结果:

no-alt

结果是,在三副本全部丢失的情况下,恢复成功了,这就比较令我感到诧异,随后团队将这个现象反应给了官方,也期待之后进一步完善。

4. 总结及思考

一般情况下,当 TiDB 集群发生故障后,只需进行重启即可完成多数派的选举,使集群能够正常对外提供服务。

然而,在极端情况下,例如 SST 文件损坏,就需要使用有损恢复的方式进行数据恢复。在这种情况下,恢复方案需要经过反复验证,以确保在线上的极端情况下能够成功完成数据恢复。

本文主要以 V6.1.0 为分界,针对 V6.1.0 版本及其之前和之后的两个版本,在 3 副本状态下多副本丢失的场景下进行故障模拟,并分别使用 tikv-ctl 手动恢复和在线有损恢复两种方式进行数据恢复。

值得注意的是,V6.1.0 之前的版本在线有损恢复功能为实验特性,因此对于小于 V6.1.0 的集群,我们可以考虑使用 tikv-ctl 手动恢复;而对于版本大于 V6.1.0 的集群,我们则可以考虑使用在线有损恢复方式。

同时,如果能够将恢复过程自动化,将极大地提高业务的稳定性。

0
14
14
12

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

评论
暂无评论