写点什么

一篇文章带你玩转 TiDB 灾难恢复

  • 2022 年 7 月 11 日
  • 本文字数:6946 字

    阅读完需:约 23 分钟

作者: dbapower 原文来源:https://tidb.net/blog/8da47c81


一篇文章带你玩转 TiDB 灾难恢复

一、背景

高可用是 TiDB 的另一大特点,TiDB/TiKV/PD 这三个组件都能容忍部分实例失效,不影响整个集群的可用性。下面分别说明这三个组件的可用性、单个实例失效后的后果以及如何恢复。

TiDB

TiDB 是无状态的,推荐至少部署两个实例,前端通过负载均衡组件对外提供服务。当单个实例失效时,会影响正在这个实例上进行的 Session,从应用的角度看,会出现单次请求失败的情况,重新连接后即可继续获得服务。单个实例失效后,可以重启这个实例或者部署一个新的实例。

PD

PD 是一个集群,通过 Raft 协议保持数据的一致性,单个实例失效时,如果这个实例不是 Raft 的 leader,那么服务完全不受影响;如果这个实例是 Raft 的 leader,会重新选出新的 Raft leader,自动恢复服务。PD 在选举的过程中无法对外提供服务,这个时间大约是 3 秒钟。推荐至少部署三个 PD 实例,单个实例失效后,重启这个实例或者添加新的实例。

TiKV

TiKV 是一个集群,通过 Raft 协议保持数据的一致性(副本数量可配置,默认保存三副本),并通过 PD 做负载均衡调度。单个节点失效时,会影响这个节点上存储的所有 Region。对于 Region 中的 Leader 节点,会中断服务,等待重新选举;对于 Region 中的 Follower 节点,不会影响服务。当某个 TiKV 节点失效,并且在一段时间内(默认 30 分钟)无法恢复,PD 会将其上的数据迁移到其他的 TiKV 节点上。

二、架构

wtidb28.add.shbt.qihoo.net  192.168.1.1  TiDB/PD/pump/prometheus/grafana/CCSwtidb27.add.shbt.qihoo.net  192.168.1.2  TiDBwtidb26.add.shbt.qihoo.net  192.168.1.3  TiDBwtidb22.add.shbt.qihoo.net  192.168.1.4  TiKVwtidb21.add.shbt.qihoo.net  192.168.1.5  TiKVwtidb20.add.shbt.qihoo.net  192.168.1.6  TiKVwtidb19.add.shbt.qihoo.net  192.168.1.7  TiKVwtidb18.add.shbt.qihoo.net  192.168.1.8  TiKVwtidb17.add.shbt.qihoo.net  192.168.1.9  TiFlashwtidb16.add.shbt.qihoo.net  192.168.1.10  TiFlash
复制代码


集群采用 3TiDB 节点,5TiKV,2TiFlash 架构来测试灾难恢复,TiFlash 采用的方式是先部署集群,后部署 TiFlash 的方式,版本 3.1.0GA


在恢复之前,首先应该通过 pd-ctl 调整 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
复制代码


这样可以将恢复过程中可能的异常情况降到最少。下面将按照出现掉电故障的节点个数及


TiKV 配置副本数来分别分析,并给出解决方案

三、宕机两台测试

集群默认 3 副本,5 台机器宕机任意两台,理论上存在三种情况,一种是 3 副本中,有两个副本正巧在宕机的这两台上,一种是 3 副本中,只有一个 region 在宕机的两台机器上,还一种就是宕机的两台机器里不存在某些内容的任何副本,本次我们测试让 wtidb21 和 wtidb22 两个 TiKV 节点宕机。


我们先看一下宕机前测试表的状况


mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily;+----------+| count(*) |+----------+|  1653394 |+----------+1 row in set (0.91 sec)
mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily force index (idx_day_ver_ch);+----------+| count(*) |+----------+| 1653394 |+----------+1 row in set (0.98 sec)
复制代码


两台同时宕机后:


mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily;               ERROR 9002 (HY000): TiKV server timeout mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily force index (idx_day_ver_ch);ERROR 9005 (HY000): Region is unavailable
复制代码


看一下宕机的两台 store_id


/data1/tidb-ansible-3.1.0/resources/bin/pd-ctl -i -u http://192.168.1.1:2379» store
复制代码


知道是 1 和 4


检查大于等于一半副本数在故障节点上的 region


