github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/docs/cn/2019-09-09-BR-key-rewrite-disscussion.md (about)

     1  Last updated: 2019-09-12
     2  
     3  ## BR 关于 Key rewrite 的讨论
     4  
     5  ### Key rewirte 结构
     6  
     7  Key Rewrite 的目的有二:
     8  
     9  1. 为 BR 提供修改 Table ID 的功能,以支持恢复到 Schema Version 不同的集群
    10  2. 为 Lightning 提供添加前缀的功能,省略 Lightning <-> Importer 之间重复的数据传输
    11  
    12  一个 BR 的 SST 可能包含多个 Tables,所以要支持多条 Rewrite Rules 同时生效。SST 可能来自非 TiDB 系统,所以 Importer 不应该有 Key 编码格式的假设(不一定是 t«tid»_ 开头)。
    13  
    14  给 Importer / TiKV 参考的 Key Rewrite 数据结构建议如下:
    15  
    16  ```protobuf
    17  message RewriteRule {
    18  	bytes old_prefix = 1;  // this can be empty for universal prefix insertion!
    19  	bytes new_prefix = 2;  // these are _not_ just an integer!
    20  }
    21  
    22  message RestoreRequest {
    23  	...
    24  	repeated RewriteRule rewrite_rules = N;
    25  	...
    26  }
    27  ```
    28  
    29  正向替代一个 Key:
    30  
    31  ```rust
    32  fn rewrite_key(rules: &[RewriteRule], key: &[u8]) -> Cow<[u8]> {
    33      for rule in rules {
    34          if key.starts_with(rule.old_prefix) {
    35              return Cow::Owned(rule.new_prefix + key[rule.old_prefix.len()..])
    36          }
    37      }
    38      Cow::Borrowed(key)
    39  }
    40  ```
    41  
    42  反向还原一个 Key:
    43  
    44  ```rust
    45  fn undo_rewrite_key(rules: &[RewriteRule], key: &[u8]) -> Cow<[u8]> {
    46      for rule in rules {
    47          if key.starts_with(rule.new_prefix) {
    48              return Cow::Owned(rule.old_prefix + key[rule.new_prefix.len()..])
    49          }
    50      }
    51      Cow::Borrowed(key)
    52  }
    53  ```
    54  
    55  ### Key Rewrite 对现在导入流程的影响
    56  
    57  现在 BR 的导入流程如下:
    58  1. 把 **KV 对**写入到 RocksDB 实例(“**SST File**”)来排序
    59  2. (Prepare) 在 client 侧遍历此 SST file,
    60     1. (Pre-split) 找到所有 *Range* 的 EndKey 和 rewrite rule 的 NewPrefix,依照这些 Key 执行 BatchSplit,如下。
    61     2. (GetRegion) 执行 *PD* 的 *get_region_info* 取得这些 Key 对应的 *Region* 信息。
    62     3. (Split) 把这个 *Region* 从这些 Key 处使用 *batch_split_region* 分裂若干部分。如果出 EpochNotMatch 或 NotLeader 的错会由 Split 重试若干次(这里是因为 batch 的缘故,如果直接交给 GetRegion 重试,我们必须得要重试整个 batch),如果还是不成功,则会交由 GetRegion 开始重试。
    63     4. (Scatter) 使用 *scatter_region* 将分裂后 *Range* 对应的 *Region* 打散。
    64  3. (Import) 按 Prepare 步获得的 *Ranges* 并行导入
    65     1. (GetRegion) 重新取得这个 *Range* 对应的 *Region*。如果现在横跨了多个 Regions,会按 *PD* 返回的信息分割 *Range* 之后重试。
    66     2. (Encode) 读取 **Engine file** 在这个 **Range** 的 **KV pairs**,然后编码成 *SST* 格式。
    67     3. (Upload) 把 *SST* 分别上传到 Peers (Leader + Followers)。
    68     4. (Ingest) 对 Leader (或第一个 Follower) 发送 ingest_sst 命令。
    69  
    70  > 因为 markdown 的缘故,我们在这里用**粗体**来表示 Rewrite 之前的,用*斜体*来表示 Rewrite 之后的。
    71  
    72  当执行了 Key Rewrite,上下游的 Key Range 就会不一致。我们使用**红色**来标示使用 Rewrite 前的 Keys 的对象、用*蓝色*标示 Rewrite 后的对象。其中不变的是:
    73  
    74  1. 源数据必然是 Rewrite 前的
    75  2. PD 和 Ingest 后的结果必然是 Rewrite 后的。
    76  
    77  我们看到 Rewrite 前后的 Keys 在各步骤交叉被使用。这里的问题是怎么选一个合适的位置去 Rewrite Keys 来为 BR 提取最大性能。
    78  
    79  ### 解决方案:Key rewrite before ingest in every replica
    80  
    81  <img src="../resources/solution3-of-key-rewrite.svg" alt="solution3-of-key-rewrite" width="500" />
    82  
    83  每个 Peer 独自在 ingest 之前进行 Key Rewrite。
    84  * 源数据读盘:(R+1)**N** (Split 前 in Importer、Key rewrite 前 in TiKV)
    85  * Importer 写盘:0
    86  * Importer 读盘:0
    87  * TiKV 写盘:R**N** (Key rewrite 后暂存记录新数据到 SST 文件 (?))
    88  * 网络传输:0
    89  * Key Rewrite 次数:R+1 (Pre-split + Raft-Ingest 前)