7.1 监控与调优:先看见,再动手 本节摘要:HBase 调优的第一原则是"没有指标不动手"。监控指标分四层——系统资源、JVM、HBase 内部、表与 Schema,层层往里收敛才能找到瓶颈。本节给出四层指标清单、Web UI 与 JMX 的现场观测方法、Prometheus 采集链路,以及一张"瓶颈位置到参数"的对应表,把调优从玄学变成查表。 一切从一条延迟曲线开始 假设某个周三下午,业务方在群里发来一句"订单查询变慢了"。这时候最忌讳两件事:一是立刻去改参数碰运气,二是从架构开始怀疑。正确的动作是打开监控,看那条查询延迟曲线——它什么时候开始抬升?抬升的同时还有什么曲线在动?
本节摘要:HBase 调优的第一原则是"没有指标不动手"。监控指标分四层——系统资源、JVM、HBase 内部、表与 Schema,层层往里收敛才能找到瓶颈。本节给出四层指标清单、Web UI 与 JMX 的现场观测方法、Prometheus 采集链路,以及一张"瓶颈位置到参数"的对应表,把调优从玄学变成查表。
假设某个周三下午,业务方在群里发来一句"订单查询变慢了"。这时候最忌讳两件事:一是立刻去改参数碰运气,二是从架构开始怀疑。正确的动作是打开监控,看那条查询延迟曲线——它什么时候开始抬升?抬升的同时还有什么曲线在动?
我经历过的一次真实排查大致是这样的:点查 P99 从 8 毫秒涨到 60 毫秒,同时段 Compaction 队列长度从 2 涨到 15,磁盘利用率从 55% 爬到 91%。三条曲线放在一起,指向就很清楚:磁盘快满了,Compaction 反复搬移数据,读请求在跟 Compaction 抢 IO。处理也不是调参,而是扩盘加清理过期数据。如果当时第一反应是"调大 BlockCache",只会把问题推后。
这个例子说明监控的用法:单个指标说明不了任何问题,一组同时间轴的曲线才有诊断力。所以先花力气把指标体系建全。
排查任何性能问题,我都按同一条路径走:先看最外层的系统资源,排除硬件瓶颈;再看 JVM,排除 GC 停顿;然后看 HBase 内部队列,定位写入或读取的哪一环堵了;最后落到表与 Schema 层,检查是不是数据分布本身有问题。
| 层 | 代表指标 | 异常时的一般结论 |
|---|---|---|
| 系统层 | CPU 利用率与 iowait、磁盘吞吐与队列、网络带宽、磁盘剩余空间 | iowait 高或磁盘队列长,先解决 IO 容量 |
| JVM 层 | 老年代占用、GC 次数与耗时、停顿时间分布 | 频繁 Full GC 或长停顿,查堆大小与缓存配置 |
| HBase 层 | RPC 队列长度、请求延迟 P99、Flush 队列、Compaction 队列、StoreFile 数、BlockCache 命中率、Region 数分布 | 队列堆积在哪一环,瓶颈就在哪一环 |
| 表设计层 | 单 Region 读写请求数、Region 大小分布、热点行键前缀 | 个别 Region 请求量是均值十倍以上即热点 |
第四层最贴近本册主线:一行数据的落点分布不均,最终就表现为个别 RegionServer 的请求曲线一枝独秀。第 5 章讲过的 RowKey 设计缺陷,在监控上的形态就是"某台机器的 Region 读写量远超其他机器"。

