深入 HBase 18 - Metrics、hbtop、Compaction 与性能调优
HBase 性能问题很少只由一个参数造成。热点 Region、StoreFile 过多、compaction backlog、BlockCache 命中率下降、GC 停顿、HDFS locality 变差,都会表现成读写延迟上升。调优的第一步不是改配置,而是把症状映射到可观察对象。
本篇只抓一个核心问题:如何用 Metrics、hbtop、JMX 和服务端状态识别热点 Region、compaction backlog、缓存失配、GC 和磁盘瓶颈。
观察路径图
flowchart TD
Slow[读写变慢] --> Client[Client retry/timeout]
Slow --> RS[RegionServer metrics]
RS --> Hot[Region #REQ/S/#WRITE/S]
RS --> Cache[BlockCache hit/miss]
RS --> Store[#SF / StoreFile size]
RS --> Compaction[Compaction queue/progress]
RS --> GC[JVM GC pauses]
Store --> HDFS[Block locality / HDFS IO]
Compaction --> HDFS
这条路径把“慢”拆成五类问题。请求是否集中到少数 Region;读是否频繁穿透 BlockCache;StoreFile 数是否太多;compaction 是否追不上 flush;JVM 或 HDFS 是否成为瓶颈。每一类都有可观察指标。
官方 Metrics 文档说明,HBase 使用 Hadoop Metrics API,默认带一组 metrics 配置,可通过 JMX、HTTP /jmx、/metrics 或 Prometheus 相关方式接入监控。官方 hbtop 文档提供交互式视图,支持 Region、Namespace、Table、RegionServer、User、Client 等模式。
hbtop 读法
hbtop 是快速定位的入口,不是长期监控替代品。它每隔一段时间刷新,把 Region、表、namespace、RegionServer 等维度的请求和存储状态排出来。
1 | |
Region 模式里常用字段包括:
| 字段 | 含义 | 常见判断 |
|---|---|---|
#REQ/S |
每秒请求数 | 是否有热点 Region |
#READ/S |
每秒读请求 | 读热点 |
#WRITE/S |
每秒写请求 | 写热点 |
#SF |
StoreFile 数 | 读放大和 compaction 压力 |
MEMSTORE |
MemStore 大小 | flush 压力 |
LOCALITY |
Block locality | 读是否跨节点 |
%COMP |
compaction 进度 | 后台合并是否长期占用 |
热点 Region 的信号通常是少数 Region 的 #REQ/S 或 #WRITE/S 远高于同表其他 Region。原因常见于 row key 前缀单调递增、salt 桶数不足、时间桶粒度过粗、预分区缺失。处理方式回到第 03 篇的 row key 设计,而不是先调 RegionServer 线程数。
StoreFile 数过多会增加读放大。一个 Get 可能要在 MemStore 和多个 HFile 中合并可见版本;Bloom Filter 和 BlockCache 能降低代价,但不能把多个 StoreFile 的元数据、索引和 seek 成本消掉。
LOCALITY 降低通常意味着 Region 迁移或 HDFS block 放置和 RegionServer 不再同节点。HBase 仍能读,只是读路径可能跨网络。重启、balance、Region move 之后要观察 locality 逐步恢复。
Metrics 和 JMX
RegionServer 的 HTTP UI 暴露 /jmx 和 /metrics 等监控端点。Metrics2 的常见接法是把 conf/hadoop-metrics2-hbase.properties 配成对应 sink,然后由 Prometheus、Ganglia 或其他系统采集。
1 | |
1 | |
这两条命令的端口和路径依部署而定,当前本地没有运行的 RegionServer,标记为 UNVERIFIED_RUNTIME。文章不提供固定 JSON 输出,因为不同版本、sink 和开关会改变字段集合。
Metrics 的正确用法是做关联判断:
| 症状 | 指标组合 | 初步方向 |
|---|---|---|
| 点查变慢 | BlockCache miss 上升 + #SF 高 |
cache 失配或读放大 |
| 写入变慢 | MemStore 接近阈值 + flush queue 增长 | flush 压力 |
| 后台 IO 高 | compaction queue 高 + HDFS 写吞吐高 | compaction backlog |
| 局部超时 | 单 Region #REQ/S 高 |
row key 热点 |
| 全局抖动 | GC pause 上升 + handler backlog | JVM 或内存压力 |
单个指标很容易误导。BlockCache hit 下降可能是 scan 把缓存污染了,也可能是工作集变大,还可能是 Region 刚迁移导致冷 cache。必须同时看访问模式、Region 分布和 StoreFile 状态。
Compaction backlog
Compaction 是 HBase LSM 路径的后台成本。写入先进入 WAL 和 MemStore,flush 生成 HFile;多个 HFile 再通过 minor 或 major compaction 合并。写入压力大于 compaction 能力时,StoreFile 数会增长,读放大会变高,最终可能触发写入限流。
诊断 compaction backlog 需要看三类信号。
第一,#SF 持续增长。单个 Store 的 StoreFile 数越多,读路径要检查的文件越多。Bloom Filter 可以避免一部分无关文件,但不能消除所有读放大。
第二,compaction queue 长期不下降。短时间队列上升是正常后台行为,长期积压说明磁盘、线程或策略跟不上。
第三,HDFS 和磁盘写压力升高。Compaction 读旧 HFile、写新 HFile,再删除旧文件;它会制造额外 I/O。把 compaction 线程简单调大,可能只是把磁盘打满。
处理顺序通常是:先判断热点是否导致单 Region StoreFile 暴涨,再判断 flush 太频繁是否来自 MemStore 配置或写入突刺,最后再调 compaction 相关线程和阈值。顺序反过来会把架构问题掩盖成参数问题。
Scan 和缓存污染
第 14 篇讲过 Scan#setCacheBlocks(false) 的边界。大范围一次性 scan 如果默认进入 BlockCache,会把热点点查需要的数据挤出去。症状是 scan 期间或之后点查延迟上升,BlockCache miss 增加。
这类问题不该先扩大 cache。更小的改动是按访问模式设置 scan 参数:一次性批处理 scan 关闭 block cache;交互式范围查询保留合理 caching;热点点查保留缓存。访问模式不同,缓存策略也要不同。
这也是 HBase 宽列模型的直接后果。列族是物理存储边界,BlockCache 和 HFile 都按列族组织。冷热数据混进同一列族,会让 scan 和点查互相污染。
最小实验
以下实验需要 HBase 2.6.6 集群,当前本地不可执行,标记为 UNVERIFIED_RUNTIME。实验先建立一张表,再用单调 row key 写入制造热点。
1 | |
Shell 内创建 namespace 和表:
1 | |
启动连续写入程序后打开 hbtop,观察请求是否集中到一个 key range。
1 | |
观察 Region 模式下 #WRITE/S 是否集中在最后一个 key range。再把 row key 改为 salt 前缀,重新写入同等规模数据,比较 Region 维度分布是否更均匀。
制造 scan 缓存污染时,先记录点查 BlockCache 指标,再运行一次大范围 scan。Java 代码应显式区分两种 scan:
1 | |
这段 API 形状按 HBase 2.6 public API 核对,未在本地编译,标记为 UNVERIFIED_RUNTIME。
失败恢复
性能调优失败常见于“改了参数但没有回看指标”。每次变更都应绑定一个观察窗口:变更前指标快照、变更项、变更后同一批指标。没有快照,就不能判断变更是否有效。
热点 Region 的恢复通常要改 row key 或做预分区。临时 move Region 只能改变承载节点,不能改变 key 分布。写入仍然集中到一个 Region 时,新节点也会继续热。
Compaction backlog 的恢复要防止把磁盘压垮。增加 compaction 线程前先看磁盘利用率、HDFS 写吞吐和 RegionServer GC。磁盘已满载时,更多线程只会制造更长队列。
BlockCache 失配的恢复要回到访问模式。把所有 scan 都设成大 caching,或把所有 scan 都关闭 cache,都会伤害另一类请求。按任务类型区分参数更稳。
工程迁移
| HBase 症状 | 迁移到其他系统的同类问题 | 处理模式 |
|---|---|---|
| 热点 Region | 分片数据库热点 shard | 改 key 分布 |
| StoreFile 过多 | LSM SSTable 过多 | 控制后台 merge |
| BlockCache miss | 应用缓存命中率下降 | 分离冷热访问 |
| GC pause | JVM 服务抖动 | 限制对象分配和堆压力 |
| locality 降低 | 计算存储错位 | 重新放置或预热 |
模式提炼:先把慢请求映射到对象,再把对象映射到成本。Region 是负载对象,StoreFile 是读放大对象,MemStore 是写缓冲对象,BlockCache 是读缓存对象,compaction 是后台 I/O 对象。
常见误解
误解一:HBase 慢了就调大线程。线程只是执行资源,热点、读放大和磁盘瓶颈不会因为线程变多而消失。
误解二:BlockCache 越大越好。缓存如果被大 scan 污染,扩大缓存只能推迟问题。
误解三:Major compaction 是常规提速按钮。Major compaction 会产生大量 I/O,应作为受控维护操作,不应在业务高峰随意触发。
误解四:Region move 能解决 row key 热点。move 只能换机器,不能改变数据分布。
练习
- 给一张写热点表画出排查顺序:hbtop Region 模式、
hbase:metakey range、row key 样本、预分区方案。 - 设计一个 scan 缓存污染实验,列出运行前后必须采集的 BlockCache 指标。
- 把 compaction backlog 和 GC pause 同时出现的场景拆成两个独立假设,分别写出验证指标。
系列导航
- 深入 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 Metrics & Monitoring:https://hbase.apache.org/docs/operational-management/metrics-and-monitoring/
- Apache HBase hbtop:https://hbase.apache.org/docs/hbtop/
- Apache HBase Performance Tuning:https://hbase.apache.org/docs/performance/
- Apache HBase RegionServer Architecture:https://hbase.apache.org/docs/architecture/regionserver/
证据等级:指标入口、hbtop 字段和诊断边界为 VERIFIED_SOURCE;本地未运行 HBase 集群,实验为 UNVERIFIED_RUNTIME。
