深入 HBase 16 - Coprocessor、Endpoint 与 Phoenix 边界
Coprocessor 把一部分逻辑放进 HBase 服务端进程执行。这个能力很强,也很危险:代码和 RegionServer 在同一个 JVM、同一套权限、同一条请求路径里运行。Endpoint 能做局部聚合和自定义 RPC,但它不是分布式 SQL 引擎;Phoenix 提供 SQL 层,但 Phoenix 和 HBase 的升级边界也不能混写。
本篇只抓一个核心问题:计算下推能做什么,何时会破坏隔离、升级和运维边界。
Coprocessor 路径图
flowchart TD
Client[HBase Client] --> RPC[RegionServer RPC]
RPC --> Observer[Observer hooks]
Observer --> Region[Region]
Region --> Store[Store/MemStore/HFile]
Client --> Endpoint[Endpoint service]
Endpoint --> Region
Region --> WAL[WAL]
Observer 是事件钩子。它可以在 Get、Put、Scan、flush、compaction、WAL 等事件前后执行代码。典型用途是权限检查、审计、轻量索引维护、请求改写。
Endpoint 是服务端 RPC 扩展。客户端显式调用 Endpoint,代码在承载相关 Region 的 RegionServer 上执行,返回聚合结果或自定义结果。典型用途是对一个表的多个 Region 做局部求和,再由客户端合并。
这两个能力都不改变 HBase 的核心存储模型。row key、Region、WAL、MemStore、HFile、BlockCache 仍然是读写路径的主体;Coprocessor 只是挂在路径上的扩展点。
关键对象与状态
| 对象 | 作用 | 风险 |
|---|---|---|
RegionObserver |
拦截 Region 读写事件 | 影响主读写路径 |
MasterObserver |
拦截表、namespace、assignment 等事件 | 影响控制面 |
WALObserver |
拦截 WAL 相关事件 | 影响 durability 路径 |
CoprocessorService |
暴露 Endpoint RPC | 需要客户端显式调用 |
| Coprocessor JAR | 服务端执行代码 | 版本、依赖、权限都进入服务端 |
官方 Coprocessor 文档把它标成高级特性,并明确提醒:Coprocessor 直接运行在 RegionServer 上,能访问数据,错误代码可能破坏数据或稳定性;同时没有资源隔离。HBase Security Model 也说明,加载 coprocessor 等同授予服务器级访问,应当只用于受信任扩展。
状态变化可以按加载方式分成两类。
系统级 Coprocessor 通过 hbase-site.xml 加载。它随服务进程启动,作用于所有相关对象。卸载通常需要修改配置并重启。
表级 Coprocessor 通过表描述加载。它作用于单表,但加载本身是 schema change。生产环境必须用 ACL 限制谁能修改表描述,否则“能改 schema 的用户”就能把代码放进 RegionServer。
Observer 的适用边界
Observer 适合处理“请求经过这里时必须检查或补充”的逻辑。AccessController 就是典型例子:HBase ACL 是通过 coprocessor 实现的,读写请求进入 RegionServer 后先经过权限检查。
但 Observer 不适合承载重计算。一个 prePut 或 postGet 里做远程 HTTP、复杂 JSON 解析、大量内存分配,会直接拖慢 RegionServer 读写线程。没有资源隔离意味着一个坏 hook 可以把同 JVM 的正常 Region 一起拖住。
Observer 也不适合做跨行事务。它可以在一个 Put 经过时补写另一张表,但 HBase 不会因此提供跨行原子性。如果第二次写失败,主写入和副作用之间的补偿需要应用自己设计。
可迁移模式是“把强约束放在路径内,把重计算放在路径外”。权限检查、审计标记、幂等校验适合路径内;复杂派生数据、搜索索引重建、宽表回填适合异步作业或复制流。
Endpoint 不是查询引擎
Endpoint 的价值在于减少数据搬运。对一个跨很多 Region 的求和操作,客户端逐行 scan 会把大量 Cell 拉到客户端;Endpoint 可以让每个 RegionServer 在本地扫描并返回局部结果,客户端只合并小结果。
这并不等于 HBase 有了通用 SQL。Endpoint 没有统一优化器、没有成本模型、没有跨 Region join、没有快照隔离级别的查询计划,也不会自动维护二级索引。它是“服务端自定义 RPC”,不是“分布式关系查询层”。
HBase 2.x 的 Coprocessor 文档还提示一个升级风险:Table 上的同步 coprocessorService 方法已经 deprecated,建议使用 AsyncTable 的 coprocessorService。Endpoint 依赖 protobuf 和服务端扩展点,升级时必须重新核对 public API、依赖打包和运行时加载路径;具体大版本差异集中放到第 19 篇。
最小调用形状如下,API 名称按官方文档核对,当前本地无 HBase 依赖,标记为 UNVERIFIED_RUNTIME。
1 | |
这段代码不伪造业务 proto,也不把内部类当稳定 API。实际 Endpoint 示例应以项目自己的 .proto 和官方 2.6 API 为准。
Phoenix 的边界
Phoenix 在 HBase 之上提供 SQL 接口、二级索引和查询优化能力。它不是 HBase 内核的一部分,也不是把 HBase 变成关系数据库的开关。
Phoenix 需要和 HBase 版本匹配。它会依赖 HBase 的 client、server 端扩展和部分内部行为。HBase 升级时,Phoenix 兼容矩阵必须和 HBase release notes 一起检查;不能只看 HBase 服务端能启动就认为 SQL 层安全。
Phoenix 适合让已知模式的点查、范围查和有限聚合获得 SQL 入口。它不适合把 HBase 当 MySQL 替代品,尤其不适合依赖跨行事务、任意 join、强一致全局二级索引和高并发 OLTP 约束。
工程判断上,Phoenix 是上层访问模式,不是 HBase 存储语义本身。写 HBase 基础机制时应先解释 row key、Region 和 HFile,再解释 Phoenix 如何把 SQL 映射到这些对象。
最小实验
本地无 HBase 集群,实验为 UNVERIFIED_RUNTIME。目标是验证加载、调用和卸载三个状态,不给出固定输出。
创建实验表:
1 | |
Shell 内创建表:
1 | |
检查当前表描述:
1 | |
动态加载 coprocessor 前,必须确认 JAR 已在所有 RegionServer 可读的位置,通常是 HDFS 路径。加载动作是表 schema change,生产环境应先在测试集群验证。
1 | |
观察点包括表描述里的 coprocessor 属性、RegionServer 日志的加载异常、读写请求延迟、Coprocessor Metrics UI 或 JMX 里的调用耗时。
失败恢复
Coprocessor 加载失败时,首要动作是恢复表或服务端配置,而不是重试业务写入。表级 coprocessor 可以通过删除表属性卸载;系统级 coprocessor 通常要修改 hbase-site.xml 并重启服务。
错误 coprocessor 可能导致 RegionServer abort。生产环境应把三类保护放在前面:JAR 白名单、最小权限、上线前 MiniCluster 或测试集群验证。HBase 提供 CoprocessorWhitelistMasterObserver 用于限制可加载 JAR。
Endpoint 失败时要按 RPC 失败处理。客户端必须设置 timeout,限制结果大小,并对每个 Region 的异常给出降级策略。把 Endpoint 当作普通函数调用会隐藏 Region 局部失败。
Phoenix 失败恢复要按上层系统处理。索引异常、统计信息过期、查询计划变化都可能表现为 HBase 读写变慢,但根因在 SQL 层。排查时应先分开 HBase 表读写健康度和 Phoenix 查询计划。
工程迁移
| 需求 | 推荐边界 | 原因 |
|---|---|---|
| 权限、审计、可见性标签 | Observer | 必须在服务端路径内执行 |
| 局部聚合、减少网络搬运 | Endpoint | 计算靠近 Region |
| 复杂 ETL、索引重建 | 外部作业 | 避免阻塞 RegionServer |
| SQL 点查和有限范围查 | Phoenix | 让 SQL 映射到已设计好的 row key |
| 跨行事务和任意 join | 关系数据库或 NewSQL | HBase 内核不提供这类边界 |
模式提炼:扩展点越靠近数据路径,越要把逻辑写小、权限收紧、故障回滚路径写清。路径内代码换来低延迟,也拿走了隔离。
常见误解
误解一:Coprocessor 是安全的插件系统。官方安全模型说得很清楚,加载 coprocessor 等同给服务端级访问,必须信任代码。
误解二:Endpoint 能把 HBase 变成 SQL 引擎。Endpoint 只是自定义服务端 RPC;SQL、优化器和索引语义属于 Phoenix 这类上层系统。
误解三:Observer 可以补齐外键和跨行事务。Observer 可以写副作用,不能把 HBase 的行级原子性扩展成跨行事务。
误解四:Phoenix 兼容 HBase 就等于没有升级风险。Phoenix 的 server 端扩展和 HBase API 版本必须一起核查。
练习
- 把一个“写入时同步维护二级索引”的需求拆成 Observer、异步作业、Phoenix 三种方案,比较失败补偿路径。
- 设计 Endpoint 求和实验,列出每个 Region 局部失败时客户端应如何返回。
- 检查生产集群是否限制表级 coprocessor 加载权限,并确认是否启用 JAR 白名单。
系列导航
- 深入 HBase 00 - 导读:row key 决定数据位置
- 深入 HBase 01 - 数据模型:Cell、Column Family 与版本
- 深入 HBase 02 - 集群架构:HMaster、RegionServer、ZooKeeper 与 hbase:meta
- 深入 HBase 03 - Schema 与 row key 设计
- 深入 HBase 04 - 写入路径:WAL、MVCC、MemStore 与 flush
- 深入 HBase 05 - 读取路径:BlockCache、Bloom Filter 与 HFile block
- 深入 HBase 06 - HFile 内部结构
- 深入 HBase 07 - Compaction、TTL、版本与 Delete
- 深入 HBase 08 - Region 生命周期:split、merge 与 assignment
- 深入 HBase 09 - Client 路由与重试
- 深入 HBase 10 - 行级原子性与一致性边界
- 深入 HBase 11 - RegionServer 崩溃与 WAL 恢复
- 深入 HBase 12 - 跨集群 Replication
- 深入 HBase 13 - Snapshot、Backup 与恢复
- 深入 HBase 14 - Get、Scan、Filter 与分页
- 深入 HBase 15 - BufferedMutator 与 Bulk Load
- 深入 HBase 16 - Coprocessor、Endpoint 与 Phoenix 边界
- 深入 HBase 17 - Kerberos、RPC 保护与 ACL
- 深入 HBase 18 - Metrics、hbtop、Compaction 与性能调优
- 深入 HBase 19 - HBase 3.0 与设计边界
参考资料
- Apache HBase Coprocessors:https://hbase.apache.org/docs/cp/
- Apache HBase Security Model:https://hbase.apache.org/security-model/
- Apache HBase 2.6 User API:https://hbase.apache.org/2.6/apidocs/
- Apache Phoenix Overview:https://phoenix.apache.org/docs/
证据等级:Coprocessor、Endpoint 和安全边界为 VERIFIED_SOURCE;本地未加载 coprocessor,实验为 UNVERIFIED_RUNTIME。