指标系统看趋势,现场定位还得靠即时工具。日常我常用三个入口。
**Master 的 Web UI(16010 端口)**看全局:Region Servers 页每台机器的 Region 数、请求量、StoreFile 大小是否均衡;Tables 页看单表的 Region 分布与请求热点。某个 Region 的请求数是同表兄弟的十倍,一眼就能看出来——这是热点问题最快的确认方式。
**RegionServer 的 Web UI(16030 端口)**看细节:某个 Region 的 BlockCache 命中率、StoreFile 个数与大小、正在进行的 Compaction 都在这一层。
Shell 的 status 与 list_region 做脚本化巡检:
hbase:090:0> status 'vm1,vm2,vm3' 3 live servers vm1:16020 requestsPerSecond=8120, numberOfOnlineRegions=152 vm2:16020 requestsPerSecond=980, numberOfOnlineRegions=88 vm3:16020 requestsPerSecond=1040, numberOfOnlineRegions=90 0 dead servers hbase:091:0> list_region 'orders' SERVER | REGION_NAME | START_KEY | END_KEY | REQ vm1 | orders,,1724055.... | | 0100 | 25310 vm2 | orders,0100,1724055.... | 0100 | 0200 | 1900 vm3 | orders,0200,1724055.... | 0200 | | 2050
vm1 上那个起始键为空的 Region 承载了两万五千多请求,是另外两个的十倍以上——这就是教科书式的热点:大量行键排在第一个分区内(回顾第 5 章,多半是行键没有打散)。
HBase 的全部内部指标都通过 JMX 暴露。RegionServer 的 JMX 端口可在配置里开启,用 JConsole 连上能看到按 RegionServer、按表、按 Region 三级聚合的 MBean。代码里也能直接拉取:
JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://vm1:10101/jmxrmi"); JMXConnector jmxc = JMXConnectorFactory.connect(url, null); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); ObjectName name = new ObjectName("Hadoop:service=HBase,name=RegionServer,sub=Server"); System.out.println(mbsc.getAttribute(name, "readRequestsCount")); System.out.println(mbsc.getAttribute(name, "writeRequestsCount")); System.out.println(mbsc.getAttribute(name, "storeFileSize")); jmxc.close();
但自写脚本难成体系,生产上通行的方案是 JMX Exporter 加 Prometheus 加 Grafana:每台 RegionServer 挂一个 JMX Exporter 进程(把 MBean 翻译成 Prometheus 指标格式),Prometheus 定时抓取存储,Grafana 出看板。采集配置只需在 Prometheus 的配置文件里加一段目标清单:
scrape_configs: - job_name: 'hbase' static_configs: - targets: ['vm1:9400', 'vm2:9400', 'vm3:9400']
看板上我必放的四条查询,用 PromQL 写出来是这样:
# 每台机器读写请求速率 rate(hbase_regionserver_write_requests_count[5m]) rate(hbase_regionserver_read_requests_count[5m]) # Flush 与 Compaction 队列长度(持续大于 10 就要介入) hbase_regionserver_flush_queue_length hbase_regionserver_compaction_queue_length # BlockCache 命中率 hbase_RegionServer_RegionServer_metrics_blockCacheHitRatio_percent
告警规则围绕前面说的三条红线设:P99 延迟、队列长度、单机 Region 数与磁盘水位。宁少勿滥——报警一个月没人理的看板,等于没有看板。
指标定位了瓶颈层,接下来才是动参数。下面这张表把常用参数按瓶颈归类,每项都注明对应的机制章节——调优参数不是咒语,每个都对应第 2、3 章里讲过的某个具体机制。
| 症状 | 优先动作 | 参数或手段 | 机制回顾 |
|---|---|---|---|
| 写入毛刺、Flush 队列堆积 | 加大 MemStore 总额度 | hbase.regionserver.global.memstore.size 调到 0.45 至 0.5 | 2.2 节水位线 |
| 单 Region 频繁 Flush | 提高单 Store 阈值 | hbase.hregion.memstore.flush.size 128 MB 起按需上调 | 2.2 节 |
| Region 太大分裂频繁 | 上调分区上限 | hbase.hregion.max.filesize 10 GB 起步 | 3.1 节分裂 |
| 读放大、StoreFile 碎片多 | 收紧合并触发 | hbase.hstore.compaction.min 从 3 调到 5 以上 | 3.2 节 |
| Major Compaction 干扰高峰 | 错峰并限流 | majorcompaction 周期调大 每表错开 | 3.2 节 |
| 随机点查读盘多 | 开布隆 ROW | 列族建表参数 BLOOMFILTER | 6.2 节 |
| 读缓存挤占堆 | 上堆外缓存 | BucketCache 相关参数组合 | 6.2 节 |
| RPC 排队但 CPU 不高 | 加处理线程 | hbase.regionserver.handler.count 按核数上调 | 4.1 节 |
两个真实教训值得写进来。其一是 handler 数:默认 30,曾有一台机器 CPU 只用了三成但请求排队,把 handler 加到 120 后吞吐翻倍——排队而 CPU 空闲,就该加并发。其二是 Compaction 带宽:默认不限速,一次大合并能把千兆网卡吃满,读写全部抖动;给它限到 50 MB 每秒后,合并变慢但业务曲线平稳——Compaction 是后台任务,永远别让它跟前台抢路。
JVM 层的通用姿势相对固定:RegionServer 用 G1 回收器,堆 16 到 32 GB 为宜(再大不如加机器),并把停顿目标设在百毫秒以内。相关设置在环境脚本里:
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS \ -Xms16g -Xmx16g -XX:+UseG1GC \ -XX:MaxGCPauseMillis=100 \ -Xlog:gc*:file=rs-gc.log:time,uptime:filecount=5,filesize=50m"
GC 日志务必长期保留,排查长停顿时它比任何指标都诚实——停顿发生时刻、哪个区在回收、回收了多久,一目了然。
⚠️ 常见坑:把 major compaction 直接禁用来消除周期性抖动。墓碑与过期版本从此无人清理,StoreFile 越积越多,读放大在几周后反噬。正确做法是错峰:把各表的 Major Compaction 时间打散到业务低峰(凌晨),并保留每周至少一次。
指标体系建好了,接下来处理变更类操作:机器要加减、数据要备份——这些动作直接改变数据的落点,是下一节的主题。