简介:HBase(hbase-2.4.9-bin.tar.gz)是Apache Hadoop生态中面向列存储的分布式开源数据库,适合大数据开发、后端存储与NoSQL方向的学习者及运维人员,用于搭建非结构化数据的海量存储与实时读写环境。压缩包共2384个文件,约270.36MB,以1996个html文档、223个jar依赖包为主,辅以sh启动脚本、xml与properties配置、css/js静态资源及少量图片字体,完整覆盖运行、配置与文档查阅所需。目前已有5534人学习下载,热度较高。资源基于Google Bigtable论文思想实现,在HDFS之上提供类Bigtable能力,采用列族模式而非传统行式关系模型,适合非结构化数据存储场景。包内保留hbase-env、regionservers等配置样例与命令行脚本,便于读者直接部署单机或集群环境,对照官方文档理解RegionServer、Master等核心组件,快速完成从环境搭建到数据读写的实践入门。
1. HBase 2.4.9 单机与伪分布式落地:从 tar.gz 解包到第一个读写请求
手里拿到hbase-2.4.9-bin.tar.gz这个包,很多人第一反应是直接tar -zxvf解压完就start-hbase.sh,结果要么进程起不来,要么起来了hbase shell卡在create上不动。HBase 不是那种解压即用的组件,它依赖 JDK、依赖 ZooKeeper、依赖hbase-site.xml里那几个关键路径,任何一个没对齐,表现都是「进程在、服务不通」这种玄学状态。这篇笔记就围绕这个二进制包,把单机模式和伪分布式模式两条路走通,顺带把hbase安装与配置里最容易翻车的参数、hbase端口清单里必须放行的端口、以及后面pyspark写入hbase需要提前埋好的配置一次讲清楚。适合手上只有一台测试机、想先把 HBase 跑起来再谈集群的工程师,也适合被hbase面试题里「HBase 读写流程」问懵、想动手验证一遍的人。
2. 解包前后的环境对齐:JDK、主机名与目录规划
2.1 为什么 JDK 版本和 hostname 必须先定死
HBase 2.4.9 编译时对标的是 JDK 8,虽然 JDK 11 也能跑,但hbase-2.4.9-bin.tar.gz里带的 Hadoop 依赖和部分反射调用在 JDK 17 上会直接抛InaccessibleObjectException。我一般会在解压前先确认三件事:java -version输出是不是 1.8.x、hostname能不能被ping通、/etc/hosts里有没有把主机名映射到 127.0.0.1 或本机内网 IP。第三点最容易被忽略,HBase 的 RegionServer 注册用的是主机名而不是 IP,如果 hostname 解析不到,Master 起来了也看不到 RegionServer。
# 确认 JDK 版本,必须是 1.8 java -version # 输出应类似:java version "1.8.0_361" # 确认主机名可解析 hostname ping -c 1 $(hostname) # 如果 ping 不通,往 /etc/hosts 补一行(假设主机名是 hbase-test) echo "127.0.0.1 hbase-test" | sudo tee -a /etc/hosts上面这段的逻辑是:java -version决定后面能不能用,ping $(hostname)决定 HBase 内部通信能不能用,/etc/hosts那行是补救措施。参数上唯一要注意的是主机名不要带下划线,HBase 对主机名合法性有校验,带下划线会在启动时报Invalid hostname。
2.2 解压路径与软链接的取舍
hbase-2.4.9-bin.tar.gz解压出来是hbase-2.4.9目录,我习惯把它放到/opt或/data下,然后做一个不带版本号的软链接hbase。这样做的好处是后面写HBASE_HOME、改配置文件、升级版本时不用动环境变量,坏处是软链接指向要记清楚,排查问题时readlink -f一下确认实际路径。
# 解压到 /opt sudo tar -zxvf hbase-2.4.9-bin.tar.gz -C /opt # 建立软链接,后续统一用 /opt/hbase sudo ln -s /opt/hbase-2.4.9 /opt/hbase # 确认目录结构 ls /opt/hbase # 应看到 bin conf hbase-webapps lib logs 等目录解压后先别急着改配置,ls /opt/hbase/conf看一眼,里面应该有hbase-env.sh、hbase-site.xml、regionservers三个核心文件。hbase-env.sh管环境变量,hbase-site.xml管运行参数,regionservers管 RegionServer 列表。伪分布式下regionservers里写本机主机名即可,单机模式下这个文件其实用不到,但留着不影响。
2.3 环境变量与 HBASE_HOME 的写法
环境变量这块,HBASE_HOME指向软链接,PATH里加上$HBASE_HOME/bin,另外HBASE_LOG_DIR建议单独指一个目录,不然日志默认落在$HBASE_HOME/logs下,和安装目录混在一起,清理时容易误删。hbase-env.sh里还要显式指定JAVA_HOME,因为 HBase 启动脚本不会去读系统JAVA_HOME,它只认这个文件里的值。
# 编辑 /opt/hbase/conf/hbase-env.sh export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export HBASE_LOG_DIR=/data/hbase-logs export HBASE_MANAGES_ZK=trueHBASE_MANAGES_ZK=true是伪分布式和单机模式下最省事的选项,意思是让 HBase 自己拉起一个内置 ZooKeeper,不用额外装。生产环境当然要独立 ZooKeeper,但测试机上学hbase安装与配置,这个开关能省掉一半的排错时间。注意这个变量在hbase-env.sh里默认是注释掉的,要手动打开。
3. hbase-site.xml 关键参数:单机与伪分布式的分水岭
3.1 单机模式的最小配置
单机模式下 HBase 用本地文件系统,不依赖 HDFS,所有数据落在hbase.rootdir指定的本地目录。这个模式适合验证 API、跑单元测试,但不适合压测,因为本地文件系统没有副本机制,也没有 HDFS 的吞吐优势。配置上只需要改一个hbase.rootdir,其余保持默认。
<configuration> <property> <name>hbase.rootdir</name> <value>file:///data/hbase-data</value> </property> </configuration>这里file:///三个斜杠不能少,两个斜杠会被解析成相对路径,启动时会在当前目录下建一个data目录,后面找数据都找不到。/data/hbase-data这个目录不需要提前建,HBase 启动时会自己创建,但父目录/data要有写权限。
3.2 伪分布式模式的四个必调参数
伪分布式和单机的区别在于:数据存 HDFS、ZooKeeper 独立管理、进程按 Master/RegionServer 角色分开。配置上要加四个属性,少一个都会退化成单机或者起不来。
<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://localhost:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>localhost</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/data/zookeeper-data</value> </property> </configuration>hbase.rootdir指向 HDFS 的 NameNode 地址,端口要和core-site.xml里fs.defaultFS一致,常见做法是 9000 或 8020。hbase.cluster.distributed设为 true 才会以分布式模式启动,否则即使配了 HDFS 地址也走本地。hbase.zookeeper.quorum写 localhost 表示用本机 ZooKeeper,配合前面HBASE_MANAGES_ZK=true就是内置的。hbase.zookeeper.property.dataDir是 ZooKeeper 快照目录,默认在/tmp下,重启机器就丢,必须改到持久化路径。
3.3 端口清单与防火墙放行
HBase 用到的端口不少,伪分布式下至少涉及这几个:HMaster 的 RPC 端口 16000、Web UI 端口 16010,RegionServer 的 RPC 端口 16020、Web UI 端口 16030,ZooKeeper 客户端端口 2181。如果开了防火墙,这些端口要放行,否则hbase shell连不上 Master,报Connection refused。
| 组件 | 端口 | 用途 |
|---|---|---|
| HMaster | 16000 | RPC 通信 |
| HMaster | 16010 | Web UI |
| RegionServer | 16020 | RPC 通信 |
| RegionServer | 16030 | Web UI |
| ZooKeeper | 2181 | 客户端连接 |
# 以 firewalld 为例,放行上述端口 sudo firewall-cmd --permanent --add-port=16000/tcp sudo firewall-cmd --permanent --add-port=16010/tcp sudo firewall-cmd --permanent --add-port=16020/tcp sudo firewall-cmd --permanent --add-port=16030/tcp sudo firewall-cmd --permanent --add-port=2181/tcp sudo firewall-cmd --reload放行后可以用telnet localhost 16010验证 Web UI 端口通不通,通的话会看到 HTML 响应头。这一步在云主机上尤其重要,安全组和系统防火墙是两层,只放一层等于没放。
3.4 启动顺序与进程验证
配置改完,启动顺序是:先确认 HDFS 已启动(伪分布式依赖),再start-hbase.sh。启动脚本会依次拉起 ZooKeeper、Master、RegionServer,日志分别落在$HBASE_LOG_DIR下。启动后jps应该看到HMaster、HRegionServer、HQuorumPeer三个进程。
# 确认 HDFS 已启动 jps | grep -E "NameNode|DataNode" # 启动 HBase /opt/hbase/bin/start-hbase.sh # 验证进程 jps | grep -E "HMaster|HRegionServer|HQuorumPeer"如果jps只看到HMaster没有HRegionServer,八成是regionservers文件里主机名写错,或者 hostname 解析有问题。如果三个进程都没有,去看$HBASE_LOG_DIR/hbase-*-master-*.log,最常见的报错是java.net.BindException,说明端口被占,lsof -i:16000查一下谁占的。
4. 第一个读写请求:hbase shell 与 Java API 双路径
4.1 hbase shell 建表与写入
进程起来后,hbase shell是最快的验证方式。建表时要注意列族名和表名都是区分大小写的,create 'test', 'cf'里的cf是列族,后面所有列都挂在列族下。写入用put,读取用get和scan,这几个命令在hbase面试题里出现频率极高,但真正动手敲一遍和背答案完全是两回事。
# 进入 shell /opt/hbase/bin/hbase shell # 建表,指定列族 cf create 'test', 'cf' # 写入一行数据,rowkey 为 row1 put 'test', 'row1', 'cf:name', 'hbase-test' # 读取 get 'test', 'row1' # 扫描全表 scan 'test'put的第三个参数cf:name里,cf是列族,name是列限定符,两者用冒号分隔。get返回的结果里会带时间戳,这是 HBase 多版本特性的体现,默认保留一个版本,可以通过alter调整。scan不加限制会扫全表,数据量大时加LIMIT或STARTROW/STOPROW。
4.2 Java API 写入的最小可运行代码
shell 验证完,下一步是用 Java API 确认客户端依赖没问题。HBase 客户端依赖hbase-client和hbase-common两个 jar,版本要和服务端一致,2.4.9 的客户端连 2.4.x 服务端没问题,连 2.3.x 可能报VersionMismatch。
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.*; public class HBaseDemo { public static void main(String[] args) throws Exception { // 加载 hbase-site.xml,classpath 里要有 conf 目录 Configuration conf = HBaseConfiguration.create(); // 显式指定 ZooKeeper 地址,避免读不到配置 conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "2181"); try (Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("test"))) { // 构造 Put,rowkey 为 row2 Put put = new Put("row2".getBytes()); put.addColumn("cf".getBytes(), "name".getBytes(), "java-api".getBytes()); table.put(put); // Get 验证 Get get = new Get("row2".getBytes()); Result result = table.get(get); System.out.println(new String(result.getValue("cf".getBytes(), "name".getBytes()))); } } }这段代码的关键点是HBaseConfiguration.create()会去 classpath 找hbase-site.xml,如果找不到就用默认值,默认 ZooKeeper 地址是 localhost:2181,恰好和伪分布式一致,所以能跑通。但生产环境必须把hbase-site.xml打进 classpath,否则连错集群都不知道。Connection是重量级对象,要复用,不要每次操作都新建,Table是轻量级的,可以随用随取。
4.3 pyspark 写入 HBase 的前置配置
pyspark写入hbase是热词里出现频率很高的场景,核心依赖hbase-spark这个 connector。2.4.9 对应的 connector 版本是hbase-spark-2.4.9,需要在spark-submit时通过--jars引入,同时把hbase-site.xml放到 Spark 的 classpath 里。写入时用HBaseContext.bulkPut比逐条put快一个数量级,因为走的是 RegionServer 的批量接口。
from pyspark import SparkContext from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("pyspark-hbase") \ .getOrCreate() # 假设 df 是要写入的 DataFrame,包含 rowkey 和 value 两列 def write_to_hbase(iterator): import happybase conn = happybase.Connection('localhost', port=9090) table = conn.table('test') for row in iterator: table.put(row['rowkey'], {'cf:name': row['value']}) df.foreachPartition(write_to_hbase)这里用的是happybase走 Thrift 接口,需要额外启动hbase thrift start,默认端口 9090。另一种方式是直接用hbase-spark的HBaseContext,不依赖 Thrift,但需要 Scala 环境。两种方式各有取舍,Thrift 方式对 Python 友好,但多一层网络开销;HBaseContext性能好,但配置复杂。测试阶段我一般先用 Thrift 跑通逻辑,再换HBaseContext压性能。
5. 避坑与排查:那些让 HBase 起不来的细节
5.1 现象:start-hbase.sh 后 jps 没有 HMaster
原因通常是hbase-env.sh里JAVA_HOME没配或配错,启动脚本找不到 java 命令,进程静默退出。解决方法是echo $JAVA_HOME确认路径存在,然后bash -x /opt/hbase/bin/start-hbase.sh看执行到哪一步断的。另一个可能是HBASE_LOG_DIR目录没有写权限,日志写不进去,进程也会退。
5.2 现象:hbase shell 卡在 create 不动
这是典型的 ZooKeeper 连接问题。hbase.zookeeper.quorum配的地址和实际 ZooKeeper 监听地址不一致,或者 2181 端口被防火墙挡了。先在 Master 节点telnet localhost 2181确认端口通,再看hbase-site.xml里 quorum 是不是写成了 IP 而 ZooKeeper 只监听主机名。HBASE_MANAGES_ZK=true时 ZooKeeper 由 HBase 拉起,如果这个开关没打开又没独立装 ZooKeeper,shell 就会一直重试。
5.3 现象:RegionServer 启动后马上挂掉
看$HBASE_LOG_DIR/hbase-*-regionserver-*.log,常见报错是hbase.rootdir指向的 HDFS 路径没权限。HBase 启动用户要对/hbase目录有写权限,hdfs dfs -chmod 777 /hbase能临时解决,但生产环境要按用户授权。另一个原因是hbase.zookeeper.property.dataDir指向的目录不存在且父目录没权限,ZooKeeper 起不来,RegionServer 跟着挂。
5.4 现象:Java API 报 NoClassDefFoundError
客户端 jar 没打全,hbase-client依赖hbase-common、hbase-protocol、hbase-shaded-*一系列包,手动加 jar 很容易漏。用 Maven 的话直接依赖hbase-client2.4.9,传递依赖会自动拉齐。如果用的是hbase-shaded-client,注意它把部分类重命名了,和普通客户端不能混用。
5.5 现象:pyspark 写入报 Thrift 连接超时
happybase.Connection默认连 localhost:9090,如果 Thrift 服务没启动或者端口不对,就会超时。先hbase thrift start确认服务在,再netstat -tlnp | grep 9090看监听地址。如果 Spark 是集群模式,localhost要换成 Thrift 服务所在节点的实际 IP,否则 executor 连的是自己那台机器。
6. 进阶技巧:用 hbase pe 和 UI 验证读写性能
跑通基本读写后,怎么确认这套 HBase 的性能边界?我一般用hbase pe这个自带工具,它能模拟读写负载,输出吞吐和延迟。命令格式是hbase pe <mode> <opts>,常用的是--nomapred单机模式、--rows指定行数、--size指定每行大小。
# 写 100 万行,每行 100 字节,10 个线程 /opt/hbase/bin/hbase pe --nomapred --rows=1000000 --size=100 --threads=10 randomWrite # 顺序读 /opt/hbase/bin/hbase pe --nomapred --rows=1000000 --size=100 --threads=10 sequentialReadrandomWrite测的是随机写吞吐,sequentialRead测顺序读,两者结果差异能反映 rowkey 设计是否合理。如果随机写吞吐远低于顺序写,说明 rowkey 分布不均,热点集中在少数 Region。这个工具的好处是不依赖外部压测框架,装完 HBase 就能用,适合快速摸底。
另一个验证手段是 Web UI。Master 的 16010 端口能看到 RegionServer 列表、表列表、请求数;RegionServer 的 16030 端口能看到 Region 分布、StoreFile 大小、MemStore 占用。我习惯在压测时开着 16030 页面刷新,观察 MemStore 是否频繁 flush,如果 flush 过于频繁,说明hbase.hregion.memstore.flush.size设小了,默认 128MB,测试环境可以调到 256MB 减少 flush 次数。
| 参数 | 默认值 | 测试环境建议 | 作用 |
|---|---|---|---|
| hbase.hregion.memstore.flush.size | 128MB | 256MB | 控制 MemStore 刷写阈值 |
| hbase.regionserver.handler.count | 30 | 60 | RPC 处理线程数 |
| hbase.hregion.max.filesize | 10GB | 5GB | Region 分裂阈值 |
最后说个我自己的习惯:每次改完hbase-site.xml,先stop-hbase.sh再start-hbase.sh,不要用restart脚本,因为部分参数不支持热加载,restart 有时会残留旧进程,导致新配置不生效。改配置前cp hbase-site.xml hbase-site.xml.bak,翻车了能快速回滚。这套流程在测试机上跑通后,再往集群迁移时,把hbase.rootdir换成 HDFS 集群地址、hbase.zookeeper.quorum换成独立 ZooKeeper 列表,其余配置基本可以照搬。希望帮到你。
本文还有配套的精品资源,点击获取