br/docs/cn/2019-08-05-new-design-of-backup-restore.md
Last updated: 2020-03-07
背景:由于需要和 TiDB CDC 在技术上整合,这个文档描述了一个基本思路是 kv scan 的新方案。
备份的原理其实就是 select *,但是这个方案和 mydumper 的区别:
整个备份过程由独立于 TiKV 的外部程序推进,整体的基本流程:
外部程序执行备份的基本流程:
分布式备份是新方案的设计目标之一。最简单高效的方式是直接把备份的命令下发到 TiKV,利用 region info accessor 把命令细分到各个 region leader 上,执行 scan。当下推执行完成后,需要查漏补缺,确保所有范围都覆盖到。这个实现方式最大的问题是可能会备份多余的数据,比如 leader 在备份迁移了,不过好在这不是什么正确性问题。还有一个方式是常规的按照 region 下发备份的命令,也很简单但是效率没有前者高,也有备份多余文件的可能,但是概率毕竟低。
由于 TiDB/TiKV 的事务模型是 percolator,数据的存储需要使用 3 个 CF,分别是 default,lock,write,所以如果 TiKV 想把数据保存在 SST 中,那么起码 TiKV 需要扫出 write 和 default 的数据。这意味现有的 iterator 是不适用的,因为:
它只能吐出 default 的 key-value
它吐出的是 point-in-time 的数据。
在全量备份中,TiKV 需要根据一个 ts(称为 backup_ts)扫出 write 和 default 的 key-value。Iterator 吐出的 key 需要是 datakey,也就是 key 的第一个字节是 ‘z’,这是为了在恢复的时候可以直接 ingest,不用 rewrite key。
在增量备份中,TiKV 需要扫出一段时间的增量数据,一段时间具体来说:
(backup_ts, current_ts]
由于备份的保证是 SI,所有增量数据可以直接通过扫 write CF 中的记录即可得到。需要吐出的记录有:
不需要吐出的记录有:
性能是 KV Scan 方案最头疼的问题之一,因为全表扫描势必会对线上业务造成影响。增量的实现也是全表扫,同样有性能问题,这个和传统数据库的增量不一样。好在也有优化的办法,比如学一下 crdb 在 SST 上加上 max ts 的 table property。
备份存储设计了一个接口,下层可以有不同的实现,比如本地磁盘,s3 等。由于外部存储不一定是真正的磁盘,所以 TiKV 在生成 SST 的时候,不会直接把数据写到本地磁盘上,而是先缓存在内存中,生成完毕后直接写到外部存储中,这样能避免 IO,提高整个备份的吞吐,当然这里需要内存控制。
异常处理备份期间的异常和 select * 一样,可分为两种可恢复和不可恢复,所有的异常都可以直接复用 TiDB 现有的机制。
可恢复异常一般包含:
当发生这些异常时,备份的进度不会被打断。
除了以上的其他错误都是不可恢复异常,发生后,它们会打断备份的进度。
超出 GC 时间是说,需要备份的数据已经被 GC,这情况一般发生在增量备份上,会导致增量不完整。在发生这个错的时候,外部程序需要重新来一个全量备份。
现在默认的 GC 时间是 10 分钟,实在是太短了,这意味着一旦开启全量备份,增量备份必须马上接上,而且运行的间隔必须在 10 分钟内。这个频率集群肯定受不了。这个问题不只备份会遇到, CDC 也会遇到。在开始备份或者 CDC 的集群上需要适当延长 GC 时间,比如延长到 6 小时或者 12 小时。
恢复所需的工作有以下几个:
由于 TiDB 的数据在 TiKV 那保存的形式是:
Key: tablePrefix{tableID}_recordPrefixSep{rowID}
Value: [col1, col2, col3, col4]
Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue
Value: rowID
在 Key 中编码了 tableID,所以我们不能直接把备份下来的 SST 文件不经任何处理恢复到 TiKV 中,否则就有可能因为 tableID 对不上而导致数据错乱。
为了解决该问题,我们必须在恢复前对 SST 文件进行 key 的改写,将原有的 tableID 替换为新创建的 tableID,同样的 indexID 也需要相同的处理。
TiKV 对 Region 的大小是有限制的,默认为 96MB,超出该阈值则需要分裂(Split)。集群刚启动时,只有少量的 Region 的,我们在恢复的时候不能把所有数据都恢复到这些少量的 Region 中,所以需要提前将 Region 分裂并打散(Scatter)。
由于备份 SST 文件是按照 Region 生成的,天然就有合适的大小和对应的数据范围,所以我们可以在根据各个 SST 文件中的范围对集群中的 Region 进行分裂。分裂完成后还需要打散新分裂出来的 Region,防止发生数据倾斜。
现在使用的方案已经不再使用 tikv-importer(它也预计会在 4.0 被移除),转而使用了 TiKV 一组能够下载/导入 SST 文件的新 API。相关的信息可以在这里找到。
这里的困境是 ingest SST 失败会导致各个副本之间的数据不一致。
我们通过 placement rule 实现资源隔离。
原集群恢复涉及到各种 ID 的问题,最简单办法就是 rewrite key,把 key 中老 ID 替换成新 ID。这是 crdb 的方案,我们同样可以借鉴。
我们通过 Key rewrite 在理论上支持原集群恢复。
备份 RPC 的相关内容已经可以在 kvproto 仓库 找到。
新的恢复 RPC 见这里。