前提
笔者目前需要搭建数据平台,发现了Windows
系统下,Hadoop
和Hive
等组件的安装和运行存在大量的坑,而本着有坑必填的目标,笔者还是花了几个晚上的下班时候在多个互联网参考资料的帮助下完成了Windows10
系统下Hadoop
和Hive
开发环境的搭建。这篇文章记录了整个搭建过程中的具体步骤、遇到的问题和对应的解决方案。
环境准备
基于笔者的软件版本洁癖,所有选用的组件都会使用当前(2020-10-30)最高的版本。
软件 | 版本 | 备注 |
Windows |
10 |
操作系统 |
JDK |
8 |
暂时不要选用大于等于JDK9 的版本,因为启动虚拟机会发生未知异常 |
MySQL |
8.x |
用于管理Hive 的元数据 |
Apache Hadoop |
3.3.0 |
- |
Apache Hive |
3.1.2 |
- |
Apache Hive src |
1.2.2 |
因为只有1.x 版本的Hive 源码提供了.bat 启动脚本,有能力可以自己写脚本就不用下此源码包 |
winutils |
hadoop-3.3.0 |
Hadoop 的Windows 系统下的启动依赖 |
下面列举部分组件对应的下载地址:
Apache Hadoop 3.3.0
:https://mirror.bit.edu.cn/apache/hadoop/common/hadoop-3.3.0/hadoop-3.3.0.tar.gz
Apache Hive 3.1.2
:https://mirrors.bfsu.edu.cn/apache/hive/hive-3.1.2/apache-hive-3.1.2-bin.tar.gz
Apache Hive 1.2.2 src
:https://mirrors.bfsu.edu.cn/apache/hive/hive-1.2.2/apache-hive-1.2.2-src.tar.gz
winutils
:https://github.com/kontext-tech/winutils
(如果下载速度慢,可以先把仓库导入gitee.com
再下载,或者用笔者已经同步好的仓库https://gitee.com/throwableDoge/winutils
)
下载完这一些列软件之后,MySQL
正常安装为系统服务随系统自启。解压hadoop-3.3.0.tar.gz
、apache-hive-3.1.2-bin.tar.gz
、apache-hive-1.2.2-src.tar.gz
和winutils
到指定目录:
接着把源码包apache-hive-1.2.2-src.tar.gz
解压后的bin
目录下的文件拷贝到apache-hive-3.1.2-bin
的bin
目录中:
然后把winutils
中的hadoop-3.3.0\bin
目录下的hadoop.dll
和winutils.exe
文件拷贝到Hadoop
的解压目录的bin
文件夹下:
最后再配置一下JAVA_HOME
和HADOOP_HOME
两个环境变量,并且在Path
中添加%JAVA_HOME%\bin;
和%HADOOP_HOME%\bin
:
笔者本地安装的JDK版本为1.8.0.212,理论上任意一个小版本的JDK8都可以。
接着用命令行测试一下,如果上述步骤没问题,控制台输出如下:
配置和启动Hadoop
在HADOOP_HOME
的etc\hadoop
子目录下,找到并且修改下面的几个配置文件:
core-site.xml(这里的tmp
目录一定要配置一个非虚拟目录,别用默认的tmp
目录,否则后面会遇到权限分配失败的问题)
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/e:/LittleData/hadoop-3.3.0/data/tmp</value> </property> </configuration> 复制代码
hdfs-site.xml(这里要预先创建nameNode
和dataNode
的数据存放目录,注意一下每个目录要以/
开头,笔者这里预先在HADOOP_HOME/data
创建了nameNode
和dataNode
子目录)
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.http.address</name> <value>0.0.0.0:50070</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/e:/LittleData/hadoop-3.3.0/data/nameNode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/e:/LittleData/hadoop-3.3.0/data/dataNode</value> </property> <property> <name>dfs.permissions.enabled</name> <value>false</value> </property> </configuration> 复制代码
mapred-site.xml
<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration> 复制代码
yarn-site.xml
<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name> <value>org.apache.hadoop.mapred.ShuffleHandler</value> </property> </configuration> 复制代码
至此,最小化配置基本完成。接着需要格式化namenode
并且启动Hadoop
服务。切换至$HADOOP_HOME/bin
目录下,使用CMD
输入命令hdfs namenode -format
(格式化namenode
切记不要重复执行):
格式化namenode
完毕后,切换至$HADOOP_HOME/sbin
目录下,执行start-all.cmd
脚本:
这里命令行会提示start-all.cmd
脚本已经过期,建议使用start-dfs.cmd
和start-yarn.cmd
替代。同理,如果执行stop-all.cmd
也会有类似的提示,可以使用stop-dfs.cmd
和stop-yarn.cmd
替代。start-all.cmd
成功执行后,会拉起四个JVM
实例(见上图中的Shell
窗口自动新建了四个Tab
),此时可以通过jps
查看当前的JVM
实例:
λ jps 19408 ResourceManager 16324 NodeManager 14792 Jps 15004 NameNode 2252 DataNode 复制代码
可见已经启动了ResourceManager
、NodeManager
、NameNode
和DataNode
四个应用,至此Hadoop
的单机版已经启动成功。通过stop-all.cmd
命令退出这四个进程。可以通过http://localhost:8088/
查看调度任务的状态:
通过http://localhost:50070/
去查看HDFS
的状态和文件:
重启Hadoop
的办法:先执行stop-all.cmd
脚本,再执行start-all.cmd
脚本。
配置和启动Hive
Hive
是构筑于HDFS
上的,所以务必确保Hadoop
已经启动。Hive
在HDFS
中默认的文件路径前缀是/user/hive/warehouse
,因此可以先通过命令行在HDFS
中创建此文件夹:
hdfs dfs -mkdir /user/hive/warehouse hdfs dfs -chmod -R 777 /user/hive/warehouse 复制代码
同时需要通过下面的命令创建并为tmp
目录赋予权限:
hdfs dfs -mkdir /tmp hdfs dfs -chmod -R 777 /tmp 复制代码
在系统变量中添加HIVE_HOME
,具体的值配置为E:\LittleData\apache-hive-3.1.2-bin
,同时在Path
变量添加%HIVE_HOME%\bin;
,跟之前配置HADOOP_HOME
差不多。下载和拷贝一个mysql-connector-java-8.0.x.jar
到$HIVE_HOME/lib
目录下:
创建Hive
的配置文件,在$HIVE_HOME/conf
目录下已经有对应的配置文件模板,需要拷贝和重命名,具体如下:
$HIVE_HOME/conf/hive-default.xml.template
=>$HIVE_HOME/conf/hive-site.xml
$HIVE_HOME/conf/hive-env.sh.template
=>$HIVE_HOME/conf/hive-env.sh
$HIVE_HOME/conf/hive-exec-log4j.properties.template
=>$HIVE_HOME/conf/hive-exec-log4j.properties
$HIVE_HOME/conf/hive-log4j.properties.template
=>$HIVE_HOME/conf/hive-log4j.properties
修改hive-env.sh
脚本,在尾部添加下面内容:
export HADOOP_HOME=E:\LittleData\hadoop-3.3.0 export HIVE_CONF_DIR=E:\LittleData\apache-hive-3.1.2-bin\conf export HIVE_AUX_JARS_PATH=E:\LittleData\apache-hive-3.1.2-bin\lib 复制代码
修改hive-site.xml
文件,主要修改下面的属性项:
属性名 | 属性值 | 备注 |
hive.metastore.warehouse.dir |
/user/hive/warehouse |
Hive 的数据存储目录,这个是默认值 |
hive.exec.scratchdir |
/tmp/hive |
Hive 的临时数据目录,这个是默认值 |
javax.jdo.option.ConnectionURL |
jdbc:mysql://localhost:3306/hive?characterEncoding=UTF-8&serverTimezone=UTC |
Hive 元数据存放的数据库连接 |
javax.jdo.option.ConnectionDriverName |
com.mysql.cj.jdbc.Driver |
Hive 元数据存放的数据库驱动 |
javax.jdo.option.ConnectionUserName |
root |
Hive 元数据存放的数据库用户 |
javax.jdo.option.ConnectionPassword |
root |
Hive 元数据存放的数据库密码 |
hive.exec.local.scratchdir |
E:/LittleData/apache-hive-3.1.2-bin/data/scratchDir |
创建本地目录$HIVE_HOME/data/scratchDir |
hive.downloaded.resources.dir |
E:/LittleData/apache-hive-3.1.2-bin/data/resourcesDir |
创建本地目录$HIVE_HOME/data/resourcesDir |
hive.querylog.location |
E:/LittleData/apache-hive-3.1.2-bin/data/querylogDir |
创建本地目录$HIVE_HOME/data/querylogDir |
hive.server2.logging.operation.log.location |
E:/LittleData/apache-hive-3.1.2-bin/data/operationDir |
创建本地目录$HIVE_HOME/data/operationDir |
datanucleus.autoCreateSchema |
true |
可选 |
datanucleus.autoCreateTables |
true |
可选 |
datanucleus.autoCreateColumns |
true |
可选 |
hive.metastore.schema.verification |
false |
可选 |
修改完毕之后,在本地的MySQL
服务新建一个数据库hive
,编码和字符集可以选用范围比较大的utf8mb4
(虽然官方建议是latin1
,但是字符集往大范围选没有影响):
上面的准备工作做完之后,可以进行Hive
的元数据库初始化,在$HIVE_HOME/bin
目录下执行下面的脚本:
hive.cmd --service schematool -dbType mysql -initSchema 复制代码
这里有个小坑,hive-site.xml
文件的第3215
行有个神奇的无法识别的符号:
此无法识别符号会导致Hive
的命令执行异常,需要去掉。当控制台输出Initialization script completed schemaTool completed
的时候,说明元数据库已经初始化完毕:
在$HIVE_HOME/bin
目录下,通过hive.cmd
可以连接Hive
(关闭控制台即可退出):
> hive.cmd 复制代码
尝试创建一个表t_test
:
hive> create table t_test(id INT,name string); hive> show tables; 复制代码
查看http://localhost:50070/
确认t_test
表已经创建成功。
尝试执行一个写入语句和查询语句:
hive> insert into t_test(id,name) values(1,'throwx'); hive> select * from t_test; 复制代码
写用了30
多秒,读用了0.165
秒。
使用JDBC连接Hive
HiveServer2
是Hive
服务端接口模块,必须启动此模块,远程客户端才能对Hive
进行数据写入和查询。目前,此模块还是基于Thrift RPC
实现,它是HiveServer
的改进版,支持多客户端接入和身份验证等功能。配置文件hive-site.xml
中可以修改下面几个关于HiveServer2
的常用属性:
属性名 | 属性值 | 备注 |
hive.server2.thrift.min.worker.threads |
5 |
最小工作线程数,默认值为5 |
hive.server2.thrift.max.worker.threads |
500 |
最大工作线程数,默认值为500 |
hive.server2.thrift.port |
10000 |
侦听的TCP 端口号,默认值为10000 |
hive.server2.thrift.bind.host |
127.0.0.1 |
绑定的主机,默认值为127.0.0.1 |
hive.execution.engine |
mr |
执行引擎,默认值为mr |
在$HIVE_HOME/bin
目录下执行下面的命令可以启动HiveServer2
:
hive.cmd --service hiveserver2 复制代码
客户端需要引入hadoop-common
和hive-jdbc
依赖,依赖的版本尽量和对接的Hadoop
和Hive
版本对应。
<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-jdbc</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <version>2.3.5.RELEASE</version> </dependency> 复制代码
hadoop-common
依赖链比较长,会连带下载大量其他相关依赖,所以可以找个空闲时间在某个Maven
项目先挂起该依赖下载的任务(笔者挂起此依赖下载任务洗完澡仍然没下完,还会出现org.glassfish:javax.el
的快照包无法下载的问题,不过不影响正常使用)。最后添加一个单元测试类HiveJdbcTest
:
@Slf4j public class HiveJdbcTest { private static JdbcTemplate TEMPLATE; private static HikariDataSource DS; @BeforeClass public static void beforeClass() throws Exception { HikariConfig config = new HikariConfig(); config.setDriverClassName("org.apache.hive.jdbc.HiveDriver"); // 这里笔者修改过hive-site.xml的对应配置,因为端口不是默认的10000 // config.setJdbcUrl("jdbc:hive2://127.0.0.1:10091"); config.setJdbcUrl("jdbc:hive2://127.0.0.1:10091/db_test"); DS = new HikariDataSource(config); TEMPLATE = new JdbcTemplate(DS); } @AfterClass public static void afterClass() throws Exception { DS.close(); } @Test public void testCreateDb() throws Exception { TEMPLATE.execute("CREATE DATABASE db_test"); } @Test public void testCreateTable() throws Exception { TEMPLATE.execute("CREATE TABLE IF NOT EXISTS t_student(id INT,name string,major string)"); log.info("创建t_student表成功"); } @Test public void testInsert() throws Exception { int update = TEMPLATE.update("INSERT INTO TABLE t_student(id,name,major) VALUES(?,?,?)", p -> { p.setInt(1, 10087); p.setString(2, "throwable"); p.setString(3, "math"); }); log.info("写入t_student成功,更新记录数:{}", update); // 这里比较神奇,数据写入了,返回的update数量为0 } @Test public void testSelect() throws Exception { List<Student> result = TEMPLATE.query("SELECT * FROM t_student", rs -> { List<Student> list = new ArrayList<>(); while (rs.next()) { Student student = new Student(); student.setId(rs.getLong("id")); student.setName(rs.getString("name")); student.setMajor(rs.getString("major")); list.add(student); } return list; }); // 打印日志:查询t_student成功,结果:[HiveJdbcTest.Student(id=10087, name=throwable, major=math)] log.info("查询t_student成功,结果:{}", result); } @Data private static class Student { private Long id; private String name; private String major; } } 复制代码
可能遇到的问题
下面小结一下可能遇到的问题。
Java虚拟机启动失败
目前定位到是Hadoop
无法使用JDK[9+
的任意版本JDK
,建议切换为任意JDK8
的小版本。
出现找不到Hadoop执行文件异常
确保已经把winutils
中的hadoop-3.3.0\bin
目录下的hadoop.dll
和winutils.exe
文件拷贝到Hadoop
的解压目录的bin
文件夹中。
start-all.cmd
脚本执行时有可能出现找不到批处理脚本的异常。此问题在公司的开发机出现过,在家用的开发机没有重现,具体解决方案是在start-all.cmd
脚本的首行加入cd $HADOOP_HOME
,如cd E:\LittleData\hadoop-3.3.0
。
无法访问localhost:50070
一般是因为hdfs-site.xml
配置遗漏了dfs.http.address
配置项,添加:
<property> <name>dfs.http.address</name> <value>0.0.0.0:50070</value> </property> 复制代码
然后调用stop-all.cmd
,再调用start-all.cmd
重启Hadoop
即可。
Hive连接MySQL异常
注意MySQL
的驱动包是否已经正确拷贝到$HIVE_HOME/lib
下,并且检查javax.jdo.option.ConnectionURL
等四个属性是否配置正确。如果都正确,注意是否MySQL
的版本存在问题,或者服务的版本与驱动版本不匹配。
Hive找不到批处理文件
一般描述是'xxx.cmd' is not recognized as an internal or external command...
,一般是Hive
的命令执行时的异常,需要把Hive 1.x
的源码包的bin
目录下的所有.cmd
脚本拷贝到$HIVE_HOME/bin
对应的目录下。
文件夹权限问题
常见如CreateSymbolicLink
异常,会导致Hive
无法使用INSERT
或者LOAD
命令写入数据。出现这类问题可以通过下面方式解决:
Win + R
然后运行gpedit.msc
- 计算机设置 -Windows
设置 — 安全设置 - 本地策略 - 用户权限分配 - 创建符号链接 - 添加当前用户。
或者直接使用管理员账号或者管理员权限启动CMD
,然后执行对应的脚本启动Hadoop
或者Hive
。
SessionNotRunning异常
启动HiveServer2
中或者外部客户端连接HiveServer2
时候有可能出现此异常,具体是java.lang.ClassNotFoundException: org.apache.tez.dag.api.TezConfiguration
的异常。解决方案是:配置文件hive-site.xml
中的hive.execution.engine
属性值由tez
修改为mr
,然后重启HiveServer2
即可。因为没有集成tez
,重启后依然会报错,但是60000
ms后会自动重试启动(一般重试后会启动成功):
这算是一个遗留问题,但是不影响客户端正常连接,只是启动时间会多了60
秒。
HiveServer2端口冲突
修改配置文件hive-site.xml
中的hive.server2.thrift.port
属性值为未被占用的端口,重启HiveServer2
即可。
数据节点安全模式异常
一般是出现SafeModeException
异常,提示Safe mode is ON
。通过命令hdfs dfsadmin -safemode leave
解除安全模式即可。
AuthorizationException
常见的是Hive
通过JDBC
客户端连接HiveServer2
服务时候会出现这个异常,具体是信息是:User: xxx is not allowed to impersonate anonymous
。这种情况只需要修改Hadoop
的配置文件core-site.xml
,添加:
<property> <name>hadoop.proxyuser.xxx.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.xxx.groups</name> <value>*</value> </property> 复制代码
这里的xxx是指报错时候具体的系统用户名,例如笔者开发机的系统用户名为doge
然后重启Hadoop
服务即可。
MapRedTask的权限问题
常见的是Hive
通过JDBC
客户端连接HiveServer2
服务执行INSERT
或者LOAD
操作时候抛出的异常,一般描述是Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask. Permission denied: user=anonymous, access=EXECUTE, inode="/tmp/hadoop-yarn":xxxx:supergroup:drwx------
。通过命令hdfs dfs -chmod -R 777 /tmp
赋予匿名用户/tmp
目录的读写权限即可。
小结
没什么事最好还是直接在Linux
或者Unix
系统中搭建Hadoop
和Hive
的开发环境比较合理,Windows
系统的文件路径和权限问题会导致很多意想不到的问题。本文参考了大量互联网资料和Hadoop
和Hive
的入门书籍,这里就不一一贴出,站在巨人的肩膀上。
(本文完 c-4-d e-a-20201102)