4
1
4
0
专栏/.../

TiKV集群断电(灾难)恢复过程记录

 Timber  发表于  2023-07-03

背景

我们的应用使用TiKV作为存储层存储用户的数据。服务部署在用户的现场,现场遇到断电时,服务器文件经常会损坏,包括TiKV服务和pd服务的文件,本文整理了修复现场TiKV集群断电的过程,希望能起到参考作用。由于对TiKV的运维不熟悉,有些操作可能并不合理,欢迎大家在评论区指正。

环境

TiKV版本v6.1.0,TiKV集群中包含6个TiKV节点,pd正常。

修复过程

理论基础

在介绍修复过程之前,有必要对TiKV相关的一些概念进行说明。对TiKV架构熟悉的同学可以跳过。

TiKV是存储数据的服务,当sst文件或raftlog损坏或丢失时,表示存储数据遭受损坏。当TiKV受损时,应尽可能减少数据的丢失,需要先使用工具对TiKV集群进行修复,如果修复无效,可以使用unsafe方式进行恢复。unsafe方式是在TiKV大多数sst文件受损时从其他TiKV节点恢复数据的方法,期间会删除本机TiKV数据,可能会出现数据丢失的情况,使用时需要慎重!

TiKV内部存储的逻辑单元是Region,对应了一段key range。而底层实际存储数据的物理单元是rocksDB的sst文件,一个region可能存储在多个sst文件内,一个sst文件内也可能包含了多个region的信息。当sst文件损坏时,除了修复sst文件之外,还需要修复pd维护的region信息。因此:当TiKV集群恢复成功后,region状态可能并不完整,还需要对region进行修复。

大致修复思路

pd维护了集群的元数据,TiKV的恢复可能会用到pd的元信息以及pd的工具。因此,在TiKV集群遭遇断电时,应该先查看pd集群是否正常运行,如果有pd节点无法正常启动,优先处理pd节点。当pd集群恢复正常后,恢复TiKV节点。TiKV节点均启动后,通过pd修复受损的region。

排查现场情况

