
1. 为什么我要在本地从零搭一套大数据数仓先把结论放前面这套东西不是给生产环境用的它的价值在于让你真正搞明白 Hive 的一条 SQL 到底经过了哪些组件、元数据存在哪、YARN 怎么调度、Spark 又为什么能读 Hive 的表。我见过太多人用了两三年 Hive问他 metastore 存哪、HDFS 上小文件为什么多、MapReduce 的 shuffle 卡在哪全答不上来。原因很简单——集群是别人搭好的你只负责写 SQL。所以这篇东西的目标很明确在一台机器或者几台虚拟机上把 Apache 生态里数仓最核心的几个组件从零部署起来包括HadoopHDFS YARN、Hive、Spark、ZooKeeper、Kafka、Flume、HBase这一整套。搭完之后你能跑通一条完整链路数据采集进 KafkaFlume 落盘到 HDFSHive 建外部表做数仓分层Spark 读 Hive 表做计算结果再写回 HDFS 或 HBase。适合谁看有一定 Linux 基础、会写基本 SQL、想搞清楚大数据组件之间关系的同学。完全零基础也能跟着做但遇到报错得愿意自己查日志。不适合谁想直接上生产集群的那应该去看发行版方案或者云上的托管服务手工搭的这套稳定性和运维能力都撑不住生产。我下面给的版本组合是经过实测能跑通的组件之间的兼容性是这套东西最大的坑版本选错了能让你卡一整天。先给一张总表后面每个组件再展开。组件版本作用默认端口JDK1.8.0_xxx所有组件的基础运行环境-Hadoop3.3.xHDFS 存储 YARN 资源调度9870 / 8088ZooKeeper3.8.x分布式协调Kafka/HBase 依赖2181Hive3.1.x数仓建模、SQL 解析、元数据管理10000Spark3.3.x内存计算引擎替代 MR4040Kafka3.6.x消息队列数据采集缓冲9092Flume1.11.x日志采集落 HDFS-HBase2.5.x列式存储随机读写16010提示所有组件版本尽量选同一大版本线内的稳定版别混用。比如 Hadoop 3.x 配 Hive 3.x 配 Spark 3.x这是官方文档明确验证过的组合。跨大版本比如 Hive 2.x 配 Hadoop 3.x不是不能跑但 metastore 的 schema 和依赖 jar 会让你怀疑人生。2. 部署前的整体规划与选型思路2.1 单机伪分布式还是多节点怎么选很多人一上来就想搞三台虚拟机做完全分布式我的建议是第一次搭先用单机伪分布式。原因有三个。第一伪分布式和完全分布式在配置上的差异其实很小主要是core-site.xml里 fs.defaultFS 的地址、workers 文件、以及各组件是否配 HA。你把伪分布式跑通了改成完全分布式只是改几个地址的事但如果你一上来就搞多节点出了问题你根本不知道是网络、是配置、还是组件本身的问题。第二资源消耗。Hadoop Hive Spark Kafka HBase 全开光 JVM 堆内存就要吃掉 8G 以上。三台虚拟机每台给 4G跑起来会非常卡调试体验极差。单机给 16G 内存反而更流畅。第三调试效率。伪分布式所有日志都在一台机器上tail -f一个窗口就能看全。多节点你得挨个 ssh 上去看光切换就够烦的。等你把伪分布式跑通、理解了每个组件的作用再上三节点完全分布式那时候你心里是有底的。我下面以伪分布式为主线讲关键地方会标注完全分布式需要改什么。2.2 组件之间的依赖关系先理清楚再动手这套东西不能随便按顺序装有严格的依赖链JDK是一切的前提所有组件都跑在 JVM 上。ZooKeeper要先于 Kafka 和 HBase 装因为这两个都依赖它做协调。Hadoop要先于 Hive、Spark、HBase 装因为 HDFS 是底层存储YARN 是资源调度。Hive依赖 HadoopHDFS 存数据YARN 跑 MR元数据默认存 Derby但生产上必须换成 MySQL。Spark可以独立跑但要读 Hive 表就得配 Hive 的 metastore 连接。Flume依赖 Hadoop写 HDFS和 Kafka读消息。所以正确的安装顺序是JDK → ZooKeeper → Hadoop → MySQL → Hive → Spark → Kafka → Flume → HBase。这个顺序别乱乱了会出现各种找不到类连不上的问题。2.3 元数据库为什么必须换掉 DerbyHive 默认用内嵌的 Derby 存元数据这东西只能单会话访问你开两个 hive 客户端就报错。生产上必须换成 MySQL 或者 PostgreSQL。我选 MySQL因为资料多、坑少。换 MySQL 要做三件事一是把 MySQL 的驱动 jar 放到 Hive 的 lib 目录二是改hive-site.xml里的连接串三是用schematool初始化 schema。第三步很多人会忘结果启动就报table not found。注意MySQL 的驱动版本要和 MySQL 服务端版本匹配。MySQL 8.x 要用mysql-connector-java-8.0.xx.jar用 5.x 的驱动连 8.x 的服务端会报时区或者认证插件的问题。这个坑我踩过报错信息是Public Key Retrieval is not allowed加个allowPublicKeyRetrievaltrue参数能解决但根本办法还是换对驱动。3. 基础环境准备JDK、主机名、免密、防火墙3.1 JDK 安装与环境变量Hadoop 3.x 和 Hive 3.x 都要求 JDK 8别用 JDK 11 或 17会有模块化相关的报错。下载 tar.gz 包解压到/opt/module下然后配环境变量。# 解压 tar -zxvf jdk-8uXXX-linux-x64.tar.gz -C /opt/module/ # 改名方便后续引用 mv /opt/module/jdk1.8.0_XXX /opt/module/jdk # 配置环境变量编辑 /etc/profile.d/my_env.sh export JAVA_HOME/opt/module/jdk export PATH$PATH:$JAVA_HOME/bin配完source /etc/profile.d/my_env.sh然后java -version验证。这里有个细节环境变量一定要配成全局的因为 Hadoop 的守护进程是通过脚本启动的脚本里会读 JAVA_HOME。如果你只在当前 shell 配了用start-dfs.sh启动时会报JAVA_HOME not set。3.2 主机名和 hosts 映射伪分布式也要配主机名因为很多组件的配置里写的是主机名而不是 IP。改/etc/hostname为比如hadoop101然后在/etc/hosts里加上映射。# /etc/hosts 192.168.1.101 hadoop101改完hostname hadoop101让当前会话生效或者重启。这一步不做的话后面 HDFS 的 datanode 注册会用错地址表现为 web 界面能看到 datanode 但是状态是 dead。3.3 SSH 免密登录Hadoop 的启动脚本需要 ssh 到本机完全分布式是到其他节点去启动守护进程所以必须配免密。ssh-keygen -t rsa # 一路回车 ssh-copy-id hadoop101 ssh hadoop101 # 测试能直接进去就对了3.4 防火墙和 SELinux这个不用多说直接关。伪分布式下防火墙会挡住组件之间的端口通信表现为各种connection refused。systemctl stop firewalld systemctl disable firewalld setenforce 0 # 临时关闭 SELinux提示setenforce 0是临时的重启失效。要永久关闭得改/etc/selinux/config里的SELINUXdisabled。生产环境当然不能这么干但本地学习环境关掉能省掉 80% 的诡异问题。4. Hadoop 集群部署HDFS 与 YARN 配置详解4.1 目录规划与解压我习惯把组件都放在/opt/module下数据放在/opt/data下。Hadoop 解压后改名成hadoop。tar -zxvf hadoop-3.3.x.tar.gz -C /opt/module/ mv /opt/module/hadoop-3.3.x /opt/module/hadoop然后配环境变量export HADOOP_HOME/opt/module/hadoop export PATH$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin4.2 核心配置文件逐个拆解Hadoop 的配置全在$HADOOP_HOME/etc/hadoop/下要改的文件有core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml、workers、hadoop-env.sh。core-site.xml定义 HDFS 的地址和临时目录configuration property namefs.defaultFS/name valuehdfs://hadoop101:8020/value /property property namehadoop.tmp.dir/name value/opt/data/hadoop/tmp/value /property /configurationfs.defaultFS的端口Hadoop 3.x 默认是 80202.x 是 9000。这个端口后面 Hive 和 Spark 都要用记牢。hadoop.tmp.dir一定要改默认在/tmp下重启机器就没了NameNode 的元数据会丢集群直接起不来。hdfs-site.xml定义副本数和 NameNode/DataNode 的数据目录configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/opt/data/hadoop/namenode/value /property property namedfs.datanode.data.dir/name value/opt/data/hadoop/datanode/value /property /configuration伪分布式下副本数必须设成 1因为只有一个 DataNode设成 3 会一直报could only be replicated to 0 nodes。yarn-site.xml定义 ResourceManager 和 NodeManagerconfiguration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.resourcemanager.hostname/name valuehadoop101/value /property property nameyarn.nodemanager.resource.memory-mb/name value4096/value /property property nameyarn.scheduler.maximum-allocation-mb/name value2048/value /property /configurationyarn.nodemanager.resource.memory-mb是给 NodeManager 的总内存yarn.scheduler.maximum-allocation-mb是单个容器能申请的最大内存。这两个值要根据你机器的实际内存调设太大跑不起来设太小任务会 OOM。我 16G 的机器给 YARN 4G单容器 2G够跑 demo 了。mapred-site.xml指定 MR 跑在 YARN 上configuration property namemapreduce.framework.name/name valueyarn/value /property /configurationworkers文件列出 DataNode 和 NodeManager 的节点伪分布式就写一行hadoop101。hadoop-env.sh里要显式指定 JAVA_HOMEexport JAVA_HOME/opt/module/jdk4.3 格式化与启动配置改完第一次启动前要格式化 NameNodehdfs namenode -format注意格式化只能做一次。如果你后面改了配置想重新格式化必须先把dfs.namenode.name.dir和dfs.datanode.data.dir指向的目录删掉否则会出现 clusterID 不一致的问题DataNode 起不来。这个坑非常经典报错是Incompatible clusterIDs。启动 HDFS 和 YARNstart-dfs.sh start-yarn.sh然后jps看进程应该能看到 NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager 五个进程。少一个就去看对应的日志日志在$HADOOP_HOME/logs/下。Web 界面验证HDFS 是http://hadoop101:9870YARN 是http://hadoop101:8088。能打开就说明基础环境没问题了。5. Hive 部署元数据换 MySQL 与远程模式配置5.1 MySQL 安装与 Hive 元数据库创建先装 MySQL建一个专门给 Hive 用的库和用户CREATE DATABASE metastore CHARACTER SET latin1; CREATE USER hive% IDENTIFIED BY hive123; GRANT ALL PRIVILEGES ON metastore.* TO hive%; FLUSH PRIVILEGES;字符集用 latin1 是 Hive 官方推荐的因为元数据里有些字段长度是按字节算的用 utf8 可能超长。这个细节很多人不知道用 utf8 建库后面建表可能报Specified key was too long。5.2 hive-site.xml 关键配置Hive 的配置在$HIVE_HOME/conf/hive-site.xml核心是这几项configuration property namejavax.jdo.option.ConnectionURL/name valuejdbc:mysql://hadoop101:3306/metastore?useSSLfalseamp;allowPublicKeyRetrievaltrue/value /property property namejavax.jdo.option.ConnectionDriverName/name valuecom.mysql.cj.jdbc.Driver/value /property property namejavax.jdo.option.ConnectionUserName/name valuehive/value /property property namejavax.jdo.option.ConnectionPassword/name valuehive123/value /property property namehive.metastore.warehouse.dir/name value/user/hive/warehouse/value /property property namehive.metastore.uris/name valuethrift://hadoop101:9083/value /property /configurationhive.metastore.uris这一项是远程模式的关键。配了它Hive 客户端就不直接连 MySQL而是通过 metastore 服务访问。这样多个客户端可以同时连Spark 也能通过这个地址读 Hive 表。5.3 初始化 schema 与启动 metastore把 MySQL 驱动 jar 拷到$HIVE_HOME/lib/下然后初始化schematool -dbType mysql -initSchema看到schemaTool completed就成功了。然后启动 metastore 服务hive --service metastore 再启动 hiveserver2可选给 JDBC 连接用hive --service hiveserver2 最后直接敲hive进客户端建个表试试CREATE TABLE test(id int, name string) ROW FORMAT DELIMITED FIELDS TERMINATED BY \t; SHOW TABLES;能建能查就说明 Hive 通了。提示hive.metastore.uris配了之后如果 metastore 服务没启动hive 客户端会卡住然后报连接超时。所以每次用之前先确认 9083 端口在监听。我一般写个启动脚本把 metastore 和 hiveserver2 一起拉起来。6. Spark 部署on YARN 模式与读写 Hive6.1 Spark 安装与 Hive 集成Spark 解压后关键是把 Hive 的配置拿过来。最省事的做法是把hive-site.xml拷到$SPARK_HOME/conf/下再把 MySQL 驱动拷到$SPARK_HOME/jars/下。这样 Spark 就能直接读 Hive 的元数据了。cp $HIVE_HOME/conf/hive-site.xml $SPARK_HOME/conf/ cp $HIVE_HOME/lib/mysql-connector-java-8.0.xx.jar $SPARK_HOME/jars/6.2 spark-env.sh 与 on YARN 提交spark-env.sh里指定 Hadoop 配置目录和 JAVA_HOMEexport JAVA_HOME/opt/module/jdk export HADOOP_CONF_DIR/opt/module/hadoop/etc/hadoopHADOOP_CONF_DIR必须配否则 Spark on YARN 找不到 HDFS 和 YARN 的地址。提交任务测试spark-submit \ --master yarn \ --deploy-mode client \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.3.x.jar 10能算出 Pi 的值就说明 Spark on YARN 通了。6.3 Spark SQL 读 Hive 表的实操进spark-sql或者spark-shell直接查 Hive 表spark.sql(show databases).show() spark.sql(select * from test).show()如果报Table or view not found八成是hive-site.xml没拷对或者 metastore 服务没起。如果报Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient那是 metastore 连接的问题检查hive.metastore.uris和 9083 端口。注意Spark 3.x 默认用的是自己的 catalog 实现要读 Hive 表得开enableHiveSupport()。在 spark-sql 里默认是开的但在代码里SparkSession.builder()要显式加.enableHiveSupport()。这个细节不注意代码里就是读不到 Hive 表。7. Kafka、Flume、HBase 的部署要点7.1 Kafka 部署与 ZooKeeper 连接Kafka 依赖 ZooKeeper先确保 ZK 起来了。Kafka 的配置在config/server.propertiesbroker.id0 listenersPLAINTEXT://hadoop101:9092 log.dirs/opt/data/kafka/logs zookeeper.connecthadoop101:2181启动bin/kafka-server-start.sh -daemon config/server.properties建个 topic 测试bin/kafka-topics.sh --create --topic test --bootstrap-server hadoop101:9092 --partitions 1 --replication-factor 17.2 Flume 采集链路配置Flume 的配置是写一个 conf 文件定义 source、channel、sink。一个典型的 Kafka 到 HDFS 的配置a1.sources r1 a1.channels c1 a1.sinks k1 a1.sources.r1.type org.apache.flume.source.kafka.KafkaSource a1.sources.r1.kafka.bootstrap.servers hadoop101:9092 a1.sources.r1.kafka.topics test a1.sources.r1.kafka.consumer.group.id flume-group a1.channels.c1.type memory a1.channels.c1.capacity 10000 a1.sinks.k1.type hdfs a1.sinks.k1.hdfs.path /flume/%Y%m%d/%H a1.sinks.k1.hdfs.filePrefix log- a1.sinks.k1.hdfs.rollInterval 60 a1.sinks.k1.hdfs.rollSize 134217728 a1.sinks.k1.hdfs.fileType DataStream a1.sources.r1.channels c1 a1.sinks.k1.channel c1rollInterval和rollSize控制文件滚动太小会产生大量小文件太大延迟高。我一般设 60 秒或者 128M哪个先到滚哪个。7.3 HBase 部署与 HDFS 集成HBase 的配置主要是hbase-site.xmlconfiguration property namehbase.rootdir/name valuehdfs://hadoop101:8020/hbase/value /property property namehbase.cluster.distributed/name valuetrue/value /property property namehbase.zookeeper.quorum/name valuehadoop101/value /property /configurationhbase.rootdir指向 HDFS这样 HBase 的数据就存在 HDFS 上。启动start-hbase.sh然后hbase shell进去create test, cf建表测试。8. 常见问题排查速查表搭这套东西90% 的时间会花在排查问题上。我把踩过的坑整理成表遇到问题先对照着查。现象可能原因排查方向DataNode 起不来报 Incompatible clusterIDs重复格式化 NameNode删掉 name 和 data 目录重新格式化Hive 启动报 table not foundschema 没初始化跑 schematool -initSchemaSpark 读不到 Hive 表hive-site.xml 没拷或 metastore 没起检查 9083 端口和配置文件YARN 任务一直 ACCEPTED资源不够调大 nodemanager 内存或减小容器内存Kafka 连不上 ZKZK 没起或地址错telnet 测 2181 端口Flume 写 HDFS 报权限错当前用户没 HDFS 写权限用 hdfs dfs -chmod 改权限HBase 启动后 HMaster 秒退与 HDFS 时间不同步或端口冲突看 master 日志检查 16000 端口8.1 日志是第一排查手段所有组件出问题第一步都是看日志。Hadoop 的日志在$HADOOP_HOME/logs/Hive 的在/tmp/用户名/hive.log或者配置的 log 目录Spark 的在$SPARK_HOME/logs/。日志里搜ERROR和Exception基本能定位到问题。8.2 端口占用问题组件多了端口容易冲突。用netstat -tlnp | grep 端口查。常见的冲突Hive 的 10000 和别的服务撞HBase 的 16000 和别的撞。改配置里的端口就行。8.3 时间同步问题HBase 和 YARN 对时间敏感机器时间差太多会导致各种诡异问题。本地环境用ntpdate同步一下或者直接手动date -s设对。9. 我踩过的几个印象深刻的坑第一个是Hive 的元数据字符集。我一开始用 utf8 建 metastore 库建表的时候报Specified key was too long; max key length is 767 bytes。查了半天才知道 Hive 的元数据表有些字段是 varchar(767)utf8 下三个字节一个字符超了。换成 latin1 立马就好。这个坑官方文档里有提但很容易被忽略。第二个是Spark 的 Hive 支持。我在代码里写SparkSession.builder().getOrCreate()然后spark.sql(select * from hive_table)一直报找不到表。折腾了一小时才发现没加.enableHiveSupport()。加了之后还要确保hive-site.xml在 classpath 里否则它连的是内存里的 Derby当然找不到 Hive 的表。第三个是YARN 的内存配置。我机器 16G一开始给 NodeManager 配了 8G单容器 4G结果跑 Spark 任务一直卡在 ACCEPTED。后来发现是yarn.scheduler.maximum-allocation-mb设得比yarn.nodemanager.resource.memory-mb还大调度器认为没有节点能满足这个请求就一直排队。把单容器内存调到 2G 就好了。这个逻辑其实很简单但配置的时候容易想当然。第四个是Flume 的小文件。我一开始rollInterval设了 10 秒跑了一晚上 HDFS 上多了几万个几 KB 的小文件NameNode 内存直接告警。后来改成 60 秒 128M 滚动小文件问题缓解了很多。数仓里小文件是万恶之源采集阶段就要控制好。10. 搭完之后怎么练手集群搭好只是开始真正有价值的是拿它练手。我建议按这个顺序练先练Hive 数仓分层。建 ODS、DWD、DWS、ADS 四层用外部表接原始数据用分区表做增量用 ORC 格式压缩存储。把load data、insert overwrite、partition这些操作跑熟。再练Spark 读写 Hive。用 Spark SQL 做 ETL把 Hive 表读进来做聚合、join再写回 Hive 或者 HDFS。对比一下 Spark 和 MR 的速度差异感受一下内存计算的优势。然后练Kafka Flume 实时链路。往 Kafka 发消息Flume 消费落 HDFSHive 外部表挂上去查。这条链路跑通你就理解了离线数仓的数据是怎么进来的。最后练HBase 随机读写。把 Hive 的结果导到 HBase用 rowkey 做点查感受一下列式存储和 HDFS 顺序扫描的区别。这套东西搭一次可能要大半天但搭完之后你对整个大数据数仓的理解会上一个台阶。以后再看到数据中台湖仓一体这些概念你心里是有底的因为你知道底层就是这些组件在支撑。我个人在实际操作中的体会是搭集群的过程比看十篇文章都管用报错报多了自然就懂了。