3.1 Region 寻址、分裂与落点迁移 本节摘要:Region 是 HBase 按行键区间切分的分布式与负载均衡单位。本节讲 Region 的精确定义、建表时的预分区、增长到阈值后的自动分裂过程、Master 的迁移与均衡,以及这一切对客户端"落点判断"的影响——行键不变,落点常变。 Region 到底是什么 把 1.2 与 1.3 的结论组装起来:一张表按行键字典序切成若干左闭右开的区间,每个区间就是一个 Region,形如 ;首尾 Region 的边界是负无穷与正无穷。Region 内按列族分 Store(2.2 节),Store 下是 MemStore 加一组 HFile。三个"单位"再对齐一次: 行键:数据的逻辑地址;
本节摘要:Region 是 HBase 按行键区间切分的分布式与负载均衡单位。本节讲 Region 的精确定义、建表时的预分区、增长到阈值后的自动分裂过程、Master 的迁移与均衡,以及这一切对客户端"落点判断"的影响——行键不变,落点常变。
把 1.2 与 1.3 的结论组装起来:一张表按行键字典序切成若干左闭右开的区间,每个区间就是一个 Region,形如 [起始键, 结束键);首尾 Region 的边界是负无穷与正无穷。Region 内按列族分 Store(2.2 节),Store 下是 MemStore 加一组 HFile。三个"单位"再对齐一次:
Meta 表里每个 Region 一行,记录区间边界与宿主机(1.3 节)。一行数据的落点 = 它的行键落在哪个区间 + 那个区间此刻挂在哪台 RegionServer。后半句是动态的,本节剩下的篇幅都在讲这个"动态"。
新表默认只有 1 个 Region,所有写入先挤进同一台机器,直到它大到分裂——冷启动阶段的热点不可避免,高峰建表甚至会直接把单节点打挂。工程上标准做法是建表即预分区:
hbase:040:0> create 'orders', {NAME => 'cf', VERSIONS => 5}, hbase:041:1* SPLITS => ['1000', '2000', '3000', '4000', '5000', '6000', '7000', '8000', '9000']
SPLITS 数组给出分界键,得到 10 个 Region,建表后被 Master 尽量摊到不同 RegionServer。用 list_region 'orders' 验证:
hbase:042:0> list_region 'orders' SERVER | REGION_NAME | START_KEY | END_KEY vm1 | orders,,1724... | | 1000 vm2 | orders,1000,1724.. | 1000 | 2000 vm3 | orders,2000,1724.. | 2000 | 3000 ... | ... 10 row(s)
分界键怎么选?答案是必须与 RowKey 设计联动(第 5 章的主战场):行键若以反转用户号打头,分界键就按反转后的哈希空间均匀切;行键若带时间戳前缀,切分界等于按时间切。先定 RowKey 再定预分区,顺序反了白干。
也有偷懒的均匀哈希法:行键第一位用 00–ff 的十六进制前缀(加盐,见 5.1 节),预分区分界取 10, 20, 30 ... f0,与数据分布天然对齐。
Region 增长到阈值就自动一分为二。相关参数(列族级):
| 参数 | 默认 | 含义 |
|---|---|---|
| hbase.hregion.max.filesize | 10 GB | Region 内最大 Store 文件达到即触发分裂 |
| hbase.regionserver.region.split.policy | IncreasingToUpperRegionSplitPolicy | 策略:随 Region 数增大提高触发门槛 |
分裂的过程(以 2.x 為例)分四步:
整个过程对客户端的表现是:分裂瞬间到 Meta 更新完成之间,请求可能打到旧地址,收到"Region 已迁移"类异常,客户端 SDK 自动重查 Meta 并重试——业务无感,但会有几毫秒到几十毫秒的毛刺。这就是 1.3 节埋的"寻址缓存失效重查"的触发场景之一。
⚠️ 常见坑:高峰期大量 Region 同时触发自动分裂(比如整点批量导入),毛刺叠加成抖动。生产上常见做法:关闭自动分裂,改在低峰定时手动 split(策略与利弊在 3.2 节 Compaction 一并讨论)。
分裂之外,Master 还有两个搬动 Region 的动机:
负载均衡。 Master 周期性(默认每 5 分钟)检查各 RegionServer 的 Region 数与负载,把最闲机器上的 Region 挪到最忙机器的对偶操作。迁移的过程是"先卸载(下线 Region、Flush MemStore)再在目标机打开",期间该 Region 短暂不可写。
人工调度。 运维命令:
hbase:043:0> move 'ENCODED => <region-hash>', 'vm3,16020,1724055' hbase:044:0> split 'orders,,1724055....', '5500' -- 指定分裂点手动分裂 hbase:045:0> merge_region 'hashA', 'hashB', true -- 合并两个相邻小 Region
新节点上线、热点治理、例行维护,都靠这三条命令。注意 move 的第二个参数是目标 RegionServer 的 服务器名,端口,启动时间戳 三段式,从 Web UI 复制最稳。
把 1.3 节寻址与本节内容合起来,行键 k 的落点可以写成:
// 伪代码:客户端视角的落点计算 RegionInfo r = metaCache.lookup("orders", k); // 1 字典序落在哪个区间 ServerName s = r.getServer(); // 2 该区间当前宿主 if (write(k).fails()) { // 3 失败说明缓存过期 metaCache.refresh("orders", k); // 重查 Meta retry(); // SDK 自动完成 }
同一个行键,昨天在 vm1、分裂后到 vm2、均衡后到 vm3——数据没动(HFile 还在 HDFS 原地),动的是"谁负责服务它"。Region 迁移搬的是服务责任与少量元数据,不是海量数据本身,这是 HBase 均衡操作秒级完成的原因。
💡 关键直觉:判断"这行数据在哪台机器"要过两道字典序比较:行键 vs Region 边界、Region vs 宿主机映射。第二道随时会变,所以一切落点结论都要加时间戳。
Region 边界讲完了。Region 内部的 StoreFile 却越积越多,3.2 节看 Compaction 怎么收拾这堆碎片。