现有如下,一堆数据文件,以日期命名,因为需要按分区加载到一个分区表中。
如果手动加载,会浪费很多时间,所以有两种方式实现自动遍历并加载到hive表。
第一种:JAVA代码
连接hdfs,读取每一个数据,远程执行hive -e,这样会浪费大量的中间过程。
package com.czxy.demo05;
import net.neoremind.sshxcute.core.ConnBean;
import net.neoremind.sshxcute.core.Result;
import net.neoremind.sshxcute.core.SSHExec;
import net.neoremind.sshxcute.task.impl.ExecCommand;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.log4j.Logger;
import java.net.URI;
/**
* app_traffic
*/
public class LoadData02 {
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
//该类的对象是一个文件系统对象
FileSystem fileSystem=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
//获取某一目录下的所有文件
FileStatus stats[]=fileSystem.listStatus(new Path("/HistoryDatas/app_traffic_data_output"));
//设置链接的服务器
ConnBean connBean=new ConnBean("hadoop01", "root","123456" );
//链接服务器
SSHExec sshExec =SSHExec.getInstance(connBean);
sshExec.connect();
//控制台输出对象
Logger test = Logger.getLogger("Test");
//遍历
for(int i = 0; i < stats.length; ++i){
//获取文件路径
Path path = stats[i].getPath();
//获取文件名中的日期
String date = path.getName().split("\\.")[0];
String[] split = date.split("-");
//截取出年月日,按分区加载数据
String year =split[0];
String month = split[1];
String day = split[2];
//设置执行的命令 注意,hive -e 只认准双引号,所以使用转义的双引号而不是单引号↓ 分区加DT是为了和表中字段区别
ExecCommand execCommand=new ExecCommand("hive -e \"use telecom;LOAD DATA INPATH '"+path+"' OVERWRITE INTO TABLE app_traffic PARTITION (DTyear='"+year+"',DTmonth='"+month+"',DTday='"+day+"');\"");
//执行命令
Result exec = sshExec.exec(execCommand);
test.info("当前进度:"+i+" 总进度:"+stats.length);
}
//关闭连接
sshExec.disconnect();
//关闭HDFS文件系统对象
fileSystem.close();
}
}
第二种:shell脚本
脚本执行省去了建立连接和本机与节点传输数据的时间,更快!
#!/bin/bash
#将要导入的文件路径写到files0.txt备用
hadoop fs -ls /HistoryDatas/app_traffic_data_output/ | awk '{print $8}' > files0.txt
#按行读取files0.txt每一行是一个路径
#路径示例:/HistoryDatas/app_traffic_data_output/2018-08-29.txt
cat ./files0.txt | while read line
do
#截取出日期字符串
VarDate=$(echo $line | awk -F '/' {'print $4'} | awk -F '.' {'print $1'} )
#分别获取年月日,按分区加载数据
VarYear=$(echo $VarDate | awk -F '-' '{print $1}')
VarMonth=$(echo $VarDate | awk -F '-' '{print $2}')
VarDay=$(echo $VarDate | awk -F '-' '{print $3}')
#执行数据加载
hive -e "use telecom;LOAD DATA INPATH '"${line}"' OVERWRITE INTO TABLE app_traffic PARTITION (DTyear='"${VarYear}"',DTmonth='"${VarMonth}"',DTday='"${VarDay}"');"
done
#$()表示提取命令的标准输出 shell 赋值符号两边不能有空格
#AWK没有标准输入,如果把$line放在后面,awk会把整个文件都读走,所以使用管道,让awk只处理管道里的内容
#这样就不会造成只执行一次循环的情况了
上面这个是第一版,赶工赶出来的,下面这个经过优化,速度更快(快了10倍不止)
#!/bin/bash
#将要导入的文件路径写到files0.txt备用
hadoop fs -ls /HistoryDatas/networkqualityinfo_db_data_output/ | awk '{print $8}' > files0.txt
#按行读取files0.txt每一行是一个路径
echo 'use telecom;' >> tmpSQL.sql
#路径示例:/HistoryDatas/app_traffic_data_output/2018-08-29.txt
cat ./files0.txt | while read line
do
#截取出日期字符串
VarDate=$(echo $line | awk -F '/' {'print $4'} | awk -F '.' {'print $1'} )
#分别获取年月日,按分区加载数据
VarYear=$(echo $VarDate | awk -F '-' '{print $1}')
VarMonth=$(echo $VarDate | awk -F '-' '{print $2}')
VarDay=$(echo $VarDate | awk -F '-' '{print $3}')
#执行数据加载
echo 'LOAD DATA INPATH "'"${line}"'" OVERWRITE INTO TABLE networkqualityinfo PARTITION (DTyear="'"${VarYear}"'",DTmonth="'"${VarMonth}"'",DTday="'"${VarDay}"'");' >> tmpSQL.sql
#hive -e "use telecom;LOAD DATA INPATH '"${line}"' OVERWRITE INTO TABLE networkqualityinfo PARTITION (DTyear='"${VarYear}"',DTmonth='"${VarMonth}"',DTday='"${VarDay}"');"
done
#$()表示提取命令的标准输出 shell 赋值符号两边不能有空格
#AWK没有标准输入,如果把$line放在后面,awk会把整个文件都读走,所以使用管道,让awk只处理管道里的内容
#这样就不会造成只执行一次循环的情况了
生成的tmpSQL.sql里面第一行为use telecom;
是使用数据库,然鹅,第二行的path估计会是空的,需要手动修改。
(load data inpath “” 那个双引号估计是空的)
如果查看生成的.sql文件没问题,就可以使用hive -f 执行了
由于hive -e 需要重复的启动hive和关闭hive浪费了不少时间,
这样可以节约大量的时间。