[tidb@wtidb28 bin]$ /data1/tidb-ansible-3.1.0/resources/bin/pd-ctl  -u http://192.168.1.1:2379  -d region --jq='.regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as $total | map(if .==(1,4) then . else empty end) | length>=$total-length)}'{"id":18,"peer_stores":[4,6,1]}{"id":405,"peer_stores":[7,4,1]}{"id":120,"peer_stores":[4,1,6]}{"id":337,"peer_stores":[4,5,1]}{"id":128,"peer_stores":[4,1,6]}{"id":112,"peer_stores":[1,4,6]}{"id":22,"peer_stores":[4,6,1]}{"id":222,"peer_stores":[7,4,1]}{"id":571,"peer_stores":[4,6,1]}
复制代码


在剩余正常的 kv 节点上执行停 kv 的脚本:


ps -ef|grep tikvsh /data1/tidb/deploy/scripts/stop_tikv.shps -ef|grep tikv
复制代码


变更其属主,将其拷贝至 tidb 目录下


chown -R tidb. /home/helei/tikv-ctl


这个分情况, 如果是 region 数量太多,那么按照 region 来修复的话,速度会比较慢,并且繁琐,这个使用使用 all 操作比较便捷,但是有可能误杀有两个 peer 的副本,也就是说可能你坏的这台机器,有个 region 只有一个在这台机器上,但他也会只保留一个 region 副本在集群里


下面的操作要在所有存活的节点先执行 stop kv 操作(要求 kv 是关闭状态),然后执行


[tidb@wtidb20 tidb]$ ./tikv-ctl --db /data1/tidb/deploy/data/db unsafe-recover remove-fail-stores -s 1,4 --all-regions                                                                    removing stores [1, 4] from configrations...success
复制代码


重启 pd 节点


ansible-playbook stop.yml --tags=pd这里如果pd都关了的话,你是登不上库的[helei@db-admin01 ~]$ /usr/local/mysql56/bin/mysql -u xxxx -h xxxx -P xxxx -pxxxxxxxxx ansible-playbook start.yml --tags=pd
复制代码


重启存活的 kv 节点


sh /data1/tidb/deploy/scripts/start_tikv.sh


检查没有处于 leader 状态的 region


[tidb@wtidb28 bin]$ /data1/tidb-ansible-3.1.0/resources/bin/pd-ctl -u http://192.168.1.1:2379 -d region --jq '.regions[]|select(has("leader")|not)|{id: .id,peer_stores: [.peers[].store_id]}'


这里我没有搜到任何的非 leader region,只有副本数是 3,且同时挂 3 台机器以上,且正巧有些 region 全部的 region 都在这 3 台机器上,前面步骤是 unsafe all-region,pd 这个检查没有处于 leader 状态的 region 步骤才会显示出来,才会需要对应到表查询丢了那些数据,才需要去创建空 region 啥的,我这个情况,只要还保留一个副本,不管 unsafe 执行的是 all-regions,还是指定的具体的 region 号,都是不需要后面的步骤


正常启动集群后,可以通过 pd-ctl 来观看之前的 region 数,理论上在使用 unsafe –all-regions 后,仅剩的 1 个 region 成为 leader,剩余的 kv 节点通过 raft 协议将其再次复制出 2 个 follower 拷贝到其他 store 上


例如本案例里的


{"id":18,"peer_stores":[4,6,1]}


通过 pd-ctl 可以看到他现在在犹豫 1,4kv 节点损坏,在执行 unsafe-recover remove-fail-stores –all-regions 后,将 1,4 的移除,仅剩的 6 成为 leader,利用 raft 协议在 5,7 节点复制出新的 follower,达成 3 副本顺利启动集群


» region 18{  "id": 18,  "start_key": "7480000000000000FF0700000000000000F8",  "end_key": "7480000000000000FF0900000000000000F8",  "epoch": {    "conf_ver": 60,    "version": 4  },  "peers": [    {      "id": 717,      "store_id": 6    },    {      "id": 59803,      "store_id": 7    },    {      "id": 62001,      "store_id": 5    }  ],  "leader": {    "id": 717,    "store_id": 6  },  "written_bytes": 0,  "read_bytes": 0,  "written_keys": 0,  "read_keys": 0,  "approximate_size": 1,  "approximate_keys": 0}
复制代码


如果只同时挂了 2 台机器,那么到这里就结束了,如果只挂 1 台那么不用处理的


先看一下数据现在是没问题的,之前的步骤恢复的很顺利


mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily;+----------+| count(*) |+----------+|  1653394 |+----------+1 row in set (0.86 sec)
mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily force index (idx_day_ver_ch);+----------+| count(*) |+----------+| 1653394 |+----------+1 row in set (0.98 sec)
复制代码


这里有个插曲


