很多刚接触大数据平台的人,第一次看到的就是这样一个组合:Apache Hadoop、Hive、Hue、Spark、HBase。名字一个比一个熟,感觉自己都会,但真开始动手把五个组件装到同一个集群里,让它们跑起来、共享数据、互相协作,你会发现这事远不止“五个软件装了就行”。这篇文章没有废话,就是把我在实际环境中把这五件套整合在一起的完整过程写下来:每一个连接点怎么配、每个连接点容易怎么坏、坏了怎么查。适合正在搭离线数仓、准备做SparkSQL分析、又需要HBase承接实时读写的团队和开发者参考,也适合一个人一台虚拟机把整个链路先跑通的初学者。
1. 整体方案设计与架构定位
1.1 从“五件套”想清楚各自干什么
很多人上来就装Hive,装完之后发现Hive其实不会自己存数据。Hive只是在HDFS之上做了一层“翻译”:把SQL翻译成分布式作业,把表结构和文件目录对应起来。所以准确地说:Hive依赖Hadoop的HDFS来存数据,真正执行的默认计算可以走Hadoop的MapReduce,也可以走Spark。理解了这一点,整套架构的关系就开始清晰了。
把这五个组件放到一张图上,各干各的:
| 组件 | 核心职责 | 类比 |
|---|---|---|
| Hadoop(HDFS+YARN) | 分布式存储 + 资源调度 | 仓库和物业调度中心 |
| Hive | SQL语义层 + 统一元数据 | 翻译官和账本 |
| Spark | 内存计算引擎 | 高效的运输队 |
| HBase | 分布式列族数据库 | 快递柜,支持随机存取 |
| Hue | Web界面统一入口 | 监控室和操作台 |
为什么要强调各干各的?因为整合的本质就是让它们各司其职而不是越位。举个例子,HBase虽然挂在HDFS上,但它管理着自己目录下的文件,如果让Hive直接去扫描HBase的数据目录,完全读不出有效内容;反过来,SparkSQL去看Hive表很顺利,是因为它走了Hive Metastore这条“账本”通道。
1.2 整合的真正难点是4个连接点
安装单个组件,基本是一路下一步;整合的真正工作量,在于把“连接点”打通:
- Hive 和 HDFS 的连接:Hive 数据仓库的默认目录要落在HDFS上,Hive表本质是HDFS上的一串目录和文件。
- Spark 和 Hive 的连接:SparkSQL 需要通过 Hive Metastore 获取库表元数据,这样Spark里建的表Hive能查,Hive里建的表Spark能查。
- Hive 和 HBase 的连接:通过Hive的HBaseStorageHandler,让Hive可以读HBase的实时表,HBase也可以反过来通过Hive做复杂聚合。
- Hue 和所有组件的连接:Hue 本质是一个聚合网关,它要分别拿到各组件的地址、端口、认证方式,才能把文件浏览、SQL编辑、调度监控统一到一个浏览器页面。
这四个连接点中,第1点是底层,做不好后面全白搭;第2点是使用体验的分水岭,做不好SparkSQL就是个孤立工具;第3点是数仓接实时数据的关键;第4点是团队效率的加分项,但前面三条不通它就完全没意义。
1.3 为什么采用“共享资源池”而不是各起炉灶
有一种错误做法是:Hadoop归Hadoop跑,Spark单独开一个独立集群,HBase又用另外一组进程。结果就是内存互相挤,运维要盯三套监控,数据还要来回拷贝。我推荐的做法是让 Spark on YARN,也就是说 Spark 本身不常驻资源,只有提交任务时才向YARN申请容器。这样整个集群只有HDFS、YARN、Hive Metastore、HBase、Hue这几个常驻服务在跑,资源调度统一归YARN管,Spark任务即使写出几百个并发容器,跑完就释放,不会和HBase抢资源。
说得更直白一点:Hive和Spark都跑在YARN容器里,HBase的RegionServer独立常驻但也在同一批机器上。这样集成后的架构既满足离线批处理(Hive/Spark)、又满足实时随机读写(HBase),还只有一个运维入口(Hue),是最稳妥的中小型平台方案。
2. 环境准备与版本选型
2.1 版本选型是第一步,也是最大的坑
如果我在文章里只说一句“注意版本兼容”,你大概不会当回事,但实际工作中80%的整合失败都来自版本冲突,尤其是Spark和Hive之间、HBase和Hue之间。我的习惯是:不追新,追求通过社区验证的组合。
给一套我用过的、稳定运行过的参照组合:
| 组件 | 参照版本 | 选型理由 |
|---|---|---|
| Hadoop | 3.3.6 | Hadoop 3.x 生态成熟,YARN 能力比 2.x 好用 |
| Hive | 3.1.3 | 与 Spark 3.x 兼容性好,官方文档有明确支持 |
| Spark | 3.3.4 | SparkSQL 原生集成 Hive,提交到 YARN 稳定 |
| HBase | 2.4.17 | 支持 Hadoop 3 命名空间,RegionServer 稳定 |
| Hue | 4.11.0 | 集成 Hive、Spark、HBase 都有成熟模块 |
这里有一个关键认知:Hive 2.x 和 Spark 2.x、3.x 混搭很容易出问题,Hive 3.1.x 与 Spark 3.x 在元数据对接上比较顺。如果你们团队要用Spark 3.4或更高,请再去翻对应版本的兼容文档,不要拍脑袋。
其他依赖:JDK建议1.8,HBase 2.4和Hive 3.1对JDK8的支持最成熟;主节点额外准备一台MySQL/MariaDB,Hive Metastore的元数据要落到关系库里。
2.2 节点规划与资源估算
我习惯按三台虚拟机来规划,主机名就叫node01、node02、node03:
- node01:HDFS NameNode、YARN ResourceManager、Hive Metastore/HiveServer2、Hue
- node02:HDFS DataNode、YARN NodeManager、HBase RegionServer、Spark客户端
- node03:HDFS DataNode、YARN NodeManager、HBase RegionServer、Spark客户端
如果只有一台开发机,把所有服务堆到一台也不是不行,但内存至少16GB,否则HBase和YARN同时起来后很容易陷入OOM循环。
资源估算也简单:每个DataNode/NodeManager节点至少8GB内存,其中HBase RegionServer默认最大堆很大,要主动调小,否则留给容器内存就少了。一般我这样分:16GB的机器,NodeManager最大可用8GB,RegionServer给4GB,剩4GB给操作系统和HDFS进程。
2.3 基础依赖:hosts、SSH、JDK、MySQL
在动手装五件套之前,先把底层铺好:
- hosts:三台机器都要写 node01/node02/node03 的IP映射,不只写自己这台。很多连接失败问题,最后查出来是主机名解析不过去。
- SSH免密:至少保证node01能免密登录所有节点,启动脚本和后续启停Hadoop都要用。
- JDK 1.8:建议按官方要求配置JAVA_HOME,三台一致。
- MySQL:在node01上装好,给Hive建一个独立的库,比如库名hive_meta,账号hive。授权时不要只授权localhost,Metastore跑了thrift服务后是走网络连接的。
这里提醒一句:MySQL连接串里的字符编码,建议显式加上useUnicode=true&characterEncoding=UTF-8,不然后面Hive表注释里的中文容易乱码。
3. 核心集成配置:逐个击破五个组件的连接点
3.1 先把Hadoop装稳:YARN是后面所有计算的钥匙
先用两段能直接抄的配置说明核心。core-site.xml:
<property> <name>fs.defaultFS</name> <value>hdfs://node01:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property>yarn-site.xml:
<property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>node01</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>8192</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>4096</value> </property> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>1024</value> </property>这里要讲清楚两个参数的含义:minimum-allocation是单个容器最少申请内存,maximum是单个容器最多申请;nodemanager.resource是整台机器愿意给YARN的总内存。如果你的机器只有16GB,RegionServer又占了4GB,那NodeManager写10GB已经不保险,写8GB比较稳。如果这个值写太大,YARN在申请内存时不会管其他常驻服务,最后整机OOM,表现为HBase RegionServer突然消失、DataNode心跳中断。
启动HDFS和YARN后,先不要着急装下一个组件,用jps检查进程,再用 hdfs dfsadmin -report 看存储是否正常。这里补一个建议:把格式化NameNode之前的配置文件反复检查一遍,因为格式化之后如果要改fs.defaultFS,还得再格式化一次,代价很大。打通这一步,后面才有意义。
3.2 Hive:把Metastore做成独立服务
Hive整合里最关键的认知是:Metastore 不是一个只能给Hive自己用的内部模块,而是整个平台的元数据中枢。把hive-site.xml里的javax.jdo相关配置指向MySQL,并把metastore地址公布出来。
hive-site.xml关键项:
<property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://node01:3306/hive_meta?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8</value> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.cj.jdbc.Driver</value> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>hive</value> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>你的密码</value> </property> <property> <name>hive.metastore.uris</name> <value>thrift://node01:9083</value> </property> <property> <name>hive.server2.thrift.bind.host</name> <value>node01</value> </property> <property> <name>hive.server2.thrift.port</name> <value>10000</value> </property> <property> <name>hive.metastore.warehouse.dir</name> <value>hdfs://node01:9000/user/hive/warehouse</value> </property>需要做的操作按顺序来:
- 把MySQL JDBC驱动放进 Hive 的 lib 目录。我自己经常漏这一步,然后初始化时报错找不到Driver。
- 执行初始化:bin/schematool -dbType mysql -initSchema
- 启动顺序:先启动metastore,确认9083端口起来,再启动hiveserver2。因为hiveserver2启动时也会尝试连接metastore,顺序反了会报一堆连接失败。
- 测试:bin/beeline -u jdbc:hive2://node01:10000,跑show databases看结果。
这里有个非常重要的经验:hive.metastore.uris一定要配置,而且不能用默认的本地模式。很多人在单机测试时习惯不配它,Hive会在本地起一个内嵌Metastore。可一旦Spark或Hue要连接Hive,它们不会自动走“本地内嵌”这条路,都要通过网络访问9083端口。不配就会导致Spark能看到一套空的默认库,Hive里却有自己的表——两者完全对不上。
3.3 Spark on YARN:接上Hive元数据
Spark安装不需要常驻进程,只需要在所有能提交任务的节点放一份解压好的客户端。我建议node01、node02、node03各放一份,或者至少node01和node02有一份,不然任务提交时的HDFS本地化会多走一跳。
Spark要做的三件事。
第一,spark-env.sh 里设置Hadoop配置目录和Java路径:
export JAVA_HOME=/opt/jdk1.8 export HADOOP_HOME=/opt/hadoop export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export SPARK_HOME=/opt/spark第二,把Hive的hive-site.xml放到Spark的conf目录。这一步不配置,SparkSQL根本不知道Hive Metastore在哪。也可以直接在spark-defaults.conf里写:
spark.master=yarn spark.sql.catalogImplementation=hive spark.hadoop.hive.metastore.uris=thrift://node01:9083其实如果把hive-site.xml都已经复制过来了,后面两行可以靠hive-site.xml自动生效,但spark.sql.catalogImplementation=hive最直观,建议还是写清楚。这套配置做完之后,SparkSQL里show databases看到的就是Hive里的库,两者才算真正共享元数据。
第三,提交测试。不要一上来就跑大SQL,先跑一个简单的:
bin/spark-sql --master yarn --deploy-mode client -e "show databases;"看到结果后,再试建表:
CREATE TABLE spark_hive_test(id int, name string); INSERT INTO spark_hive_test VALUES (1, 'a');之后回到Hive beeline里select这个表,如果能查到,说明Spark和Hive元数据链路已经通了。
我在这步踩过的最隐蔽的坑:Spark运行时找不到Hive表的原始文件。原因是HDFS目录权限和默认目录归属不一致。比如Spark以某个用户身份运行,建的表目录owner是它,而HiveServer2用另一个身份登录,就读不到了。我的规避办法是:在跑测试时统一用户身份,最简单就是用hdfs用户或者一个统一的平台用户,能避免这种花好几个小时才能定位的权限冲突。
3.4 HBase:建列族,再让Hive能读写它
HBase的配置要点在hbase-site.xml:
<property> <name>hbase.rootdir</name> <value>hdfs://node01:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>node01,node02,node03</value> </property> <property> <name>hbase.zookeeper.property.clientPort</name> <value>2181</value> </property>HBase启动后,用 hbase shell 建一张测试表:
hbase shell create 'user_behavior', 'info' put 'user_behavior', 'row1', 'info:name', 'zhangsan'再让Hive能查询这张表。做法是在Hive中创建一张“外联表”,本质是Hive不自己复制数据,而是通过HBaseStorageHandler去读写HBase:
CREATE EXTERNAL TABLE hbase_hive_user_behavior( id string, name string ) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key, info:name") TBLPROPERTIES ("hbase.table.name" = "user_behavior");其中 hbase.columns.mapping 的规则是:第一列对应HBase的RowKey,用:key;后面每一列对应一个列族下的一个限定词。这里有个易错点:Hive表字段顺序必须和mapping对齐,否则数据错位,查出来全是乱的。
要做这一步,前提是把HBase的客户端jar复制到Hive的lib目录。和Hive整合HBase相关的常见jar包括hbase-client、hbase-server、hbase-common、hbase-protocol、htrace-core4等。注意版本要和HBase安装版本一致,不能顺手拿别的版本替代,否则类冲突的概率极高。
这一步做完,你会在Hive里看到真实数据:HBase里有实时写入的数据,Hive通过一张外部表就能做聚合分析;反过来,也可以往Hive外部表里insert,数据会落到HBase的底层表中。生产环境里,很多实时报表场景就是这么组合的:流式作业写HBase,离线数仓用Hive跑SparkSQL分析HBase里沉淀的数据。
3.5 Hue:把所有入口统一到浏览器
Hue配置文件的重点是 desktops/conf/hue.ini,按段来改。
hive段:
[beeswax] hive_server_host=node01 hive_server_port=10000或者新版里用解释器段的方式配置Hive。关键是hive_server_host要写node01而不是localhost,因为Hue虽然运行在同一台node01上,直接写localhost也能通,但写成主机名可以避免以后迁移机器时再改配置。
hdfs段:
[filebrowser] fs.defaultFS=hdfs://node01:9000对应的NameNode Web端口看版本,可以是9870或9820,自己在UI里看到实际值再填。
hbase段:
[hbase] hbase_conf_dir=/opt/hbase/conf thrift_transport=buffered hbase_clusters=(default|node01:9090)Hue通过HBase的Thrift或REST接口访问,重点是把集群地址和配置目录写对,否则HBase浏览器打不开,显示白板。
spark段:Hue 4.x以后支持Spark解释器,需要配置Livy或者直接接Spark Thrift Server。如果不想额外维护一套常驻服务,也可以只让SQL解释器走HiveServer2,因为HiveServer2可以配合Hive配置里的Spark引擎来执行SparkSQL。所以我一般会让Hue的Spark入口指向同一套HiveServer2,减少一个常驻服务和一大截配置量。
Hue初始化和启动:
cd /opt/hue ./build/env/bin/hue migrate ./build/env/bin/supervisor -d第一次启动会要求设置管理员账号。随后打开 http://node01:8888,登录后左侧菜单里能看到文件浏览、Hive查询、HBase浏览器等入口。
这里有个普遍困扰新手的问题:Hue启动报错总在最后一步才暴露,前端页面显示启动失败,但后端进程还在。排查方式永远是先看Hue的日志,而不是反复重启。日志在logs目录下,它会明确告诉你缺哪个Python依赖或者连不上哪个端口。
4. 真实环境里最常踩的坑与排查速查
4.1 元数据对不上:Spark和Hive各看各的表
这是出现频率最高的一个现象——Hive里建了一张表,SparkSQL里show tables查不到,或者在Spark里建的表,Hive那边看不到。
排查顺序:
- 检查Spark的conf里有没有hive-site.xml,或者spark-defaults.conf里有没有spark.sql.catalogImplementation=hive。没有就补上。
- 检查Metastore端口:nc -zv node01 9083,能通再接。
- 检查两边连的是不是同一套MySQL:不要Hive连库A,Spark连库B。我自己曾经在复制配置文件时,不小心把旧的hive-site.xml带了过去,结果Spark连到了另一台机器的元数据库,排查了两天。
这种“看起来配置没问题,但就是各看各的”的故障,最好的排除法就是把“同一份hive-site.xml”直接复制到所有需要访问元数据服务的组件目录,再从这份唯一配置文件里看ConnectionURL到底指向哪里。只要全平台共用一份源配置,元数据不对齐的问题基本断根。
4.2 资源分配不当:HBase退场、容器被kill
通常不是计算问题,而是资源分配问题。常见状态是:HBase起来后默认占用很大堆内存,YARN那边又把NodeManager总内存设得很大,两个服务叠加直接压垮整台机器。
解决办法是主动调小:
- hbase-env.sh里HBASE_HEAPSIZE给4GB左右;
- yarn-site.xml里nodemanager.resource.memory-mb按“机器物理内存 - HBase堆 - 系统预留”来算;
- Hive和Spark的executor内存不要开太大,单容器建议先1GB~2GB测试,后面看监控再往上调。
经验公式其实很简单:把常驻服务该占的内存先预留出来,剩下的才是YARN容器的池子。如果一台机器的物理内存是16GB,那NodeManager设8GB、RegionServer设4GB、系统和其他HDFS进程占4GB,是比较稳妥的划分。你去看官网默认值,它们大多按“整机都给YARN”的思路来,这在多框架共存时是会打架的。
4.3 包冲突:NoSuchMethodError与ClassNotFoundException
Hive的lib目录里如果同时存在多个版本的guava、jackson,或者HBase的jar版本和Hadoop shaded jar冲突,运行时会报很奇怪的类错误。
排查手段:
- 用find在对应lib目录下看是否存在多个同名不同版本的jar;
- 出现NoSuchMethodError或ClassNotFoundException时,先确定是哪个jar没加载对,再通过软链接屏蔽多余版本,而不是直接删jar。直接删很可能连带破坏其他依赖,比如guava删了,Hadoop又要用它;
- 处理HBase整合到Hive时,优先选择官方文档列出的最小jar集合,不要整个lib目录拷过来。整个目录拷过来只会把冲突批发式地全部带进来。
4.4 常见故障速查表
| 现象 | 大概率原因 | 快速处理 |
|---|---|---|
| beeline连不上10000 | HiveServer2没启动或端口被占用 | netstat检查端口;确保先启动metastore再启动HS2 |
| SparkSQL看不到Hive表 | 未连Metastore | 补spark.sql.catalogImplementation=hive,复制hive-site.xml |
| Hue页面能打开但Hive查询失败 | Hue里Hive解释器地址指向错 | 改hue.ini的hive_server_host和port并重启Hue |
| HBase浏览器白屏 | Hue的hbase配置地址或端口不对 | 检查hbase_conf_dir、thrift端口9090或REST端口20550 |
| HDFS报副本数不足 | DataNode不够或起晚了 | hdfs dfsadmin -report看节点,活跃DataNode达到2个以上时重新校验 |
| 任务跑着跑着容器被kill | 单个容器内存超限 | 调低executor内存,或调大yarn.scheduler.maximum-allocation-mb,但不能超过NodeManager总量 |
4.5 验收前先过一遍连接点清单
最后分享我实际执行的检查顺序,相当于“五件套系统上线前的体检表”:
- HDFS和YARN进程都在:node01有NameNode和ResourceManager,node02/node03有DataNode和NodeManager;
- Hive Metastore 9083监听正常;
- MySQL里能看到hive_meta库且元数据表已初始化;
- SparkSQL能看到Hive库表;
- HBase里建的表,Hive外部表能读到;
- Hue登录后,文件浏览、SQL查询、HBase浏览器三个入口都能正常打开。
每次搭建我都会按顺序过一遍,能省下大量返工时间。这个清单看起来简单,但任何一个环节断了,后续在上层写业务都会产生连锁问题。
聊到最后说点实在的。这套整合做完之后,我最大的体会是:五个组件越复杂,越要先把连接点理清楚再动手装。哪怕是版本选型这种看起来最不起眼的一步,都决定了后面三天是顺利收工还是反复找错。如果你正在做类似的事,我建议你把每台机器的内存规划写进一个简单表格,把每个组件需要开放的端口记下来,然后再开始配置,整个链路会顺畅得多。最后再分享一个小技巧:所有配置文件改完以后,尽量保留当时的备份,并顺手记录改动原因。我吃过亏,后来排查一个诡异问题时就是靠当时的备份记录,十分钟定位到了是某一行配置被误改。希望这套思路对你有用。