TiDB 工具 | 備份的 “算子下推”:BR 簡介
BR 選擇了在 Transaction KV 層面進(jìn)行掃描來實(shí)現(xiàn)備份,這樣,備份的核心便是分布在多個(gè) TiKV 節(jié)點(diǎn)上的 MVCC Scan:簡單,粗暴,但是有效,它生來就繼承了 TiKV 的諸多優(yōu)勢(shì):分布式、利于橫向拓展、靈活(可以備份任意范圍、未 GC 的任意版本的數(shù)據(jù))等等優(yōu)點(diǎn)。
相較于從前只能使用 mydumper 進(jìn)行 SQL 層的備份,BR 能夠更加高效地備份和恢復(fù):它取消了 SQL 層的開銷,同時(shí)支持備份索引,而且所有備份都是已經(jīng)排序的 SST 文件,以此大大加速了恢復(fù)。
BR 的實(shí)力在之前的文章(https://pingcap.com/zh/blog/cluster-data-security-backup)中已經(jīng)展示過了,本文將會(huì)詳細(xì)描述 BR 備份側(cè)的具體實(shí)現(xiàn):簡單來講,BR 就是備份的 “算子下推”:通過 gRPC 接口,將任務(wù)下發(fā)給 TiKV,然后讓 TiKV 自己將數(shù)據(jù)轉(zhuǎn)儲(chǔ)到外部存儲(chǔ)中。
BR 的基本流程

接口
Backup的接口,這個(gè)接口與一般的讀請(qǐng)求不同——它不會(huì)返回?cái)?shù)據(jù)給客戶端,而是直接將讀到的數(shù)據(jù)存儲(chǔ)到指定的存儲(chǔ)器(External Stroage)中:service?Backup?{
????//?收到?backup?的?TiKV,將會(huì)將?Request?指定范圍中,所有自身為?leader
????//?的 region 備份,并流式地返回給客戶端(每個(gè) region 對(duì)應(yīng)流中的一個(gè) item)。
????rpc?backup(BackupRequest)?returns?(stream?BackupResponse)?{}
}
// NOTE:隱藏了一些不重要的 field 。
message?BackupRequest?{
????//?備份的范圍,[start_key, end_key)。
????bytes?start_key?=?2;
????bytes?end_key?=?3;
????//?備份的 MVCC 版本。
????uint64?start_version?=?4;
????uint64?end_version?=?5;
????
????//?限流接口,為了確保和恢復(fù)時(shí)候的一致性,限流限制保存?zhèn)浞菸募碾A段的速度。
????uint64?rate_limit?=?7;
????
????//?備份的目標(biāo)存儲(chǔ)。
????StorageBackend?storage_backend?=?9;
????//?備份的壓縮?--?這是一個(gè)用 CPU 資源換取 I/O 帶寬的優(yōu)化。
????CompressionType?compression_type?=?12;
????int32?compression_level?=?13;
????//?備份支持加密。
????CipherInfo?cipher_info?=?14;
}
message?BackupResponse?{
????Error?error?=?1;
????//?備份的請(qǐng)求將會(huì)返回多次,每次都會(huì)返回一個(gè)已經(jīng)完成的子范圍。
????//?利用這些信息可以統(tǒng)計(jì)備份進(jìn)度。
????bytes?start_key?=?2;
????bytes?end_key?=?3;
????//?返回該范圍備份文件的列表,用于恢復(fù)的時(shí)候追蹤。
????repeated?File?files?=?4;
}
客戶端
依據(jù)每個(gè) table 的所有 data key 生成 range。(所有帶有 t{table_id}_r 前綴的 Key)
依據(jù)每個(gè) index 的所有 index key 生成 range。(所有帶有 t{table_id}_i{index_id}?前綴的 Key)
如果 table 存在 partition(這意味著,它可能有多個(gè) table ID),對(duì)于每個(gè) partition,按照上述規(guī)則生成 range。
在之前的“粗粒度備份”中,BR 客戶端每收到一個(gè) BackupResponse 就會(huì)將其中的 [start_key, end_key)?作為一個(gè) range 存入一顆區(qū)間樹中(你可以把它想象成一個(gè)簡單的 BTreeSet<(Vec
, Vec )。)> “粗粒度備份” 遇到任何可重試錯(cuò)誤都會(huì)忽略掉,只是相應(yīng)的 range 不會(huì)存入這顆區(qū)間樹中,因此樹中會(huì)留下一個(gè) “空洞”,這兩步的偽代碼如下。
func?Backup(tree?RangeTree)?{
????//?...?
????for?_,?resp?:=?range?responses?{
????????if?resp.Success?{
????????????tree.Insert(resp.StartKey,?resp.EndKey)??
????????}
????}
}
//?An?example:?
//?When?backing?up?the?ange?[1,?5).
//?[1,?2),?[3,?4)?and?[4,?5)?successed,?and?[2,?3)?failed:
//?The?Tree?would?be?like:?{?[1,?2),?[3,?4),?[4,?5)?},?
//?and?the?range?[2,?3)?became?a?"hole"?in?it.
//?
//?Given?the?range?tree?is?sorted,?it?would?be?easy?to?
//?find?all?holes?in?O(n)?time,?where?n?is?the?number?of?ranges.在 “粗粒度備份” 結(jié)束之后,我們遍歷這顆區(qū)間樹,找到其中所有 “空洞”,并行地進(jìn)行 “細(xì)粒度備份”:
找到包含該空洞的所有 region。
對(duì)他們的 leader 發(fā)起 region 相應(yīng)范圍的 Backup RPC。
成功之后,將對(duì)應(yīng)的 range 放入?yún)^(qū)間樹中。
TiKV
BackupRequest會(huì)被轉(zhuǎn)化為一個(gè)Task。Task中的start_key和end_key生成一個(gè)叫做 “Progress” 的結(jié)構(gòu):它將會(huì)把Task中龐大的范圍劃分為多個(gè)子范圍,通過:掃描范圍內(nèi)的 Region。 對(duì)于其中當(dāng)前 TiKV 角色為 Leader 的 Region,將該 Region 的范圍作為 Backup 的子任務(wù)下發(fā)。
Progress提供的接口是一個(gè)使用 “拉模型” 的接口:forward。隨后,TiKV 創(chuàng)建的各個(gè) Backup Worker 將會(huì)去并行地調(diào)用這個(gè)接口,獲得一組待備份的 Region,然后執(zhí)行以下三個(gè)步驟:對(duì)于這些 Region,Backup Worker 將會(huì)通過?RaftKV 接口,進(jìn)行一次 Raft 的讀流程,最終獲得對(duì)應(yīng) Region 在 Backup TS 的一個(gè) Snapshot。(Get Snapshot)
對(duì)于這個(gè) Snapshot,Backup Worker 會(huì)通過 MVCC Read 的流程去掃描 backup_ts?的一致版本。這里我們會(huì)掃描出 Percolator 的事務(wù),為了恢復(fù)方便,我們會(huì)準(zhǔn)備 “default” 和 “write” 兩個(gè)臨時(shí)緩沖區(qū),分別對(duì)應(yīng) TiKV Percolator 實(shí)現(xiàn)中的 Default CF 和 Write CF。(Scan)
然后,我們會(huì)先將掃描出來的事務(wù)中兩個(gè) CF 的 Raw Key 刷入對(duì)應(yīng)緩沖區(qū)中,在整個(gè) Region 備份完成(或者有些 Region 實(shí)在過大,那么會(huì)在途中切分備份文件)之后,再將這兩個(gè)文件存儲(chǔ)到外部存儲(chǔ)中,記錄它們對(duì)應(yīng)的范圍和大小等等,最后返回一個(gè) BackupResponse?給 BR。(Save)
s3://some-bucket/some-folder,可以指定備份到 S3 云盤上的some-bucket之下的some-folder目錄中。BR 的挑戰(zhàn)和優(yōu)化
BackupMeta 和 OOM