当我把 1,4 宕掉的节点恢复,这期间集群一直没有新的数据写入,原本是 6 作为 leader,新生成的 5,7 作为 follower 作为副本,而恢复后,将 5,7 剔除,重新将 1,4 作为 follower 了,region 18 还是 1,4,6 的 store_id。

四、宕机 3 台测试

如果同时挂了 3 台及以上,那么上面的非 leader 步骤检查是会有内容的


我们这次让如下三台宕机:


wtidb22.add.shbt.qihoo.net  192.168.1.4  TiKVwtidb21.add.shbt.qihoo.net  192.168.1.5  TiKVwtidb20.add.shbt.qihoo.net  192.168.1.6  TiKV
复制代码


首先,停止所有正常的 tikv,本案例是 wtidb19,wtidb18


看一下宕机的两台 store_id


/data1/tidb-ansible-3.1.0/resources/bin/pd-ctl -i -u http://192.168.1.1:2379» store
复制代码


知道是 1、4、5


检查大于等于一半副本数在故障节点上的 region


[tidb@wtidb28 bin]$  /data1/tidb-ansible-3.1.0/resources/bin/pd-ctl  -u http://192.168.1.1:2379  -d region --jq='.regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as $total | map(if .==(1,4,5) then . else empty end) | length>=$total-length)}'{"id":156,"peer_stores":[1,4,6]}{"id":14,"peer_stores":[6,1,4]}{"id":89,"peer_stores":[5,4,1]}{"id":144,"peer_stores":[1,4,6]}{"id":148,"peer_stores":[6,1,4]}{"id":152,"peer_stores":[7,1,4]}{"id":260,"peer_stores":[6,1,4]}{"id":480,"peer_stores":[7,1,4]}{"id":132,"peer_stores":[5,4,6]}{"id":22,"peer_stores":[6,1,4]}{"id":27,"peer_stores":[4,1,6]}{"id":37,"peer_stores":[1,4,6]}{"id":42,"peer_stores":[5,4,6]}{"id":77,"peer_stores":[5,4,6]}{"id":116,"peer_stores":[5,4,6]}{"id":222,"peer_stores":[6,1,4]}{"id":69,"peer_stores":[5,4,6]}{"id":73,"peer_stores":[7,4,1]}{"id":81,"peer_stores":[5,4,1]}{"id":128,"peer_stores":[6,1,4]}{"id":2,"peer_stores":[5,6,4]}{"id":10,"peer_stores":[7,4,1]}{"id":18,"peer_stores":[6,1,4]}{"id":571,"peer_stores":[6,5,4]}{"id":618,"peer_stores":[7,1,4]}{"id":218,"peer_stores":[6,5,1]}{"id":47,"peer_stores":[1,4,6]}{"id":52,"peer_stores":[6,1,4]}{"id":57,"peer_stores":[4,7,1]}{"id":120,"peer_stores":[6,1,4]}{"id":179,"peer_stores":[5,1,4]}{"id":460,"peer_stores":[5,7,1]}{"id":93,"peer_stores":[6,1,4]}{"id":112,"peer_stores":[6,1,4]}{"id":337,"peer_stores":[5,6,4]}{"id":400,"peer_stores":[5,7,1]}
复制代码


现在还剩两台存活


wtidb19.add.shbt.qihoo.net  192.168.1.7  TiKVwtidb18.add.shbt.qihoo.net  192.168.1.8  TiKV
复制代码


下面的操作要在所有存活的节 (本案例是 wtidb19 和 wtidb18) 点先执行 stop kv 操作(要求 kv 是关闭状态),然后执行


[tidb@wtidb19 tidb]$ ./tikv-ctl --db /data1/tidb/deploy/data/db unsafe-recover remove-fail-stores -s 1,4,5 --all-regions                                                                 removing stores [1, 4, 5] from configrations...success
复制代码


重启 pd 节点


ansible-playbook stop.yml --tags=pdansible-playbook start.yml --tags=pd
复制代码


重启存活的 kv 节点


sh /data1/tidb/deploy/scripts/start_tikv.sh


检查没有处于 leader 状态的 region,这里看到,1,4,5 因为所有的 region 都在损坏的 3 台机器上,这些 region 丢弃后数据是恢复不了的


[tidb@wtidb28 tidb-ansible-3.1.0]$ /data1/tidb-ansible-3.1.0/resources/bin/pd-ctl  -u http://192.168.1.1:2379 -d region --jq '.regions[]|select(has("leader")|not)|{id: .id,peer_stores: [.peers[].store_id]}'{"id":179,"peer_stores":[5,1,4]}{"id":81,"peer_stores":[5,4,1]}{"id":89,"peer_stores":[5,4,1]}
复制代码


