引入数学概率问题:之前上学时都学过,把几组小球放几个盒子,然后计算概率的问题,那么我有10组小球(每组3个),放5个盒子里(每个盒子不能空着),会有多大的概率在2个盒子损坏的情况下,保证每组小球至少保留1个?
Tikv可用性图
下面对上图简单解释下:
(1)以上都是在region默认3副本的情况下的讨论。
(2) 以上的可用性都是在大量region的情况下,无论有多少个tikv节点,一个region的3个副本肯定会调度到KV集群的3个tikv上,3个副本的多数副本不可用,该region就不可用了,大量region的情况下,有很大的概率同一个raft group的2个peer正好调度在宕机的2个tikv上,这也是为啥文章开头说概率的意义。
(3)集群可用是指:就算DBA不介入,整个集群也会正常提供服务。比如拿5个节点的tikv cluster来讲,每次只能宕机1台,tikv宕机后,在该tikv上的leader region会根据raft-group来找follwer region来提升为新leader,并且等30分钟后其他tikv节点补副本,最终完成3个副本的raft-group;另外对于该tikv节点上的follwer region,在其他tikv节点的leader region也会在另外的tikv节点补follwer region节点(3个节点的tikv集群在宕机1台的情况下,没有多余的kv节点可以补副本,这时需要扩容tikv来补)。
(4)不丢数据是指:在3副本的情况下,如果多数副本(2个副本)不可用的情况下,但是还保留着一份副本(数据没有丢),SQL读写的表现就是:该region就不可用。
所以聊了3副本的可用性问题后,咱们就通过5个tikv节点的宕机测试来验证可用性以及数据恢复方案。
实验模拟5节点集群宕机可用性
测试环境:
使用sysbench导入10张50万数据的table,然后sysbench读流量模拟请求。
sysbench /usr/share/sysbench/oltp_read_write.lua --mysql-host=10.xxxx.160 --mysql-port=4000 --mysql-db=test --mysql-user=root --mysql-password='xxx' --table_size=500000 --tables=10 --threads=30 --time=220 --report-interval=10 --db-driver=mysql prepare
sysbench /usr/share/sysbench/oltp_read_only.lua --mysql-host=10.xxxx.160 --mysql-port=4000 --mysql-db=test --mysql-user=root --mysql-password='xxx' --table_size=500000 --tables=10 --threads=30 --time=2000 --report-interval=10 --db-driver=mysql run
宕机1台
一般宕机1台不用太担心,因为如上所说,region的多数副本存活,该重新选leader的重新选,该补follwer副本的也会在其他节点补充。
在宕机1台,leader还没有选举成功或者follwer副本还没有补充完毕时,又宕机1台,其实跟下面要讲的同时宕机2台的问题分析和处理方式一样。
通过sysbench的压测任务可以看出,上图中红框位置出现20s左右的QPS抖动(QPS由之前的平均1.2万降低到4千左右),因为SQL正在访问的leader region节点发生故障,导致raft重新选举新leader后恢复正常,下面是tiup pd ctl命令来查看宕机tikv的store信息。
tiup ctl:v5.1.1 pd -u http://10.xxxxx.173:2379 store|grep -B 10 'Disconnected'
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v5.1.1/ctl /home/tidb/.tiup/components/ctl/v5.1.1/ctl pd -u http://10.xxxxx.173:2379 store
{
"store": {
"id": 5,
"address": "10.xxxx.155:20160",
"version": "5.1.1",
"status_address": "10.xxxx.155:20180",
"git_hash": "4705d7c6e9c42d129d3309e05911ec6b08a25a38",
"start_timestamp": 1628479214,
"deploy_path": "/data6/deploy/tikv-20180/bin",
"last_heartbeat": 1646640368489894705,
"state_name": "Disconnected"
通过以上可以看到tikv状态为disconnected,在这里简单提下tikv的状态,正常启动就是UP状态,当tikv节点跟PD断开超过20s后转变为Disconnected状态,默认超过30分钟(max-store-down-time设定)后tikv转变为down状态,只有变为down状态,其他存活的tikv才会补该tikv节点的region副本。对于down的tikv我们scale-in后出现offline状态,一旦所有的region都正常后最终变为tombstone状态。
同时宕机2台
我们下面通过 rm -rf tikv-data 来暴力模拟故障,同时删除 2 个 tikv 的 data 来模拟5个tikv集群2个tikv宕机处理,看下集群当前情况:
再看下sysbench压测请求的QPS情况,从打印的日志明显看到有 tikv server timeout 和 region is unavailable 的报错,sysbench报错后续的QPS已经变0。
下面开始进行tikv集群的恢复,操作步骤如下:
(1)使用tiup ctl pd(同之前的pd-ctl命令)来查看下 Tikv 哪些store id不可用了,发现是store 1和6。
tiup ctl:v5.1.1 pd -u http://xxxx:2379 store|grep -B 10 'Disconnected'
{
"store": {
"id": 6,
"address": "10.xxxx.201:20160",
"version": "5.1.1",
"state_name": "Disconnected"
--
{
"store": {
"id": 1,
"address": "10.xxxx.218:20160",
"version": "5.1.1",
"state_name": "Disconnected"
(2)PD调度关闭,避免恢复过程中产生的各种异常情况,对了,在将下面的参数调整为0之前,建议先tiup ctl pd config show看下之前的参数值为多少,另外使用tiup ctl时需要选择跟tidb集群版本一致的ctl version。
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set region-schedule-limit 0
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set replica-schedule-limit 0
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set leader-schedule-limit 0
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set merge-schedule-limit 0
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 operator show
[]
通过上面的命令可以看到调度已经关闭。
(3)停止其他UP状态的tikv,目的避免新的写入导致的region副本之间的元信息不一致,另外就是释放文件锁。
tiup cluster stop BA-xxxx_bak -R tikv
如果上面命令停不掉,可以登录到tikv节点使用systemctl stop tikv-20160停掉tikv
(4)在刚才关闭的每个tikv节点,使用tikv-ctl强制region从多副本失效的状态恢复,unsafe-recover remove-fail-stores 命令可以将故障机器从指定 Region 的 peer 列表中移除。运行命令之前,需要目标 TiKV 先停掉服务以便释放文件锁(否则执行tikv-ctl恢复时有下面的报错)。
[2022/03/14 17:51:54.215 +08:00] [ERROR] [main.rs:78] ["error while open kvdb: Storage Engine IO error: While lock file: /data6/tikv-20180/db/LOCK: Resource temporarily unavailable"]
[2022/03/14 17:51:54.215 +08:00] [ERROR] [main.rs:81] ["LOCK file conflict indicates TiKV process is running. Do NOT delete the LOCK file and force the command to run. Doing so could cause data corruption."]
tikv-ctl的-s 选项是指宕机的多个以逗号分隔的 store_id (本次实验为1和6),可以使用 -r 后面跟多个逗号分隔的Region id来指定要移除的peer,如果集群过大,需要移除的region id太多了,可简单指定 --all-regions 来对存活 store 上的全部 Region 都执行这个操作。
将跟集群版本适配的tikv-ctl拷贝到存活tikv节点
scp /home/tidb/.tiup/components/ctl/v5.1.1/tikv-ctl tidb@10.xxxx.155:/home/tidb
scp /home/tidb/.tiup/components/ctl/v5.1.1/tikv-ctl tidb@10.xxxx.208:/home/tidb
scp /home/tidb/.tiup/components/ctl/v5.1.1/tikv-ctl tidb@10.xxxx.238:/home/tidb
在每个存活tikv节点都执行下面的tikv-ctl命令(注意要在tikv stop的情况下)
$ ./tikv-ctl --data-dir /data6/tikv-20180 unsafe-recover remove-fail-stores -s 6,1 --all-regions
[2022/03/10 16:20:40.987 +08:00] [INFO] [mod.rs:118] ["encryption: none of key dictionary and file dictionary are found."]
[2022/03/10 16:20:40.987 +08:00] [INFO] [mod.rs:479] ["encryption is disabled."]
[2022/03/10 16:20:41.032 +08:00] [WARN] [config.rs:581] ["compaction guard is disabled due to region info provider not available"]
[2022/03/10 16:20:41.032 +08:00] [WARN] [config.rs:675] ["compaction guard is disabled due to region info provider not available"]
removing stores [6, 1] from configurations...
[2022/03/10 16:20:41.236 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1722 store_id: 5]"] [old_peers="[id: 1722 store_id: 5, id: 2111 store_id: 6, id: 2117 store_id: 1]"] [region_id=18]
[2022/03/10 16:20:41.236 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1982 store_id: 5]"] [old_peers="[id: 1944 store_id: 1, id: 1982 store_id: 5, id: 2119 store_id: 6]"] [region_id=26]
[2022/03/10 16:20:41.236 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1858 store_id: 5]"] [old_peers="[id: 1858 store_id: 5, id: 2108 store_id: 6, id: 2113 store_id: 1]"] [region_id=38]
[2022/03/10 16:20:41.236 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1717 store_id: 5]"] [old_peers="[id: 1717 store_id: 5, id: 2110 store_id: 6, id: 2115 store_id: 1]"] [region_id=46]
[2022/03/10 16:20:41.236 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1859 store_id: 5]"] [old_peers="[id: 1859 store_id: 5, id: 2112 store_id: 6, id: 2114 store_id: 1]"] [region_id=1009]
.....此处省略N行
[2022/03/10 16:20:41.237 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1880 store_id: 7, id: 1881 store_id: 5]"] [old_peers="[id: 1880 store_id: 7, id: 1881 store_id: 5, id: 1887 store_id: 1]"] [region_id=1879]
[2022/03/10 16:20:41.237 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 1884 store_id: 7, id: 1886 store_id: 5, id: 2107 store_id: 4]"] [old_peers="[id: 1884 store_id: 7, id: 1886 store_id: 5, id: 2107 store_id: 4]"] [region_id=1883]
success
注意:--all-regions是需要在所有store节点上执行的。另外就是使用了remove-fail-store参数后,已经被移除的节点(故障tikv节点)一定不能再加入集群,否则会导致PD元信息不一致。
(5)恢复pd调度配置
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set region-schedule-limit 2048
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set replica-schedule-limit 64
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set leader-schedule-limit 4
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 config set merge-schedule-limit 8
(6) 启动tikv集群,我这里是通过-R指定所有tikv重启,其实建议-N 指定之前状态正常的3个tikv节点启动。
tiup cluster start BA-analyse-tidb_shyc_bak -R tikv
启动过程出现报错,因为无论是我模拟的tikv data目录被删,亦或是硬盘故障,主机宕机等,还是之前的store id为1和6的tikv启动失败,通过tiup cluster display 查看还是有3个tikv节点启动成功的,不影响业务正常使用,通过继续跑sysbench和count全表,没有出现读写和表数据丢失问题。下图是随便找了2张table查看数据量也是50w,跟之前sysbench导入的数据量一致。
mysql> select count(1) from sbtest10;
+----------+
| count(1) |
+----------+
| 500000 |
+----------+
1 row in set (0.41 sec)
mysql> select count(1) from sbtest3;
+----------+
| count(1) |
+----------+
| 500000 |
+----------+
1 row in set (0.41 sec)
集群现状:
(7)后续处理:本次的事故模拟是通过rm -rf tikv-data目录来实现的,并且没有从PD的store信息里删除已经down的store id:1和6,所以上面的tikv重启操作默认会在“误操作”的tikv节点重新创建目录和启动tikv,但是启动不成功,提示duplicated store address,“新的tikv”跟老的tikv都是同一个ip和端口,自然启动不成功,报错如下:
[2022/03/10 17:00:21.320 +08:00] [ERROR] [util.rs:460] ["request failed"] [err_code=KV:PD:gRPC] [err="Grpc(RpcFailure(RpcStatus { code: 2-UNKNOWN, message: \"duplicated store address: id:1194100 address:\\\"10.218.93.201:20160\\\" version:\\\"5.1.1\\\" status_address:\\\"10.218.93.201:20180\\\" git_hash:\\\"4705d7c6e9c42d129d3309e05911ec6b08a25a38\\\" start_timestamp:1646902817 deploy_path:\\\"/data6/deploy/tikv-20180/bin\\\" , already registered by id:6 address:\\\"10.218.93.201:20160\\\" version:\\\"5.1.1\\\" status_address:\\\"10.218.93.201:20180\\\" git_hash:\\\"4705d7c6e9c42d129d3309e05911ec6b08a25a38\\\" start_timestamp:1628479259 deploy_path:\\\"/data6/deploy/tikv-20180/bin\\\" last_heartbeat:1646897290357034075 \", details: [] }))
解决方案:pd元信息删除原来的store id,然后再重启下2个被rm又重新加入集群的“新节点”
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 store delete 1
$ tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 store delete 6
$ tiup cluster start BA-analyse-tidb_shyc_bak -N 10.xxxx.218:20160
$ tiup cluster start BA-analyse-tidb_shyc_bak -N 10.xxxx.201:20160
到目前,整个集群又恢复到5个tikv完全可用的情况了,本次模拟了5个tikv节点中2个tikv数据被删除的情况下,如何恢复数据的方式和方法。
宕机3台
这次玩大的,5台机器,直接shutdown 3个tikv节点的服务器,模拟硬件故障启动不起来。
处理步骤还是跟上面2KV宕机的步骤类似(但还是有不同)
(1)pd ctl查看宕机的store id,发现是1,2,8
tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 store|grep -B 10 'Disconnected'
(2)PD调度关闭,避免恢复过程中产生的各种异常情况。
(3)检查大于等于一半副本数在故障节点上的region
tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 region --jq='.regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as $total | map(if .==(2,8,1) then . else empty end) | length>=$total-length)}'
{"id":199,"peer_stores":[8,2,5]}
{"id":677,"peer_stores":[11,1,8]}
{"id":736,"peer_stores":[2,1,5]}
{"id":14,"peer_stores":[8,2,1]}
{"id":48,"peer_stores":[1,2,5]}
{"id":52,"peer_stores":[8,2,1]}
{"id":756,"peer_stores":[1,8,11]}
{"id":46,"peer_stores":[8,2,1]}
.....此处省略N行
{"id":175,"peer_stores":[2,1,8]}
{"id":203,"peer_stores":[1,8,2]}
{"id":211,"peer_stores":[8,5,2]}
{"id":22,"peer_stores":[2,1,11]}
{"id":36,"peer_stores":[8,2,5]}
{"id":730,"peer_stores":[5,8,1]}
{"id":611,"peer_stores":[8,11,1]}
{"id":58,"peer_stores":[8,2,1]}
{"id":131,"peer_stores":[2,1,11]}
{"id":18,"peer_stores":[8,5,2]}
{"id":30,"peer_stores":[2,1,11]}
大家可以看到region id 为 52、46、175、58的3个副本都丢失了(leader和2个follower都在宕机的3个tikv节点,如果3个tikv都无法启动或者恢复,那这4个region的数据就丢了)。另外还有不少只剩下1个peer的region id,比如199、677、736等只丢了2个副本(还剩1个peer,数据可以找回)。
(4)使用tiup cluster stop -N 来关闭存活的2个tikv
(5) 在所有未宕机的tikv节点,对所有region移除故障节点的peer。
./tikv-ctl --data-dir /data6/tikv-20180/ unsafe-recover remove-fail-stores -s 2,8,1 --all-regions
[2022/03/14 20:16:22.059 +08:00] [INFO] [mod.rs:118] ["encryption: none of key dictionary and file dictionary are found."]
[2022/03/14 20:16:22.060 +08:00] [INFO] [mod.rs:479] ["encryption is disabled."]
[2022/03/14 20:16:22.104 +08:00] [WARN] [config.rs:581] ["compaction guard is disabled due to region info provider not available"]
[2022/03/14 20:16:22.104 +08:00] [WARN] [config.rs:675] ["compaction guard is disabled due to region info provider not available"]
removing stores [2, 8, 1] from configurations...
[2022/03/14 20:16:25.909 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 298 store_id: 5]"] [old_peers="[id: 298 store_id: 5, id: 460 store_id: 8, id: 838 store_id: 2]"] [region_id=3]
[2022/03/14 20:16:25.909 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 795 store_id: 5]"] [old_peers="[id: 13 store_id: 1, id: 755 store_id: 2, id: 795 store_id: 5]"] [region_id=12]
[2022/03/14 20:16:25.909 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 829 store_id: 5]"] [old_peers="[id: 83 store_id: 8, id: 686 store_id: 2, id: 829 store_id: 5]"] [region_id=16]
[2022/03/14 20:16:25.909 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 789 store_id: 5, id: 940 store_id: 11 role: Learner]"] [old_peers="[id: 715 store_id: 8, id: 789 store_id: 5, id: 836 store_id: 2, id: 940 store_id: 11 role: Learner]"] [region_id=18]
........此处省略N行
[2022/03/14 20:16:25.910 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 447 store_id: 11, id: 688 store_id: 5]"] [old_peers="[id: 445 store_id: 2, id: 447 store_id: 11, id: 688 store_id: 5]"] [region_id=444]
[2022/03/14 20:16:25.910 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 633 store_id: 5]"] [old_peers="[id: 633 store_id: 5, id: 778 store_id: 1, id: 827 store_id: 2]"] [region_id=632]
[2022/03/14 20:16:25.910 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 731 store_id: 5]"] [old_peers="[id: 731 store_id: 5, id: 732 store_id: 8, id: 825 store_id: 1]"] [region_id=730]
[2022/03/14 20:16:25.910 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 818 store_id: 5]"] [old_peers="[id: 737 store_id: 2, id: 745 store_id: 1, id: 818 store_id: 5]"] [region_id=736]
[2022/03/14 20:16:25.910 +08:00] [INFO] [debug.rs:586] ["peers changed"] [new_peers="[id: 750 store_id: 11, id: 751 store_id: 5]"] [old_peers="[id: 750 store_id: 11, id: 751 store_id: 5, id: 773 store_id: 8]"] [region_id=749]
success
注意:因为是5个KV宕机3台,需要在stop tikv server的剩余2台tikv都执行。
(6)重启2个存活的tikv
(7)再次查看多数副本在宕机的3台tikv上的region,发现就剩下这些3个副本都丢失的region了
tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 region --jq='.regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as $total | map(if .==(2,8,1) then . else empty end) | length>=$total-length)}'
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v5.1.1/ctl /home/tidb/.tiup/components/ctl/v5.1.1/ctl pd -u http://10.xxxxx:2379 region --jq=.regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as $total | map(if .==(2,8,1) then . else empty end) | length>=$total-length)}
{"id":52,"peer_stores":[8,2,1]}
{"id":46,"peer_stores":[8,2,1]}
{"id":28,"peer_stores":[8,2,1]}
{"id":203,"peer_stores":[1,8,2]}
{"id":175,"peer_stores":[2,1,8]}
{"id":58,"peer_stores":[8,2,1]}
{"id":14,"peer_stores":[8,2,1]}
(8)查看这些region id所属的table,下面拿203这个region id来查看,发现是属于test库的sbtest5表,需要查看下所有3个副本都丢失的region都属于哪些表,有时候涉及的region过多,可以搞个小脚本批量执行,执行结果都汇总到一个文本中,以便后续补数和业务沟通,这种情况是:反正数据已经丢了,避免业务访问到这些reigon的报错,需要跟业务沟通是否用空region代替,后续通过业务或者其他方式找回。
curl http://10.xxxx.160:10086/regions/203
{
"start_key": "dIAAAAAAAAA9X3KAAAAAAARH7A==",
"end_key": "dIAAAAAAAABF",
"start_key_hex": "74800000000000003d5f7280000000000447ec",
"end_key_hex": "748000000000000045",
"region_id": 203,
"frames": [
{
"db_name": "test",
"table_name": "sbtest5",
"table_id": 61,
"is_record": true,
"record_id": 280556
}
]
}
(9)再次关停存活的2个tikv并且在这2个tikv上补这种3个副本都丢了的region,用空region补充这些peer副本。
./tikv-ctl --data-dir /data6/tikv-20180/ recreate-region -p 10.xxxxx:2379 -r 52
./tikv-ctl --data-dir /data6/tikv-20180/ recreate-region -p 10.xxxxx:2379 -r 46
注意:补空region时 -r 命令后不能带多个逗号分隔的region id,只能一个region一个region的补,如果region id过多,可以考虑搞个脚本来跑。
(10)恢复pd调度配置
(11)重启2个tikv,再次查看region情况,发现所有的region的多数副本都已经正常。
tiup ctl:v5.1.1 pd -u http://10.xxxxx:2379 region --jq='.regions[] | {id: .id, peerstores: [.peers[].storeid] | select(length as $total | map(if .==(2,8,1) then . else empty end) | length>=$total-length)}'
(12) 登录查看数据,通过上面的第5步骤的region丢失来看查看,统计发现sbtest5/sbtest6表丢失了近1半的记录,其他数据表有完整数据的。
mysql> select count(1),count(c) from sbtest5;
+----------+----------+
| count(1) | count(c) |
+----------+----------+
| 280555 | 280555 |
+----------+----------+
1 row in set (0.35 sec)
mysql> select count(1),count(c) from sbtest6;
+----------+----------+
| count(1) | count(c) |
+----------+----------+
| 247312 | 247312 |
+----------+----------+
1 row in set (0.27 sec)
mysql> select count(1),count(c) from sbtest1;
+----------+----------+
| count(1) | count(c) |
+----------+----------+
| 500000 | 500000 |
+----------+----------+
1 row in set (0.38 sec)
目前集群状态:
总结和思考
总结
本篇文章主要讲了tikv节点宕机时的现象以及如何恢复的方案,tikv 3副本的 raft group能保证在宕机一台KV时不用DBA立刻介入的能力;在上面宕机 2 台 KV 时,因为根据PD的调度规则,一个raft group的peer肯定会调度到3个KV节点,所以2个KV宕机,肯定还有一个peer在,需要DBA介入,并且使用“快刀斩乱麻”的tikv-ctl尽快的恢复了集群;但是在集群宕机 3 台 KV 时就有大概率的数据丢失,这时候就需要考虑业务恢复重要还是数据保护重要了,另外在3个kv宕机的章节,我还重启了2次tikv,其实如果定好了预案,清理region中故障store的peer跟 recreate 空region可以一起做,本次只是突出region丢失数据的严重性。
另外以上是基于5个KV的集群做的多次验证,每次都出了不少的“状况”,可以根据文章的大概处理流程自己模拟验证,其实我遇到一个“严重”的“状况”是:5副本的KV当挂了3个时,可能一些mysql系统表(比如user权限表)的region也“丢失”了,导致无法进入到集群验证数据,后来通过:跟mysql类似的skip-grant-table才进入,如果用户权限都没有了,业务肯定都无法连接tidb了,这种情景就是要强调:在我没有BR备份的情况下,5个节点(3kv宕机)能给我恢复出2个kv数据的重要性了。
还有一个就是补完空region,还遇到了一些诡异的“数据准确性”问题,比如sbtest5表的默认索引k5索引被补了空region时,idxk是我重新建立的索引。通过2个索引的查询结果是不一样的,也就是说补region影响了数据的正确性,不过对于上面的集群多数KV不可用的情况下,还是建议新建集群然后将2个KV的数据恢复到新集群。
mysql> show create table sbtest5\G
*************************** 1. row ***************************
Table: sbtest5
Create Table: CREATE TABLE `sbtest5` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
KEY `k_5` (`k`),
KEY `idx_k` (`k`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=633209
1 row in set (0.00 sec)
mysql> select * from sbtest5 use index(k_5) where k=249602;
ERROR 1105 (HY000): inconsistent index k_5 handle count 99 isn't equal to value count 66
mysql>
mysql> select count(1) from sbtest5 use index(idx_k) where k=249602;
+----------+
| count(1) |
+----------+
| 66 |
+----------+
1 row in set (0.00 sec)
mysql> select count(1) from sbtest5 use index(idx_k);
+----------+
| count(1) |
+----------+
| 280555 |
+----------+
1 row in set (0.20 sec)
mysql> select count(1) from sbtest5 use index(k_5);
+----------+
| count(1) |
+----------+
| 500000 |
+----------+
1 row in set (0.27 sec)
思考:
(1) 在集群多tikv(>=3)不可用的时候,需要根据业务情况做好降级准备,最好在sql接入层(HAproxy/lvs)先把vip下线,避免新流量写入,毕竟后面tikv-ctl操作涉及到tikv所有节点的shutdown。
(2)以上的测试都是基于默认的 3 副本来分析的,对于多 KV 的核心集群,其实可以设定 5 副本来增加集群的可用性。
(3)关闭PD调度和避免宕机tikv重启的重要性,避免元信息异常引发的region不可用
(4)tikv真的不可以恢复后的补region的决定,需要根据实际情况来定,另外一定要看下补的region都是哪些业务的什么table or index,注意观察下“修复”后数据的准确性。
(5)做好TiDB的备份,如果真的线上集群不可用,至少还有备份可以恢复,就看备份的频度(容忍丢失的数据量),一旦集群多数KV不可用,需要”2条腿“走路,1个方案是BR恢复备份到新集群,另外就是在原有集群进行tikv-ctl的恢复。
(6)“多活”方案的重要性,比如基于ticdc的主备同城双机房,备机房可以做一些准实时的读请求(读写分离);另外就是 tidb 5.4 版本提供的同城双中心自适应同步模式;其他同城3中心或者异地3中心的方案可以参考官方文档。
(7)同一个集群同时3个tikv宕机的概率还是比较低的,除非就是部署时没有考虑机架或者交换机部署,由于机架掉电或者交换机故障导致的多KV的不可用,这是就要看业务是否能扛或者IDC换件的应急速度了。
本文到此就结束了,最后想说的是,每次都是靠人工模拟故障,是不是太low了,要不要用下chaos mash?OK,下一篇分享就又有的写了。哈哈