1.3 架构三层:HMaster、RegionServer 与 Region 寻址 本节摘要:HBase 集群由三类角色构成——ZooKeeper 提供协调与元数据入口、HMaster 管理元数据与 Region 分配、RegionServer 实际承载数据。客户端读写完全不经过 HMaster,而是走"ZooKeeper → Meta 表 → 目标 RegionServer"的三级寻址。本节把这条寻址链路完整走一遍。 三类角色各管什么 先给一张总览,再逐个拆。 图 1.3-1 HBase 架构与一次写请求的寻址路径 图 1.3-1 HBase 架构与一次写请求的寻址路径 ZooKeeper 是整个集群的"户籍所"。
本节摘要:HBase 集群由三类角色构成——ZooKeeper 提供协调与元数据入口、HMaster 管理元数据与 Region 分配、RegionServer 实际承载数据。客户端读写完全不经过 HMaster,而是走"ZooKeeper → Meta 表 → 目标 RegionServer"的三级寻址。本节把这条寻址链路完整走一遍。
先给一张总览,再逐个拆。

ZooKeeper 是整个集群的"户籍所"。它存着三个关键信息:/hbase/meta-region-server 节点记录 Meta 表在哪台 RegionServer 上;/hbase/master 参与主 Master 选举;每个 RegionServer 在 /hbase/rs 下建临时节点,会话断开节点自动消失——这就是故障检测的机制,Master 据此感知哪台机器死了。ZooKeeper 本身通常是 3 或 5 节点的独立小集群,HBase 对它的要求是低延迟而不是大容量。用 ZooKeeper 客户端连上能亲眼看到这些节点:
[zk: zk1:2181(CONNECTED) 0] ls /hbase [rs, meta-region-server, master, backup-masters, ...] [zk: zk1:2181(CONNECTED) 1] get /hbase/meta-region-server vm2,16020,1724055666
/hbase/rs 下每个子节点就是一台在线 RegionServer 的"户口页",名字是 主机名,端口,启动时间戳;进程一死,会话超时,节点消失——不需要任何人上报。
HMaster 是"管理面"。建表删表改列族、Region 的分配与迁移、RegionServer 宕机后的 WAL 回放切分、负载均衡的决策,都归它管。注意一个反直觉的事实:客户端的读写请求一封都不经过 Master。日常读写由客户端直连 RegionServer,所以 Master 短暂下线(比如主备切换的几十秒内)集群照样能读写,只是不能建表、分裂后的 Region 没人及时分配。Master 可配主备,靠 ZooKeeper 选举激活。
RegionServer 是"数据面",一台进程管几十到上千个 Region。每个 Region 在其内部按列族展开为 Store,每个 Store 由一个 MemStore(写缓冲)加若干 HFile 组成;RegionServer 还持有 WAL、BlockCache(读缓存)这些跨 Region 共享的部件。第 2 章会把 Store 内部拆开讲,这里先记住:Region 是 HBase 分布式的最小单位,RegionServer 是进程单位——一行数据落到哪台机器,等价于它的行键所在 Region 被分配给了哪台 RegionServer。
三类角色之下还有一个常被架构图一笔带过的参与者:HDFS。RegionServer 只是"管数据",不"存数据"——所有 HFile、WAL 都写在 HDFS 上,由 DataNode 持有实际数据块。这层分工有两个直接后果。
一是容灾的账本在 HDFS。RegionServer 宕机丢的是内存里的 MemStore,磁盘上的数据因为有三副本毫发无损,恢复只需回放 WAL。可以说 HBase 的可靠性策略是"进程可以随便死,数据在别人手里丢不了"。二是读写最终都会落到网络。RegionServer 与 HDFS DataNode 同机部署时,本地副本让读写走本地磁盘;但 Compaction、分裂这类重 IO 操作触发 HDFS 重新平衡副本后,本地性会暂时丧失,读请求要跨机取数(这个"本地性衰减"现象第 7 章扩容一节会实际遇到)。日常运维里"检查 HDFS 健康"永远排在 HBase 之前——HDFS 慢,HBase 一定慢,反之不然。
把三类角色放进同一个故障场景,能看清它们怎么咬合。假设 RegionServer-3 因 Full GC 停顿了三十秒:
/hbase/rs/rs-3 临时节点消失——死亡判定完成,全程没有心跳探测的轮询开销;注意第 3 步的微妙之处:客户端重试与 Master 恢复是并行的,靠 Meta 表这个共享状态协调——重试太早会再撞一次旧地址,太晚则白等。这正是 HBase 把寻址状态外置到一张普通表上的好处:任何一方都能读、Master 独占写,不需要额外的通知机制。
现在回答本册的核心问题:客户端怎么知道行键 o1001 的数据在哪台机器?整个流程分三步,代价是两次额外 RPC(有缓存兜底):
第一步,客户端连 ZooKeeper,读出 Meta 表的位置。第二步,去那台 RegionServer 查 Meta 表(表名 hbase:meta)——Meta 本身也是一张 HBase 表,行键是"表名,Region 起始行键,Region 创建时间戳",value 里记录每个 Region 的起始键、结束键与所在 RegionServer。第三步,拿着目标 Region 的地址直连过去读写。
客户端会把寻址结果缓存在本地,并监听失效信号。当目标 Region 发生分裂或迁移(第 3 章的主戏),客户端会收到 NotServerException 之类报错,于是重新走一遍 Meta 查询刷新缓存。集群规模不大时 Meta 表就一个 Region,由一台 RegionServer 独自服务——它是个潜在热点,但只有"寻址未命中"的请求才会打到它,稳态流量几乎为零。
⚠️ 常见坑:客户端机器的 DNS 或 /etc/hosts 配置不一致,导致拿到 RegionServer 的主机名后解析失败,报出一堆"Failed to get region location"。集群内主机名解析务必一致,这是新手部署的第一大坑。
把"谁管元数据"这个问题横向对比一下,能看出 HBase 的取舍:
| 方案 | 代表 | 特点 |
|---|---|---|
| 集中式命名节点 | HDFS NameNode | 元数据常驻内存,简单快速,但单点风险要靠 standby 解决 |
| 对等哈希环 | Cassandra | 无 Master,一致性哈希加 gossip 协议,运维去中心化但行为难预测 |
| 协调服务加专用元数据表 | HBase | Master 只管慢管理面,数据面寻址走 ZooKeeper 加 Meta 表,读写路径无单点 |
HBase 把最频繁的寻址放在 ZooKeeper(极轻量)和 Meta 表(可随集群扩大分裂)上,把低频的 DDL 与 Region 分配交给可主备切换的 Master——读写路径上没有任何一个组件是必经的单点。代价是三级寻址的延迟(首次约多两次 RPC)与 ZooKeeper 这个新依赖。
💡 关键直觉:HBase 的"Master 很闲"不是偷懒,是刻意把管理面从数据面剥离——你要排查读写问题时,几乎可以不管 Master;反过来 Master 打满 CPU 通常只影响 DDL 和均衡速度。
架构图有了,下一节把集群真正跑起来:装一个单机版 HBase,亲手写入第一行,并去 Web UI 里看看 Region 长什么样。