Why: 为什么要 merge region
Merge-region 顾名思义就是合并 region, 主要是指将数据范围相邻小 region 合并起来,以减少 region 的总个数。之所以要 merge-region, 主要是以下两个原因:
- Region 信息维护需要消耗资源:当小 region 数量太多时,维护 region 的元数据信息、raft-group 心跳等都需要额外的资源开销,而这些开销累计到一定程度就会对我们集群产生性能影响。
- Region 是大部分 TIKV 读写请求的基本单位,因此 region 太小太散会直接导致 到 TiKV 的读写请求变得零散,导致请求无法批量化处理。如一个 100 M 的 region 被切分成了 100 个 1 M 的 region, 那么读取这一个 region 的数据就需要发送 100 个请求给 KV。
When: 什么情况下需要 merge-region
-
删除了大量数据
- 批量连续空间删除:truncate/drop table/partition 删除了大量数据并过 GC lifetime 后,会产生大量的 empty region
- 零散空间删除:delete/update/insert 下每行数据 mvcc 版本较多时,在过了 gc lifetime 并完成 compact 后,会产生 undersized-region/empty-region, 也是需要 merge 的
-
用户觉得集群 region 数量太多而突然调大了 region size ,此时会在短时间内产生很多 undersized-region 并开始 merge:
- 调大 region-size 方式
//set region-split-size(online config)
set config tikv `coprocessor.region-max-size` = '256MiB';
set config tikv `coprocessor.region-max-keys` = '1440000';
// set merge size limit
set config pd `schedule.max-merge-region-size`=20;
set config tikv `schedule.max-merge-region-keys` = '1440000';
如何查看 merge-region 的进度
在 pd->region-health 面板上看到以下两个指标时,需要特别关注:
empty-region-count
: region size <= 1 Mundersized-region-count
: region size <=schedule.max-merge-region-size
且 region keys <=schedule.max-merge-region-keys
, 也就是符合 merge 条件的 region .
How: 如何加速 region merge
PD 作为集群的大脑,掌控着整个集群的健康度,其中一项重要的任务便是 region 的健康度。当发现 region 出现异常时,如我们发现了很多 empty 或者 undersized 的 region, 这些 region 在比较多时,会对 region 元数据维护这一块产生很大的压力,从而影响集群整体性能。 因此 PD 在检测到这类异常后,就会发起相应的调度操作,指导 TiKV 中的 region 去完成自我修复功能。对于小 region 来说,PD 会给出 merge region 的指令,指导 TiKV 中对应的 region 去完成 region merge 的过程。
因此,加速 region merge ,主要是要加速以下两个事情:
- PD 侧 region-merge operator 生成的速度
- TiKV 侧 region-merge operator 消费的速度
加速 PD 侧 merge-region operator 的生成速度
Merge region 是在 PD 做 region 健康度检查 时的最后一步进行, 如果 region 在前面的检测过程中没有遇到任何异常,最后就会走到 merge 这一步检查。主要涉及的参数有:
-
split-merge-interval 对于刚 split 出来的 region, 或者集群刚启动时,需要等待这个时间完成才会开始 merge
-
Merge-scheduler-limit: 8 by default. Check pd->operator->Schedulers reach limit
// update merge-scheduler-limit tiup ctl:v7.5.2 pd config set merge-schedule-limit 64 Starting component ctl: /home/tidb/.tiup/components/ctl/v7.5.2/ctl pd config set merge-schedule-limit 64 Success! tidb@172-16-120-219:~$ tiup ctl:v7.5.2 pd config show Starting component ctl: /home/tidb/.tiup/components/ctl/v7.5.2/ctl pd config show { "replication": { …… }, "schedule": { …… "max-merge-region-keys": 1000000, "max-merge-region-size": 100, "max-movable-hot-peer-size": 512, …… "merge-schedule-limit": 64, "patrol-region-interval": "10ms", …… } }
-
特别注意:Store-limit 不会对 merge 产生影响。
Merge-region operator 生成条件(MergeChecker.Check):
PD 在对 region 进行合并检查时,会检查一系列参数,当 merge-region operator 创建失败时,也会在 pd->scheduler->region merge checker 里面记录详情:
有了该监控后,我们就可以很快找到 merge-region operator 没有生成的原因,再展开下一步诊断。下面我们按照merge region 在这一过程中的检查顺序对图中的指标展开详细介绍:
-
paused
: merge_checker 已经禁用 -
recently-start
: 最近启动的 split-merge-interval(default 1h) 内,默认该 region 刚分裂过,不会被合并 -
recently-split
:最近 split-merge-interval(default 1h) 内刚分裂出来,不会被合并 -
skip-uninit-region
: region 没有 leader, 一般发生在 pd 刚启动,需要从底层存储或者 tikv 上报该 region 的心跳才能知道 leader -
no-need
: region 比较大,不需要合并。检查依据:max-merge-region-size
和max-merge-region-keys
都要符合条件才行 -
special-peer
: region 有 down 或者 pending 副本 -
abnormal-replica
: region 有异常,比如缺副本或者副本没有按照 placement-rule 放置 -
hot-region
: 热点 region 不 merge -
no-target
: 找不到可以跟它合并的 region-
adj-not-exist
: 找不到相邻的 region -
adj-recently-split
: 相邻 region 最近刚 split 过 -
adj-region-hot
: 相邻 region 有热点 -
adj-disallow-merge
: 相邻 region 不允许合并,原因可能如下:- 两个 region placement rule 不一样
- 其中一个 region 被打上了
merge_option=deny
的 label enable-cross-table-merge
为 false, 且两个 region 属于不同表
-
adj-abnormal-peerstore
: 要合并的两个 region 在异常 store(正在 offline 或者 不见 了) 上都有副本 -
adj-special-peer
:相邻节点有 down 或 pending 的副本 -
adj-abnormal-replica
: 相邻 region 有异常,比如缺副本或者副本没有按照 placement-rule 放置
-
-
target-too-large
:合并后 region size 会变成超大 region, 比如大于 region-max-size *4 -
split-size-after-merge
: 合并后 size 大于 region-max-size,又能达到 split 的标准。也就是即使合并了也会很快被 split 开,就会拒绝合并。 -
split-keys-after-merge
: 同上,只是单位变成 keys. 也就是合并后很快会达到 split 条件而再次被切开。拒绝合并 -
larger-source
: source region 的 size 或者 key 个数要比合并的 region 多,只是标记一下,这个 operator 还是会生成。
PD 上生成 merge-region operator 的关键参数
- max-merge-region-size(20M by default):Controls the size limit of Region Merge. When the Region size is greater than the specified value, PD does not merge the Region with the adjacent Regions..
- max-merge-region-keys(20 million by default): Specifies the upper limit of the Region Merge key. When the Region key is greater than the specified value, the PD does not merge the Region with its adjacent Regions.
- split-merge-interval(1h by default): Controls the time interval between the split and merge operations on the same Region. That means a newly split Region will not be merged for a while.
- merge-schedule-limit(8 by default):The number of the Region Merge scheduling tasks performed at the same time. Set this parameter to 0 to disable Region Merge.
- enable-cross-table-merge(true by default):Determines whether to enable the merging of cross-table Regions
加速 merge-region 的消费速度
说到 merge-region 的消费速度,首先我们需要知道 region-merge 是怎么做的。
Region 本身是一个逻辑概念,作为 TiKV 数据存储的最小逻辑单位,它背后的数据副本是实打实的物理存储在 tikv 上。因此在做 region merge 的时候,我们首先会将要合并的 两个 region 副本数据及角色的拓扑结构调节到完全一致,最后再安全快速的进行逻辑上的合并。
下面我们以 region 2[B,C) 与 region3[C,D) 合并成 region2[B,D) 为例子,简要讲一下 region merge 的过程:
初始化状态下:
- Region 2 的数据范围为 [B,C), 三个副本分别在 store-1, store2,store-3 上,其中 leader 在 store-2 上
- Region 3 的数据范围为 [C,D), 三个副本分别在 store-1, store-2, store-4 上,其中 leader 在 store-4 上。
要合并 region 2 和 region 3 , 首先我们需要将两个 region 对应的副本搬迁到相同的 store 上,假设在这个阶段,我们会将 region-2 在 store-3 上的副本搬迁到 store-4 上。
现在,两个 region 分别在 store-1, store-2, store-4 上各有一个副本了,但是要进行合并,我们还需要这两个 region 的副本角色分布拓扑完全一致,因此,这里我们将 region-2 的 leader 迁移到 region-3 leader 所在的节点 store-4 上:
现在,region-2 和 region-3 的副本角色拓扑完全一致了,我们就可以安全地完成逻辑合并:
从 merge-region 的执行原理我们了解到,对于 merge-region 来说,merge 过程中需要将对应的 region 搬迁到相同的 store 上,因此 TiKV 副本搬迁相关的配置都会影响 merge-region 执行的速度。最消耗资源也是最容易成为瓶颈的也是这一块 add-learner,具体调优手段可以看本 KB 第二章 [Troubleshooting Guide]Deep dive into TiKV node scaling and common issues, 这边我们不详细展开。 关键配置:
online config |
default |
thread pool |
2 |
`snap-generator` |
|
100MB |
`snap-generator` |
|
32 |
`snap_handler`.snap_sender |
|
32 |
`snap_handler`.snap_sender |
通过 PD 日志查看特定 region 的 merge 进度
日志 example:
在 merge 特定 region 卡住时可参考该日志
// 创建出 merge region 的 operator: merge: region 512607784 to 513499180
/** steps:
add learner peer 612422577 on store 16910359,
promote learner peer 612422577 on store 16910359 to voter,
remove peer on store 16910361,
add learner peer 612422578 on store 266988190,
promote learner peer 612422578 on store 266988190 to voter,
remove peer on store 266988760,
add learner peer 612422579 on store 536155773,
promote learner peer 612422579 on store 536155773 to voter,
transfer leader from store 536156435 to store 8,
remove peer on store 536156435,
add learner peer 612422580 on store 142072119,
promote learner peer 612422580 on store 142072119 to voter,
remove peer on store 536156437,
add learner peer 612422576 on store 266988192,
promote learner peer 612422576 on store 266988192 to voter,
transfer leader from store 8 to store 16910359,
remove peer on store 8, merge region 512607784 into region 513499180])\
**/
// merge operator 在生成时,会在要 merge 的两个 region 上分别生成一个 operator,也就是成对创建的。
[2024/07/22 20:02:58.803 +08:00] [INFO] [operator_controller.go:424] ["add operator"] [region-id=512607784] [operator="\"merge-region {merge: region 512607784 to 513499180} (kind:leader,region,merge, region:512607784(244793,40956), createAt:2024-07-22 20:02:58.803075332 +0800 CST m=+27423622.512140371, startAt:0001-01-01 00:00:00 +0000 UTC, currentStep:0, steps:[add learner peer 612422577 on store 16910359, promote learner peer 612422577 on store 16910359 to voter, remove peer on store 16910361, add learner peer 612422578 on store 266988190, promote learner peer 612422578 on store 266988190 to voter, remove peer on store 266988760, add learner peer 612422579 on store 536155773, promote learner peer 612422579 on store 536155773 to voter, transfer leader from store 536156435 to store 8, remove peer on store 536156435, add learner peer 612422580 on store 142072119, promote learner peer 612422580 on store 142072119 to voter, remove peer on store 536156437, add learner peer 612422576 on store 266988192, promote learner peer 612422576 on store 266988192 to voter, transfer leader from store 8 to store 16910359, remove peer on store 8, merge region 512607784 into region 513499180])\""] ["additional info"=]
[2024/07/22 20:02:58.803 +08:00] [INFO] [operator_controller.go:424] ["add operator"] [region-id=513499180] [operator="\"merge-region {merge: region 512607784 to 513499180} (kind:leader,region,merge, region:513499180(244859,40965), createAt:2024-07-22 20:02:58.803076494 +0800 CST m=+27423622.512141533, startAt:0001-01-01 00:00:00 +0000 UTC, currentStep:0, steps:[merge region 512607784 into region 513499180])\""] ["additional info"=]
[2024/07/22 20:02:58.804 +08:00] [INFO] [operator_controller.go:620] ["send schedule command"] [region-id=513499180] [step="merge region 512607784 into region 513499180"] [source=create]
[2024/07/22 20:03:04.264 +08:00] [INFO] [operator_controller.go:620] ["send schedule command"] [region-id=513499180] [step="merge region 512607784 into region 513499180"] [source="active push"]
[2024/07/22 20:03:09.267 +08:00] [INFO] [operator_controller.go:620] ["send schedule command"] [region-id=513499180] [step="merge region 512607784 into region 513499180"] [source="active push"]
[2024/07/22 20:03:14.764 +08:00] [INFO] [operator_controller.go:620] ["send schedule command"] [region-id=513499180] [step="merge region 512607784 into region 513499180"] [source="active push"]
[2024/07/22 20:03:16.293 +08:00] [INFO] [operator_controller.go:620] ["send schedule command"] [region-id=512607784] [step="merge region 512607784 into region 513499180"] [source=heartbeat]
[2024/07/22 20:03:16.388 +08:00] [INFO] [cluster.go:545] ["region Version changed"] [region-id=513499180] [detail="StartKey Changed:{7480000000000827FF7C5F7280000000C6FF7CFD9E0000000000FA} -> {7480000000000827FF7C5F7280000000C5FF5284990000000000FA}, EndKey:{7480000000000827FF7C5F7280000000C7FF8376770000000000FA}"] [old-version=244859] [new-version=244860]
[2024/07/22 20:03:16.407 +08:00] [INFO] [operator_controller.go:537] ["operator finish"] [region-id=513499180] [takes=17.603823476s] [operator="\"merge-region {merge: region 512607784 to 513499180} (kind:leader,region,merge, region:513499180(244859,40965), createAt:2024-07-22 20:02:58.803076494 +0800 CST m=+27423622.512141533, startAt:2024-07-22 20:02:58.803993629 +0800 CST m=+27423622.513058670, currentStep:1, steps:[merge region 512607784 into region 513499180]) finished\""] ["additional info"=]
// 正常现象,因为 region 512607784 已经被合并入 513499180,因此它上面的 operator 不需要处理了,这个 operator 会被清理掉。
[2024/07/22 20:03:19.768 +08:00] [WARN] [operator_controller.go:211] ["remove operator because region disappeared"] [region-id=512607784] [operator="merge-region {merge: region 512607784 to 513499180} (kind:leader,region,merge, region:512607784(244793,40956), createAt:2024-07-22 20:02:58.803075332 +0800 CST m=+27423622.512140371, startAt:2024-07-22 20:02:58.803798029 +0800 CST m=+27423622.512863076, currentStep:17, steps:[add learner peer 612422577 on store 16910359, promote learner peer 612422577 on store 16910359 to voter, remove peer on store 16910361, add learner peer 612422578 on store 266988190, promote learner peer 612422578 on store 266988190 to voter, remove peer on store 266988760, add learner peer 612422579 on store 536155773, promote learner peer 612422579 on store 536155773 to voter, transfer leader from store 536156435 to store 8, remove peer on store 536156435, add learner peer 612422580 on store 142072119, promote learner peer 612422580 on store 142072119 to voter, remove peer on store 536156437, add learner peer 612422576 on store 266988192, promote learner peer 612422576 on store 266988192 to voter, transfer leader from store 8 to store 16910359, remove peer on store 8, merge region 512607784 into region 513499180])"]
[2024/07/22 20:03:19.768 +08:00] [INFO] [operator_controller.go:572] ["operator canceled"] [region-id=512607784] [takes=20.964336678s] [operator="\"merge-region {merge: region 512607784 to 513499180} (kind:leader,region,merge, region:512607784(244793,40956), createAt:2024-07-22 20:02:58.803075332 +0800 CST m=+27423622.512140371, startAt:2024-07-22 20:02:58.803798029 +0800 CST m=+27423622.512863076, currentStep:17, steps:[add learner peer 612422577 on store 16910359, promote learner peer 612422577 on store 16910359 to voter, remove peer on store 16910361, add learner peer 612422578 on store 266988190, promote learner peer 612422578 on store 266988190 to voter, remove peer on store 266988760, add learner peer 612422579 on store 536155773, promote learner peer 612422579 on store 536155773 to voter, transfer leader from store 536156435 to store 8, remove peer on store 536156435, add learner peer 612422580 on store 142072119, promote learner peer 612422580 on store 142072119 to voter, remove peer on store 536156437, add learner peer 612422576 on store 266988192, promote learner peer 612422576 on store 266988192 to voter, transfer leader from store 8 to store 16910359, remove peer on store 8, merge region 512607784 into region 513499180])\""]
最近对 merge 优化的几个 issue
-
针对 empty region 等小 region 的副本搬迁性能得到显著提升 tikv#17408
- 针对小 region 的数据搬迁,对于小于
snap_min_ingest_size
(默认 2MB)的 region 搬迁,将由原来的 ingest 方式改成直接往 rocksdb 写入的方式。针对 empty region(<1M) 比较多的场景,会有显著的性能提升
- 针对小 region 的数据搬迁,对于小于
-
针对数据搬迁过程中 region-worker 因 delete-range 和 generate-snapshot 任务堆积导致搬迁卡住的问题在 tikv#12587 也得到了有效缓解。
常见问题及处理
Empty region 无法合并:Placement-rule 不同导致相邻 region 无法合并(符合预期)
-
现象:用户在开启了 enable-cross-table-merge 的情况下,设置了多个 placement rule, 比如 table-1、 table-2、table-3 分别设置了不同的 placement-rule, 其中:
- table 1 要求放在北京、上海
- table 2 要求放在杭州、山东
- Table 3 要求放在北京、上海
其中,table 1\table 2\table3 的 table-id 连续,也就是数据区域连续,也就是所在的 region 数据范围连续
此时,如果 table2 被 drop 掉后产生了空 1 个空 region, 这个空 region 是无法与 table1 或者 table3 的 region 合并的,这个时候就产生了空 region 一直无法被合并的情况。related KB
-
workaround: 将 table2 的 placement-rule 设置成与 table 1 或者 3 一样。
主从集群空 region 合并速度差异大
-
现象:当主从集群的 merge region 相关参数不一致时,可能导致两套集群合并速度相差很大。比如有用户将 merge region 的两个判断条件设得不一致:
- max-merge-region-size (主 20M,备 1 M),也就是说主集群只要 region 20 M 以内的就可以 merge, 但是备集群必须在 1M 内的 region 才可以 merge
- max-merge-region-keys (主 200K,备 1 K), 同理,主集群比备集群多了 1K-200K 个 key 的 region 可以 merge
-
workaround:将备集群的参数与主集群调整到一致