最近需要使用hive做离线计算,但是现有的样例数据是mysql导出的sql文件。进到hive直接source导入是不行的,会报错,因为CREATE的语法貌似不太一样。找了一圈也没有直接能处理整个数据库迁移的,使用sqoop也需要一个一个表的导入,还好sqoop可以创建表,不然还得自己写创建表的语句就太崩溃了。sqoop会报很多错,一个个解决,用网上的说法和打地鼠样的,哈哈。解决方法在第二节配置里有写。然后因为懒,每个表的迁移执行命令写成了脚本循环去跑,表名提取写了个函数提取了,所以整个要做的就是整个sql文件导入到mysql中,hive创建数据库,配置脚本然后执行。
一、安装部署sqoop
1、下载解压
http://mirror.bit.edu.cn/apache/sqoop/1.4.7/sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz
2、配置/conf/sqoop-env.sh
把template拷贝一下,添加以下内容
export HADOOP_COMMON_HOME=/opt/hadoop-2.7.7
export HADOOP_MAPRED_HOME=/opt/hadoop-2.7.7
export HIVE_HOME=/opt/apache-hive-2.3.6-bin
3、添加lib
将mysql-connector的jar包拷贝到sqoop的lib目录下。
4、测试
列出mysql所有的表:
./sqoop list-tables --connect jdbc:mysql://localhost:3306/test?useSSL=false --username root --password 123456
会列出所有的表,相当于mysql下执行show tables;
二、系统配置
1、拷贝hive的hive-site.xml复制到conf目录下
未拷贝涉及报错ERROR hive.HiveConfig: Could not load org.apache.hadoop.hive.conf.HiveConf
2、配置环境变量
vim /etc/profile,添加如下:
export CLASSPATH=.:$HIVE_HOME/lib
export HIVE_CONF_DIR=/opt/apache-hive-2.3.6-bin/conf
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HIVE_HOME/lib/*
export SQOOP_HOME=/opt/sqoop-1.4.7.bin__hadoop-2.6.0
保存后记得source一下,未配置可能报错涉及HiveConf
3、文件夹所属用户
使用root用户解压,不知道为什么解压后所有文件的用户均是非root用户。要保持和hive那些的用户一致。
chown -R root:root sqoop-1.4.7.bin__hadoop-2.6.0
4、报错ERROR Could not register mbeans java.security.AccessControlException: access denied
解决方法:
#vim $JAVA_HOME/jre/lib/security/java.policy
在grant{}内部添加如下内容:
permission javax.management.MBeanTrustPermission "register";
详细报错如下:
main ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTrustPermission" "register")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:457)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:585)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.checkMBeanTrustPermission(DefaultMBeanServerInterceptor.java:1848)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:322)
at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:522)
at org.apache.logging.log4j.core.jmx.Server.register(Server.java:380)
……
5、报错exec.DDLTask: java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.ObjectMapper.readerFor
解决方法:
是由于sqoop的lib目录下面的jackson相关的包的版本与hive的lib目录下的jackson相关jar包的版本不一致导致的。
将hive下的所有jackson相关jar全部copy到sqoop的lib目录下面
#cp -r $HIVE_HOME/lib/jackson-* $SQOOP_HOME/lib/
然后将重复的低版本的全部删掉就可以了。(切记不要全部删除,有些是不重复的)
详细报错如下:
ERROR exec.DDLTask: java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.ObjectMapper.readerFor(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/ObjectReader;
at org.apache.hadoop.hive.common.StatsSetupConst$ColumnStatsAccurate.<clinit>(StatsSetupConst.java:165)
at org.apache.hadoop.hive.common.StatsSetupConst.parseStatsAcc(StatsSetupConst.java:297)
at org.apache.hadoop.hive.common.StatsSetupConst.setBasicStatsState(StatsSetupConst.java:230)
at org.apache.hadoop.hive.common.StatsSetupConst.setBasicStatsStateForCreateTable(StatsSetupConst.java:292)
at org.apache.hadoop.hive.ql.plan.CreateTableDesc.toTable(CreateTableDesc.java:839)
FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. com.fasterxml.jackson.databind.ObjectMapper.readerFor(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/ObjectReader;
20/04/22 23:51:39 ERROR ql.Driver: FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. com.fasterxml.jackson.databind.ObjectMapper.readerFor(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/ObjectReader;
参考链接:
https://www.clzg.cn/article/74991.html
三、Sqoop命令说明
参数说明
--connect #关系型数据库连接
--username #关系型数据库连接用户名
--password #关系型数据库连接密码
--table #关系型数据库的表
--split-by #如果-m的数量不为1,则一定要加上该参数且最好是数值类型,否则会报错
--direct #快速模式,使用mysql自带的mysqldump导出数据
--delete-target-dir #如果hdfs的目录已经存在则先删除
--target-dir #导入到hdfs时的目标目录
--export-dir #从hdfs导出时的源目录
--fields-terminated-by #导入到hdfs时的hdfs文件分隔符
--input-fields-terminated-by #从hdfs导出时的hdfs文件分隔符
--hive-drop-import-delims #导入hive中的数据某列中如果有换行符或回车键可以删除
--hive-database #hive的数据库
--hive-table #hive的表
--hive-overwrite #覆盖之前的分区插入数据
--hive-partition-key #hive分区字段
--hive-partition-value #hive分区值
-m #指定map数量,也是生成的文件数。不要大于hdfs的节点数量
特别说明:如果指定的map的数量不为1,则一定要加上–split-by参数且最好是数值类型
例如一个迁移apps表,在hive中有新建数据库test,但没有此表。
./sqoop import \
--connect "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8" \
--username root \
--password 123456 \
--table apps \
--delete-target-dir \
--hive-drop-import-delims \
--fields-terminated-by "\t" \
--lines-terminated-by "\n" \
--hive-import \
--hive-overwrite \
--create-hive-table \
--hive-database test \
--hive-table apps \
-m 1
另外还有
--query "SELECT * FROM emp WHERE 1=1" #只迁移select语句后的数据
--null-string '\\N' #处理string类型的null值,用‘\\N’替代
--null-non-string '\\N' #处理非string类型的null值,用‘\\N’替代
四、迁移数据库的脚本
sql文件需要自己source导入到mysql。hive里的数据库还是要自己创建的。
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `test`;
新建一个sqoop_import.sh文件,如果没有执行权限,需要chmod 777一下,脚本如下:
#!/bin/sh
#mysql
mysql_name=root
mysql_passwd=123456
mysql_conn="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8"
#hive
hive_db=test
tables=(apps abilities)
for hive_table in ${tables[*]};do
echo ${hive_table}
${SQOOP_HOME}/bin/sqoop import \
--connect ${mysql_conn} \
--username ${mysql_name} \
--password ${mysql_passwd} \
--table ${hive_table} \
--delete-target-dir \
--hive-drop-import-delims \
--fields-terminated-by "\t" \
--lines-terminated-by "\n" \
--hive-import \
--hive-overwrite \
--create-hive-table \
--hive-database ${hive_db} \
--hive-table ${hive_table} \
--null-string '\N' \
--null-non-string '\N' \
-m 1 \
>> sqoop_import.log
done
tables=(apps abilities)变量括号里的就是表名列表字符串,可以使用show tables自己拼一下。我懒,所以就写了个提取的函数如下:
public static String getMysqlTables() {
File fileIn = new File("test.sql");
BufferedReader reader = null;
String keyword = "CREATE TABLE";
String tableList = "";
try {
reader = new BufferedReader(new FileReader(fileIn));
String tempString = null;
while ((tempString = reader.readLine()) != null) {
if (tempString.indexOf(keyword) > -1) {
String tableName = tempString.substring(tempString.indexOf(keyword)+14);
tableName = tableName.substring(0, tableName.indexOf('`'));
System.out.println(tableName);
tableList = tableList + tableName + " ";
}
}
reader.close();
return tableList;
} catch (IOException e) {
e.printStackTrace();
return "";
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}