message?BackupMeta?{
????//?Some?fields?omitted...
????//?An?index?to?files?contains?data?files.
????MetaFile?file_index?=?13;
????//?An?index?to?files?contains?Schemas.
????MetaFile?schema_index?=?14;
????//?An?index?to?files?contains?RawRanges.
????MetaFile?raw_range_index?=?15;
????//?An?index?to?files?contains?DDLs.
????MetaFile?ddl_indexes?=?16;
}//?MetaFile?describes?a?multi-level?index?of?data?used?in?backup.
message?MetaFile?{
????//?A?set?of?files?that?contains?a?MetaFile.
????//?It?is?used?as?a?multi-level?index.
????repeated?File?meta_files?=?1;
????
????//?A?set?of?files?that?contains?user?data.
????repeated?File?data_files?=?2;
????//?A?set?of?files?that?contains?Schemas.
????repeated?Schema?schemas?=?3;
????//?A?set?of?files?that?contains?RawRanges.
????repeated?RawRange?raw_ranges?=?4;
????//?A?set?of?files?that?contains?DDLs.
????repeated?bytes?ddls?=?5;
}meta_files將自身指向下一個(gè)節(jié)點(diǎn):File是一個(gè)到外部存儲(chǔ)中其他文件的引用,包含文件名等等基礎(chǔ)信息。GC, GC never changes
備份壓縮
限流與隔離
--ratelimit參數(shù)啟動(dòng) BR 的時(shí)候,TiKV 側(cè)的第三步 “Save”,將會(huì)被限流,與此同時(shí)也會(huì)限制之前步驟的流量。ratelimit限流施加于 Save 階段,因此是限制寫備份數(shù)據(jù)的速度。總結(jié)