3台TiKV无法正常重启,可能已经丢失了数据。

  1. 一台重启报错invalid configuration: Found raft data set when it should not exist. 提示raft数据不应该存在,为方便描述,假设该实例为TiKV-1
  2. 一台报错为[ERROR] [engine_factory.rs:156] ["failed to create kv engine"] [err="Engine(\"Corruption: Can't access /2103071.sst: IO error: No such file or directorywhile stat a file for size: /data/db/2103071.sst: No such file or directory,提示sst数据丢失,设为TiKV-2
  3. 一台报错为[FATAL] [server.rs:1487] ["failed to create kv engine: Storage Engine Corruption: Sst file size mismatch: /data/db/2123787.sst. Size recorded in manifest 27100810, actual size 36721252\n"],提示sst文件损坏,设为TiKV-3

当时我看到三台TiKV同时无法重启且报错均不相同,心情大概是这样的:

image.png

修复过程

柿子先捏软的,应该尽可能修复容易修复的TiKV,这样,即使其他的无法修复了,还可以用到后续的unsafe方式从正常节点上恢复数据。

  1. 修复TiKV-1.raftlog损坏,这个报错比较奇怪,Found raft data set when it should not exist. 我们设置的log确实是使用的raft-engine,但却提示不应该存在。修复步骤为:

    1. 备份raft-engine目录. 在服务器上删除该目录,重启tikv实例。这时会报错端口冲突  [FATAL] [server.rs:950] ["failed to start node: Grpc(RpcFailure(RpcStatus { code: 2-UNKNOWN, message: \"duplicated store address: id:67387001 address:\\\"xxxxxx:17515\\\" version:\\\"6.1.0 , already registered by id:29694712 address:\\\"xxxxxx:17515。 从报错中可以看到,id为67387001的端口被29694712这个id注册过了。原因是,每个TiKV实例都是一个store,每个store都有一个id,这个id在pd中维护,pd只认识原先的store id,需要在pd维护的信息中删除原先的id,新起的store id才能注册到pd中。在报错例子中,应该删除的id是29694712。
    2. 随机找一个pd实例,进入到容器中,执行./pd-ctl store查看store状态,store状态中记录了每个tikv实例的store id、store状态、地址等信息。通过失效tikv的地址找到其store_id,再通过./pd-ctl store delete {tikv-store_id} 删除原来的store-id。重启tikv成功,再查看pd-ctl store结果,新的store-id(例如b中的67387001)出现在了输出结果中。
  2. 修复TiKV-3,sst数据损坏。为啥先修sst数据损坏呢,损坏的至少还有可能抢救,丢失的是真无法抢救了。修复步骤:

    1. 停止TiKV-3,避免自动重启
    2. 修复sst文件需要用到TiKV的官方修复工具tikv-ctl,TiKV镜像中自带了该工具,也可以通过 安装TiUP 来使用tiup集成的tikv-ctl。实践时通过跑TiKV容器,挂载了tikv目录,并在容器中执行。
    3. 先执行./tikv-ctl ldb --db=data/db repair,该命令会修复db目录下的所有sst文件。
    4. 等待repair完成后,重启tikv实例,重启成功说明修复sst文件成功。
    5. ps:本来打算用另外一种简单的方式恢复的,unsafe方式,该方式可以忽略损坏的sst和region强行拉起tikv,但是并未成功,这里也分享一下: /tikv-ctl --data-dir /data bad-ssts --pd <pd_endpoint> 该工具会检测出该TiKV实例上的所有损坏的sst文件,移除这些sst文件,并且从pd中找到该文件相关的region,将该TiKV Store上的 region设置为tombstone以便让该TiKV实例在启动时跳过这些region。但在执行过程中,由于pd会隔一段时间自动重启(v6.1.0的bug,详见https://github.com/tikv/pd/issues/6647#event-9629298440 ,v6.3.0已经修复 ),该命令不会顺利执行。 工具输出结果实例如下,按照suggested operations执行命令即可。suggested operations是工具提供的建议命令,第一行表示删除sst文件,第二行表示将该实例上的受损region设置为tombstone。如果设置tombstone失败,可以加上--force选项重试。
      --------------------------------------------------------
      corruption info:
      data/tikv-21107/db/000014.sst: Corruption: Bad table magic number: expected 9863518390377041911, found 759105309091689679 in data/tikv-21107/db/000014.sst
      sst meta:
      14:552997[1 .. 5520]['0101' seq:1, type:1 .. '7A7480000000000000FF0F5F728000000000FF0002160000000000FAFA13AB33020BFFFA' seq:2032, type:1] at level 0 for Column family "default"  (ID 0)
      it isn't easy to handle local data, start key:0101
      overlap region:
      RegionInfo { region: id: 4 end_key: 7480000000000000FF0500000000000000F8 region_epoch { conf_ver: 1 version: 2 } peers { id: 5 store_id: 1 }, leader: Some(id: 5 store_id: 1) }
      suggested operations:
      tikv-ctl ldb --db=data/tikv-21107/db unsafe_remove_sst_file "data/tikv-21107/db/000014.sst"
      tikv-ctl --db=data/tikv-21107/db tombstone -r 4 --pd <endpoint>
      --------------------------------------------------------
      
  3. 修复TiKV-2,sst文件丢失。由于sst文件丢失了,我选择了直接unsafe remove-failed-store,并直接删除了该store上的所有数据,重建了该节点,选择从其他节点上恢复数据。这种方式可能会丢失数据。因此,在进行unsafe remove TiKV之前,需要尽可能先修复其他比较容易恢复的TiKV节点,减少数据丢失。

    1. 查看损坏TiKV节点的store id。随机进入一台pd的容器,执行./pd-ctl store,查看当前TiKV集群节点的状态。找到状态为Disconnected(有可能是Down状态)的store id。

    2. 使用pd-ctl unsafe recover恢复。下面命令中,首先移除了丢失的TiKV节点4(在上一步骤中得到),如果需要移除多个节点,需要用逗号隔开,如 remove-failed-stores 4, 5。其中的地址是pd节点的ip,端口为启动参数中的--advertise-client-urls

      / # ./pd-ctl -u {pd_addr} unsafe remove-failed-stores 4

      Success!

      / # ./pd-ctl -u {pd_addr} unsafe remove-failed-stores show

      [

        {

          "info": "Unsafe recovery enters collect report stage: failed stores 4",

          "time": "2023-06-05 07:35:47.195"

        },

        {

          "info": "Unsafe recovery finished",

          "time": "2023-06-05 07:35:56.182"

        }

      ]

    3. 使用remove-failed-stores show 命令查看进度,等到出现Unsafe recover finished时,表示移除成功。

    4. 此时TiKV节点下线,剩余节点能重新调度并提供服务。

    5. 停止失效的TiKV节点,删除TiKV的所有数据,重启即可,重启后的新节点将从其他正常TiKV上恢复数据

  4. 至此,TiKV节点总算是都修复完成了。但是任务还没有完成,我们的应用无法正常运行,报错CDC:ErrRegionsNotCoverSpan]regions not completely left cover span,表示tikv的regions无法满足我们应用要拉取的范围。这是因为虽然TiKV启动成功了,但是修复过程中可能导致region的失效,或者说有些region本身就丢失了,集群无法正常使用这些region提供服务,需要对region进行进一步恢复。image.png

  5. 有些region虽然已经失效了,但是可以通过重建的方式把region的逻辑拉起来,即使数据已经丢失,但在外部看起来,region是没有丢失的。恢复步骤:

    1. 随机选择一个pd节点,进入到容器中,执行./pd-ctl region查看集群中所有region的信息。region信息中包含一项为"leader",找到所有leader为[]或者缺乏leader项的region
    2. 需要重建上述步骤中找到的所有缺乏leader的region。选择一台TiKV,关闭实例,在其所在的服务器上,运行tikv容器并在容器中执行./tikv-ctl --data-dir /data --config=/tikv.toml recreate-region -p {PD_ADDR} -r {region_id},必要时可以通过脚本批量重建region
    3. 脚本执行成功后,重启TiKV实例。
  6. work out,至此,TiKV集群能正常提供服务且我们的应用依赖存储的逻辑能正常运行。

4
1
4
0

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

评论
暂无评论