简介这份资源是 Spark 2.3.0 基于 Hadoop 2.x 构建、剔除 Hive JAR 包的发行版本面向需要在独立 Hive 集群之外借助 Spark 计算能力处理 Hive 数据的中高级大数据开发者与运维人员重点解决不依赖 Hive 执行引擎、仅通过 metastore 访问元数据的集成场景。压缩包共 867 个文件约 127.77MB以 235 个 py、187 个 scala、125 个 java 源码及 109 个 jar 依赖为主体辅以 sh、cmd 启动脚本、template 配置模板与 parquet、orc、avro 等示例数据文件目录结构完整便于按模块查阅与二次配置。目前已有 660 人学习下载。读者可据此搭建精简的 Spark 运行环境结合 spark-defaults.conf 中 metastore 地址的配置思路实践 Hive on Spark 的元数据访问与 Spark SQL 查询并参考随包的示例数据与脚本快速验证集成效果。1. 从一份 without-hive 的 Spark 包说起为什么我坚持先装 JDK 8 再解压上周帮一位同事在测试机上复现一个 Hive on Spark 的查询计划他图省事直接拿了一份spark-2.3.0-bin-hadoop2-without-hive的压缩包解压就跑结果spark-sql一启动就报NoClassDefFoundError: org/apache/hadoop/hive/conf/HiveConf折腾了一下午。这个包名里的without-hive不是随便写的它意味着 Spark 自带的 Hive 依赖被剔除了专门留给那些已经有一套独立 Hive 环境、想让 Spark 复用外部 Hive 元数据和执行引擎的场景。装不好轻则spark-sql起不来重则 Hive on Spark 任务提交后一直卡在ACCEPTED状态日志里全是类加载冲突。这篇笔记面向的是要在单机或伪分布式环境里把这份包跑起来、并且准备接 Hive on Spark 的开发者我会把 JDK 版本、解压路径、环境变量、Hive 配置对接和验证步骤按我实际装机的顺序写清楚中间踩过的坑也一并记下来。2. 装前把环境钉死JDK 8、Hadoop 2.x 与路径规划2.1 版本对照为什么是 JDK 8 而不是 11Spark 2.3.0 这个版本对 JDK 的要求很明确官方编译目标是 Java 8用 JDK 11 跑会遇到Unsupported class file major version或者反射相关的IllegalAccessError。我一般会先确认机器上现有的 Javajava -version # 期望输出类似 # java version 1.8.0_202 # Java(TM) SE Runtime Environment (build 1.8.0_202-b08) # Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)如果输出是 11 或 17不要试图改JAVA_HOME指向一个不存在的 8先把 8 装上。常见做法是用系统包管理器装openjdk-8-jdk或者解压一个 tar 包到/usr/local/jdk1.8.0_202。这里的关键参数是JAVA_HOMESpark 的spark-class脚本会读它来拼java命令指向错了后面所有步骤都是白费。我习惯把 JDK 放在/usr/local/下路径里不要带空格和中文这是血泪经验带空格的路径在spark-env.sh里转义起来很麻烦。Hadoop 这边包名里的hadoop2表示它预编译时链接的是 Hadoop 2.7.x 系列的客户端库。如果你机器上已经有一套 Hadoop 2.x注意HADOOP_CONF_DIR要指向那套配置目录如果没有单机跑 local 模式也能用只是不能读 HDFS。版本对照我整理成一张表装之前对着核一遍组件推荐版本说明JDK1.8.0_202 及以上 8u 系列必须是 8不支持 11Hadoop 客户端2.7.3 至 2.7.7包名 hadoop2 对应此系列Scala2.11.8Spark 2.3.0 预编译对应版本不要混用 2.12Hive1.2.1 或 2.3.x接 Hive on Spark 时元数据版本要匹配2.2 目录规划与解压我一般把 Spark 放在/opt/spark-2.3.0软链一个/opt/spark指向它这样以后换版本只改软链环境变量不用动。解压命令sudo mkdir -p /opt sudo tar -zxvf spark-2.3.0-bin-hadoop2-without-hive.tgz -C /opt sudo ln -s /opt/spark-2.3.0-bin-hadoop2-without-hive /opt/spark ls /opt/spark # 应看到 bin conf jars sbin 等目录-C /opt指定解压目标-z解 gzip-x解包-v显示过程。软链这一步不是必须但后面配SPARK_HOME时写/opt/spark比写一长串目录名省心。解压完先别急着配环境变量进jars目录看一眼有没有hive-exec相关的 jarls /opt/spark/jars | grep -i hive # without-hive 包这里应该没有输出或者只有极少数非核心 hive jar这一步是确认你拿到的确实是 without-hive 版本。如果这里冒出一堆hive-exec-1.2.1.jar说明包拿错了后面接外部 Hive 反而会因为自带 jar 和外部 jar 冲突出问题。3. 配环境变量与 spark-env.sh三个容易写错的路径3.1 在 profile 里写 SPARK_HOME 和 PATH环境变量我统一写在/etc/profile.d/spark.sh里而不是直接改~/.bashrc这样多用户都能用sudo tee /etc/profile.d/spark.sh EOF export JAVA_HOME/usr/local/jdk1.8.0_202 export SPARK_HOME/opt/spark export HADOOP_CONF_DIR/etc/hadoop/conf export PATH$JAVA_HOME/bin:$SPARK_HOME/bin:$PATH EOF source /etc/profile.d/spark.shJAVA_HOME必须指向 JDK 根目录而不是bin目录这是第一个容易写错的路径。HADOOP_CONF_DIR指向 Hadoop 的配置目录里面要有core-site.xml和hdfs-site.xml没有的话 local 模式也能跑但读 HDFS 会报No FileSystem for scheme: hdfs。PATH里把$SPARK_HOME/bin加进去spark-shell、spark-sql才能直接敲。写完source一下然后验证echo $SPARK_HOME which spark-shell # 应输出 /opt/spark/bin/spark-shell3.2 spark-env.sh 里的内存与端口参数conf/spark-env.sh默认是spark-env.sh.template复制一份再改cd /opt/spark/conf cp spark-env.sh.template spark-env.sh在文件末尾追加几行我一般这么配export SPARK_MASTER_HOST127.0.0.1 export SPARK_MASTER_PORT7077 export SPARK_WORKER_MEMORY2g export SPARK_WORKER_CORES2 export SPARK_LOCAL_IP127.0.0.1SPARK_MASTER_HOST和SPARK_LOCAL_IP是第二个容易写错的路径类参数它们不是路径但同样是地址写localhost在某些容器环境里会解析到 IPv6 导致 worker 注册不上我一般直接写127.0.0.1。SPARK_WORKER_MEMORY给 2g 是单机测试的保守值机器内存小于 4g 时别超过物理内存的一半。SPARK_WORKER_CORES控制每个 worker 能用的核数设成 2 是为了留出核给系统。改完spark-env.sh不需要source启动脚本会自己读。3.3 接外部 Hive 时的 hive-site.xml 放置without-hive 包要接外部 Hive核心是把 Hive 的hive-site.xml放到 Spark 能读到的地方。我一般复制到$SPARK_HOME/conf/cp /etc/hive/conf/hive-site.xml /opt/spark/conf/这里第三个容易写错的路径是hive-site.xml里的javax.jdo.option.ConnectionURL它指向元数据库如果写的是jdbc:mysql://localhost:3306/hive要确认本机 MySQL 真的在 3306 且库名对得上。Spark 读这个文件是为了拿到 metastore 地址和 warehouse 路径spark.sql.warehouse.dir如果和 Hive 里配的不一致建表会建到两个地方查的时候找不到。我一般会在spark-defaults.conf里再显式写一遍echo spark.sql.warehouse.dir hdfs:///user/hive/warehouse /opt/spark/conf/spark-defaults.conf echo spark.sql.catalogImplementation hive /opt/spark/conf/spark-defaults.confspark.sql.catalogImplementation设成hive才会去读hive-site.xml里的 metastore默认是in-memory不设的话spark-sql起来看到的是一个空的内置 catalog这也是很多人以为“接上了其实没接上”的原因。4. 启动与 Hive on Spark 对接从 local 模式到 on yarn4.1 先用 local 模式验证包本身没坏配完环境变量第一步不是急着接 Hive而是用 local 模式确认这份 without-hive 包能独立跑起来spark-shell --master local[2]进去之后敲几行 Scalaval df spark.range(10) df.count() // 期望输出 res0: Long 10--master local[2]表示用本地 2 个线程跑不依赖任何集群。这一步能过说明 JDK、SPARK_HOME、包完整性都没问题。如果这里就报NoClassDefFoundError先回头看第 2 章的jars目录检查大概率是包拿错或者 JDK 版本不对。local 模式过了再往下走能省掉很多“到底是包的问题还是 Hive 的问题”的纠结。4.2 启动 standalone 集群并确认 worker 注册单机伪分布式我一般起 standalone/opt/spark/sbin/start-master.sh /opt/spark/sbin/start-slave.sh spark://127.0.0.1:7077 jps # 应看到 Master 和 Worker 两个进程start-slave.sh后面的地址要和SPARK_MASTER_HOST、SPARK_MASTER_PORT拼出来的一致。起来之后打开http://127.0.0.1:8080看 Master 页面Alive Workers 应该是 1。如果 worker 一直不注册先看$SPARK_HOME/logs/下 worker 的日志常见原因是SPARK_LOCAL_IP没设或者设成了主机名但/etc/hosts里没解析。这一步的验证意义在于后面 Hive on Spark 提交任务时如果 standalone 集群本身 worker 都没注册任务会一直WAITING你会误以为是 Hive 配置问题。4.3 Hive on Spark 的关键参数Hive on Spark 的本质是让 Hive 把查询编译成 Spark 任务通过hive.execution.enginespark切换执行引擎。在 Hive 的hive-site.xml里我一般加这几项property namehive.execution.engine/name valuespark/value /property property namespark.master/name valuespark://127.0.0.1:7077/value /property property namespark.home/name value/opt/spark/value /property property namespark.executor.memory/name value1g/value /property property namespark.serializer/name valueorg.apache.spark.serializer.KryoSerializer/value /propertyspark.home必须指向 without-hive 包的根目录Hive 会去这个目录下找jars来拼 classpath。spark.master写 standalone 地址如果集群是 yarn 就写yarn。spark.executor.memory在单机上别给太大1g 起步和SPARK_WORKER_MEMORY加起来别超物理内存。spark.serializer用 Kryo 是 Hive on Spark 的常见做法默认的 Java 序列化在 shuffle 数据量大时性能差距明显。改完 Hive 配置要重启 HiveServer2 或者重新进beeline才生效。4.4 用 beeline 跑一条跨引擎查询对接完用beeline连 HiveServer2 验证beeline -u jdbc:hive2://127.0.0.1:10000/default -n hive -p 进去后先确认引擎set hive.execution.engine; -- 期望输出 hive.execution.enginespark然后建一张小表跑聚合create table if not exists t_test(id int, name string) stored as parquet; insert into t_test values (1,a),(2,b),(3,c); select count(*) from t_test; -- 期望输出 3stored as parquet是为了避开 textfile 在小数据量下的格式差异insert into走的是 Spark 执行引擎如果hive.execution.engine没生效这条 insert 会走 MapReduce速度明显慢且日志里能看到Tez或MapReduce字样。看到Spark job相关的日志输出才算真正接上了。5. 避坑与排查五条装坏了的现场记录5.1 spark-sql 启动报 HiveConf 找不到现象敲spark-sql直接抛java.lang.NoClassDefFoundError: org/apache/hadoop/hive/conf/HiveConf。原因without-hive 包本身不带 Hive 核心 jar而spark-sql默认会尝试初始化 Hive 支持。解决要么把外部 Hive 的lib下hive-exec、hive-metastore等 jar 通过--jars传进去要么在spark-defaults.conf里把spark.sql.catalogImplementation设成in-memory先跑通非 Hive 场景。我一般选后者做初步验证确认包没问题再补 jar。5.2 worker 注册不上Master 页面 Alive Workers 为 0现象start-slave.sh执行后进程在但 Master 页面看不到 worker。原因SPARK_LOCAL_IP没设worker 用主机名注册而主机名在容器或云主机上解析不到。解决在spark-env.sh里显式export SPARK_LOCAL_IP127.0.0.1重启 worker。这个坑在容器里尤其常见玄学得很日志里只写Connection refused不告诉你地址解析错了。5.3 Hive on Spark 任务卡在 ACCEPTED现象beeline里提交查询后一直ACCEPTED不进入RUNNING。原因spark.master配的地址和实际 standalone 集群地址不一致或者 executor 内存申请超过 worker 可用内存资源一直等不到。解决核对spark.master和SPARK_MASTER_HOST:PORT把spark.executor.memory降到 worker 能给的范围内再看 Master 页面 Resources 里内存和核数是否够。5.4 元数据版本不匹配导致 metastore 连不上现象spark-sql起来后查表报MetaException或 schema 版本相关错误。原因外部 Hive 的 metastore schema 版本和 Spark 内置的 Hive 客户端版本不一致Spark 2.3.0 对应 Hive 1.2.1 的 metastore 客户端。解决确认 Hive 版本在 1.2.1 或 2.3.x 且 metastore schema 已schematool -initSchema初始化过版本差太多就换匹配的 Hive 或者用--conf spark.sql.hive.metastore.version指定。5.5 路径带空格导致 spark-class 解析失败现象启动脚本报Error: Could not find or load main class但类明明在。原因SPARK_HOME或JAVA_HOME路径里有空格spark-class拼命令时没正确转义。解决把 JDK 和 Spark 都放到无空格路径下比如/usr/local/jdk1.8.0_202和/opt/spark别放Program Files这类目录。这是最冤的一种翻车改路径比改脚本省事。6. 装完怎么验证跑通三条命令加一张对照表验证这件事我一般分三层做从包本身到集群再到 Hive on Spark逐层确认任何一层不过就停在那层排查不要跳。第一层是包完整性第二层是集群可用性第三层是引擎切换生效。下面三条命令是我每次装完必跑的# 第一层确认 Spark 版本和 Scala 版本 spark-submit --version # 期望看到 version 2.3.0 和 Scala version 2.11.8 # 第二层local 模式跑一个 wordcount 级别的作业 spark-submit --master local[2] --class org.apache.spark.examples.SparkPi \ /opt/spark/examples/jars/spark-examples_2.11-2.3.0.jar 10 # 期望输出 Pi is roughly 3.14... # 第三层确认 Hive 引擎切换 beeline -u jdbc:hive2://127.0.0.1:10000/default -n hive -p \ -e set hive.execution.engine; select count(*) from t_test; # 期望先输出 hive.execution.enginespark再输出行数第一条spark-submit --version看的是包和 JDK 的匹配如果 Scala 版本显示 2.12 说明包拿错了。第二条用自带 examples 跑 Pi不依赖任何外部数据能过说明 local 执行链路通。第三条把引擎确认和查询合在一起-e参数直接执行不交互适合写进脚本做回归。三层都过基本可以认为这套 without-hive 包在你的环境里跑通了。再补一张验证对照表方便对着看现象验证项命令通过标志失败先看版本匹配spark-submit --version2.3.0 / Scala 2.11.8包名是否 without-hivelocal 执行SparkPi示例输出 Pi 近似值JDK 是否 8集群注册jps Master 页面Master、Worker 各一SPARK_LOCAL_IP引擎切换set hive.execution.engine值为 sparkhive-site.xml是否被读到元数据连通select count(*)返回正确行数metastore 地址与版本有个小技巧验证 Hive on Spark 是否真的走了 Spark不要只看set的输出去$SPARK_HOME/logs/下看有没有对应的 application 日志生成或者 Master 页面 Applications 里有没有新任务。set只说明配置读到了真正提交任务才会暴露 classpath 和资源问题。我一般会在beeline里跑一条insert overwrite再select强制触发一次 Spark 作业比只跑select更能验证写入链路。从那以后我每次装 Spark 都强制走一遍这三层验证尤其是spark-submit --version和jps这两步看着简单但能挡掉八成“装完看着像好的其实没通”的情况。希望帮到你。本文还有配套的精品资源点击获取