<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          TiDB 工具 | 備份的 “算子下推”:BR 簡介

          共 7657字,需瀏覽 16分鐘

           ·

          2021-12-17 15:11

          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 的基本流程

          接口

          為了區(qū)別于一般的 MVCC Scan 請(qǐng)求,TiKV 提供一個(gè)叫做?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;
          }

          客戶端

          BR 客戶端會(huì)借助 TiDB 的接口,根據(jù)用戶指定需要備份的庫和表,計(jì)算出來需要備份的范圍(ranges)。計(jì)算的依據(jù)是:

          1. 依據(jù)每個(gè) table 的所有 data key 生成 range。(所有帶有 t{table_id}_r 前綴的 Key)

          2. 依據(jù)每個(gè) index 的所有 index key 生成 range。(所有帶有 t{table_id}_i{index_id}?前綴的 Key)

          3. 如果 table 存在 partition(這意味著,它可能有多個(gè) table ID),對(duì)于每個(gè) partition,按照上述規(guī)則生成 range。


          為了獲得最大的并行度,BR 客戶端會(huì)并行地所有 TiKV 發(fā)送這些 Range 上的備份請(qǐng)求。

          當(dāng)然,備份不可能一帆風(fēng)順。我們?cè)趥浞莸臅r(shí)候不可避免地會(huì)遇到問題:例如網(wǎng)絡(luò)錯(cuò)誤,或者觸發(fā)了 TiKV 的限流措施(Server is Busy),或者 Key is Locked,這時(shí)候,我們必須縮小這些 range,重新發(fā)送請(qǐng)求(否則,我們就要重復(fù)一遍之前已經(jīng)做過的工作)。

          在失敗之后,選擇合適的 range 來重發(fā)請(qǐng)求的過程,在 BR 中被稱為“細(xì)粒度備份(fine-grained backup)”,具體而言:

          1. 在之前的“粗粒度備份”中,BR 客戶端每收到一個(gè) BackupResponse 就會(huì)將其中的 [start_key, end_key)?作為一個(gè) range 存入一顆區(qū)間樹中(你可以把它想象成一個(gè)簡單的 BTreeSet<(Vec, Vec)>)。


          2. “粗粒度備份” 遇到任何可重試錯(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.
          3. 在 “粗粒度備份” 結(jié)束之后,我們遍歷這顆區(qū)間樹,找到其中所有 “空洞”,并行地進(jìn)行 “細(xì)粒度備份”:
          • 找到包含該空洞的所有 region。

          • 對(duì)他們的 leader 發(fā)起 region 相應(yīng)范圍的 Backup RPC。

          • 成功之后,將對(duì)應(yīng)的 range 放入?yún)^(qū)間樹中。


          ????? 4. 在一輪 “細(xì)粒度備份” 結(jié)束后,如果區(qū)間樹中還有空洞,則回到 (3),在超過一定次數(shù)的重試失敗之后,報(bào)錯(cuò)并退出。


          在上述 “備份” 流程完成之后,BR 會(huì)利用 Coprocessor 的接口,向 TiKV 請(qǐng)求執(zhí)行用戶所指定表的 checksum。

          這個(gè) checksum 會(huì)在恢復(fù)的時(shí)候用作參考,同時(shí)也會(huì)和 TiKV 在備份期間生成的逐文件的 checksum 進(jìn)行對(duì)比,這個(gè)比對(duì)的過程叫做 “fast checksum”。

          在 “備份” 的過程中,BR 會(huì)通過 TiDB 的接口收集備份的表結(jié)構(gòu)、備份的時(shí)間戳、生成的備份文件等信息,儲(chǔ)存到一個(gè)? “backupmeta” 中,這個(gè)是恢復(fù)時(shí)候的重要參考。

          TiKV

          為了實(shí)現(xiàn)資源隔離,減少資源搶占,backup 相關(guān)的任務(wù)都運(yùn)行在一個(gè)單獨(dú)的線程池里面。這個(gè)線程池中的線程叫做 “bkwkr”(“backup worker” 極其抽象的縮寫)。

          在收到 gRPC 備份的請(qǐng)求之后,這個(gè)BackupRequest會(huì)被轉(zhuǎn)化為一個(gè)Task。


          而后,TiKV 會(huì)利用Task中的start_keyend_key生成一個(gè)叫做 “Progress” 的結(jié)構(gòu):它將會(huì)把Task中龐大的范圍劃分為多個(gè)子范圍,通過:
          1. 掃描范圍內(nèi)的 Region。
          2. 對(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è)步驟:

          1. 對(duì)于這些 Region,Backup Worker 將會(huì)通過?RaftKV 接口,進(jìn)行一次 Raft 的讀流程,最終獲得對(duì)應(yīng) Region 在 Backup TS 的一個(gè) Snapshot。(Get Snapshot)

          2. 對(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)

          3. 然后,我們會(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)


          為了保證文件名的唯一性,備份的文件名會(huì)包括當(dāng)前 TiKV 的 store ID、備份的 region ID、start key 的哈希、CF 名稱。

          備份文件使用 RocksDB 的 Block Based SST 格式:它的優(yōu)勢(shì)是,原生支持文件級(jí)別的 checksum 和壓縮,同時(shí)具備可以在恢復(fù)的時(shí)候快速被 ingest 的潛力。

          外部存儲(chǔ)是為了適配多種備份目標(biāo)而存在的通用儲(chǔ)存抽象:有些類似于 Linux 中的 VFS,不過簡化了非常多:僅僅支持簡單的保存和下載整個(gè)文件的操作。它目前對(duì)主流的云盤都做了適配,并且支持以 URL 的形式序列化和反序列化。例如,使用s3://some-bucket/some-folder,可以指定備份到 S3 云盤上的some-bucket之下的some-folder目錄中。

          BR 的挑戰(zhàn)和優(yōu)化

          通過以上的基本流程,BR 的基本鏈路已經(jīng)可以跑通了:類似于算子下推,BR 將備份任務(wù)下推到了 TiKV,這樣可以合理利用 TiKV 的資源,實(shí)現(xiàn)分布式備份的效果。

          在這個(gè)過程中,我們遇到了許多挑戰(zhàn),在這一節(jié),我們來談?wù)勥@些挑戰(zhàn)。

          BackupMeta 和 OOM

          前文中提到,BackupMeta 儲(chǔ)存了備份的所有元信息:包括表結(jié)構(gòu)、所有備份文件的索引等等。想象一下你有一個(gè)足夠大的集群:比如說,十萬張表,總共可能有數(shù)十 TB 的數(shù)據(jù),每張表可能還有若干索引。

          如此最終可能產(chǎn)生數(shù)百萬的文件:在這個(gè)時(shí)候,BackupMeta 可能會(huì)達(dá)到數(shù) GB 之大;另一方面,由于 protocol buffer 的特性,我們可能不得不讀出整個(gè)文件才能將其序列化為 Go 語言的對(duì)象,由此峰值內(nèi)存占用又多一倍。在一些極端環(huán)境下,會(huì)存在 OOM 的可能性。

          為了緩解這個(gè)問題,我們?cè)O(shè)計(jì)了一種分層的 BackupMeta 格式,簡單來講,就是將 BackupMeta 拆分成索引文件和數(shù)據(jù)文件兩部分,類似于 B+ 樹的結(jié)構(gòu):
          具體來講,我們會(huì)在 BackupMeta 中加上這些 Fields,分別指向?qū)?yīng)的 “B+ 樹” 的根節(jié)點(diǎn):
          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 就是這顆 “B+ 樹” 的節(jié)點(diǎn):
          //?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;
          }
          它可能有兩種形態(tài):一是承載著對(duì)應(yīng)數(shù)據(jù)的 “葉子節(jié)點(diǎn)”(后四個(gè) field 被填上相應(yīng)的數(shù)據(jù)),也可以通過?meta_files將自身指向下一個(gè)節(jié)點(diǎn):File是一個(gè)到外部存儲(chǔ)中其他文件的引用,包含文件名等等基礎(chǔ)信息。

          目前的實(shí)現(xiàn)中,為了回避真正實(shí)現(xiàn)類似 B 樹的分裂、合并操作的復(fù)雜性,我們僅僅使用了一級(jí)索引,將的表結(jié)構(gòu)和文件的元數(shù)據(jù)分別存儲(chǔ)到一個(gè)個(gè) 128M 的小文件中,如此已經(jīng)足夠回避 BackupMeta 帶來的 OOM 問題了。

          GC, GC never changes

          在備份掃描的整個(gè)過程中,因?yàn)闀r(shí)間跨度較長,必然會(huì)受到 GC 的影響。

          不僅僅是 BR,別的生態(tài)工具也會(huì)遇到 GC 的問題:例如,TiCDC 需要增量掃描,如果初始版本已經(jīng)被 GC 掉,那么就無法同步一致的數(shù)據(jù)。

          過去我們的解決方案一般是讓用戶手動(dòng)調(diào)大 GC Lifetime,但是這往往會(huì)造成 “初見殺” 的效果:用戶開開心心備份,然后去做其他事情,幾個(gè)小時(shí)后發(fā)現(xiàn)備份因?yàn)?GC 而失敗了……

          這會(huì)非常影響用戶的心情:為了讓用戶能更加開心地使用各種生態(tài)工具,PD 提供了一個(gè)叫做 “Service GC Safepoint” 的功能。各個(gè)服務(wù)可以通過 PD 上的接口,設(shè)置一個(gè) “Safepoint”,TiDB 會(huì)保證在 Safepoint 指定的時(shí)間點(diǎn)之后,所有歷史版本都不會(huì)被 GC。為了防止 BR 在意外退出之后導(dǎo)致集群無法正常 GC,這個(gè) Safepoint 還會(huì)存在一個(gè) TTL:在指定時(shí)間之后若是沒有刷新,則 PD 會(huì)移除這個(gè) Service Safe Point。

          對(duì)于 BR 而言,只需要將這個(gè) Safepoint 設(shè)置為 Backup TS 即可,作為參考,這個(gè) Safepoint 會(huì)被命名為 “br-”,并且有五分鐘的 TTL。

          備份壓縮

          在全速備份的時(shí)候,備份的流量可能相當(dāng)大:具體可以看看開頭 “秀肌肉” 文章相關(guān)的部分。

          如果你愿意使用足夠多的核心去備份,那么可能很快就會(huì)到達(dá)網(wǎng)卡的瓶頸(例如,如果不經(jīng)壓縮,千兆網(wǎng)卡大約只需要 4 個(gè)核心就會(huì)被打滿。),為了避免網(wǎng)卡成為瓶頸,我們?cè)趥浞莸臅r(shí)候引入了壓縮。

          我們復(fù)用了 RocksDB Block Based Table Format 中提供的壓縮功能:默認(rèn)會(huì)使用 zstd 壓縮。壓縮會(huì)增大 CPU 的占用率,但是可以減少網(wǎng)卡的負(fù)載,在網(wǎng)卡成為瓶頸的時(shí)候,可以顯著提升備份的速度。

          限流與隔離

          為了減少對(duì)其他任務(wù)的影響,如前文所說,所有的備份請(qǐng)求都會(huì)在單獨(dú)的線程池中執(zhí)行。

          但是即便如此,如果備份消耗了太多的 CPU,不可避免地會(huì)對(duì)集群中其它負(fù)載造成影響:主要的原因是 BR 會(huì)占用大量 CPU,影響其它任務(wù)的調(diào)度;另一方面則是 BR 會(huì)大量讀盤,影響寫任務(wù)刷盤的速度。

          為了減少資源的使用,BR 提供了一個(gè)限流機(jī)制。當(dāng)用戶帶有--ratelimit參數(shù)啟動(dòng) BR 的時(shí)候,TiKV 側(cè)的第三步 “Save”,將會(huì)被限流,與此同時(shí)也會(huì)限制之前步驟的流量。


          這里需要注意一個(gè)點(diǎn):備份數(shù)據(jù)的大小往往會(huì)遠(yuǎn)遠(yuǎn)小于集群的實(shí)際空間占用。原因是備份只會(huì)備份單副本、單 MVCC 版本的數(shù)據(jù)。通過ratelimit限流施加于 Save 階段,因此是限制寫備份數(shù)據(jù)的速度。

          在 “服務(wù)端” 側(cè),也可以通過調(diào)節(jié)線程池的大小來限流,這個(gè)參數(shù)叫做?backup.num-thread,考慮到我們?cè)试S用戶側(cè)限流,它的默認(rèn)值非常高:是全部 CPU 的 75%。如果需要在服務(wù)側(cè)進(jìn)行更加徹底的限流,可以修改這個(gè)參數(shù)。作為參考,一塊 Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz CPU 每個(gè)核心大概每秒能生成 10M 經(jīng) zstd 壓縮的 SST 文件。

          總結(jié)

          通過 Service Safe Point,我們解決了手動(dòng)調(diào)節(jié) GC 帶來的 “難用” 的問題。

          通過新設(shè)計(jì)的 BackupMeta,我們解決了海量表場(chǎng)景的 OOM 問題。

          通過備份壓縮、限流等措施,我們讓 BR 對(duì)集群影響更小、速度更快(即便二者可能無法兼得)。

          總體上而言,BR 是在 “物理備份” 和 “邏輯備份” 之間的 “第三條路”:相對(duì)于 mydumper 或者 dumpling 等工具,它消解了 SQL 層的額外代價(jià);相對(duì)于在分布式系統(tǒng)中尋找物理層的一致性快照,它易于實(shí)現(xiàn)且更加靈巧。對(duì)于目前階段而言,是適宜于 TiDB 的容災(zāi)備份解決方案。


          ??Tip:上文劃線部分均有跳轉(zhuǎn),由于微信外鏈限制,大家可以點(diǎn)擊尾部【閱讀原文】查看原文

          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品麻豆日本三级 | 三级麻豆 | 成年人视频在线播放 | 高清无码视频 播放免费 | 国产成人女自拍 |