根据 region ID,确认 region 属于哪张表


[tidb@wtidb28 tidb-ansible-3.1.0]$ curl http://192.168.1.1:10080/regions/179{ "region_id": 179, "start_key": "dIAAAAAAAAA7X2mAAAAAAAAAAwOAAAAAATQXJwE4LjQuMAAAAPwBYWxsAAAAAAD6A4AAAAAAAqs0", "end_key": "dIAAAAAAAAA7X3KAAAAAAAODBA==", "frames": [  {   "db_name": "hl",   "table_name": "rpt_qdas_show_shoujizhushou_channelver_mix_daily(p201910)",   "table_id": 59,   "is_record": false,   "index_name": "key2",   "index_id": 3,   "index_values": [    "20191015",    "8.4.0",    "all",    "174900"   ]  },  {   "db_name": "hl",   "table_name": "rpt_qdas_show_shoujizhushou_channelver_mix_daily(p201910)",   "table_id": 59,   "is_record": true,   "record_id": 230148  } ]}
复制代码


这时候去看集群状态的话,


» store{  "count": 5,  "stores": [    {      "store": {        "id": 1,        "address": "192.168.1.4:20160",        "version": "3.1.0",        "state_name": "Down"      },      "status": {        "leader_weight": 1,        "region_count": 3,        "region_weight": 1,        "start_ts": "1970-01-01T08:00:00+08:00"      }    },    {      "store": {        "id": 4,        "address": "192.168.1.5:20160",        "version": "3.1.0",        "state_name": "Down"      },      "status": {        "leader_weight": 1,        "region_count": 3,        "region_weight": 1,        "start_ts": "1970-01-01T08:00:00+08:00"      }    },    {      "store": {        "id": 5,        "address": "192.168.1.6:20160",        "version": "3.1.0",        "state_name": "Down"
复制代码


监控也是没数据


库里查询也依旧被阻塞


mysql> use hlReading table information for completion of table and column namesYou can turn off this feature to get a quicker startup with -A
Database changedmysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily;
复制代码


创建空 region 解决 unavaliable 状态,这个命令要求 pd,kv 处于关闭状态


这里必须一个一个 -r 的写,要不报错:


[tidb@wtidb19 tidb]$ ./tikv-ctl --db /data1/tidb/deploy/data/db recreate-region -p 192.168.1.1:2379 -r 89,179,81thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:1188:5note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.Aborted
./tikv-ctl --db /data1/tidb/deploy/data/db recreate-region -p '192.168.1.1:2379' -r 89./tikv-ctl --db /data1/tidb/deploy/data/db recreate-region -p '192.168.1.1:2379' -r 81./tikv-ctl --db /data1/tidb/deploy/data/db recreate-region -p '192.168.1.1:2379' -r 179
复制代码


启动 pd 和 tikv 后,再次运行


[tidb@wtidb28 tidb-ansible-3.1.0]$ /data1/tidb-ansible-3.1.0/resources/bin/pd-ctl -u http://192.168.1.1:2379 -d region --jq '.regions[]|select(has("leader")|not)|{id: .id,peer_stores: [.peers[].store_id]}'


没有任何结果则符合预期


这里再次查询可以看到丢了数据,因为我们有几个 region(81,89,179)都丢失了


mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily;+----------+| count(*) |+----------+|  1262523 |+----------+1 row in set (0.92 sec)
复制代码


这里可以看到索引数据不再 region(81,89,179)中,所以还跟之前一样


mysql> select count(*) from rpt_qdas_show_shoujizhushou_channelver_mix_daily force index (idx_day_ver_ch);+----------+| count(*) |+----------+|  1653394 |+----------+1 row in set (1.01 sec)
复制代码


至此,测试完成


别忘了恢复调度:


恢复操作之后


将 PD 的调度参数还原

五、总结

看完这篇文章,相信你不会再虚 TiDB 的多点掉电问题的数据恢复了,正常情况下,极少数出现集群同时宕机多台机器的,如果只宕机了一台,那么并不影响集群的运行,他会自动处理,当某个 TiKV 节点失效,并且在一段时间内(默认 30 分钟)无法恢复,PD 会将其上的数据迁移到其他的 TiKV 节点上。但如果同时宕机两台,甚至 3 台及以上,那么看过这篇文章的你相信你一定不会再手忙脚乱不知所措了!~


发布于: 刚刚阅读数: 3
用户头像

TiDB 社区官网:https://tidb.net/ 2021.12.15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
一篇文章带你玩转 TiDB 灾难恢复_故障排查/诊断_TiDB 社区干货传送门_InfoQ写作社区