es导出hive遇到的问题汇总
首先直接上干货:
步骤
- 导入elasticsearch-hadoop-hive-5.5.2.jar包
- 创建hive外部表,数据源设置为es中,添加相应的配置
- 创建内部表拉取数据
add jar file:///home/hadoop/liubx/elasticsearch-hadoop-hive-5.5.2.jar;
add jar file:///home/hadoop/hive-1.2.1/lib/es-hadoop.jar; #代码见后面
use estest;
drop table if exists ext_es_test;
CREATE EXTERNAL TABLE ext_es_test(
typeid string,
cs_app string,
ipAddress string,
createTime timestamp
)
row format delimited fields terminated by '\t'
collection items terminated by ','
map keys terminated by ':'
STORED BY 'org.elasticsearch.hadoop.hive.EsStorageHandler'
TBLPROPERTIES(
'es.nodes' = 'master:9200',
'es.index.auto.create' = 'false',
'es.mapping.date.rich' = 'true',
'es.date.format' = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'||yyyy-MM-dd HH:mm:ss||yyyy-MM-dd",
'es.ser.reader.value.class' = 'fjjf.es.EsValueReader',
'es.resource' = 'buriedpointinfo_bumsg/',
'es.read.metadata' = 'true',
'es.mapping.names' = 'typeid:_metadata._type, cs_app:cs_app,ipAddress:ipAddress,createTime:createTime');
#注释:
'es.nodes' -- es连接node信息
'es.index.auto.create' -- 是否自动创建索引
'es.mapping.date.rich' -- 是否启用date自动转换,json中没有时间类型,es封装了一个Date类型存放特定格式的时间字符串。可以通过mapping指定format
'es.date.format' -- 自定义dateformat
'es.ser.reader.value.class' -- 自定义reader,由于本地es中存放时间采用了("yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"),导出时hive只能选择一种类型存取timestamp或者date
'es.resource' -- es索引
'es.read.metadata' -- 是否读取es元数据字段
'es.mapping.names' -- es 和hive 字段映射
#分页查询
select * from estest.ext_es_test_bumsg limit 10;
#创建内部表分页查询
drop table if exists itn_es_test;
create table itn_es_testas
select * from estest.itn_es_test;
遇到的问题:
-
es中字段较多,不需要导出所有字段。
解决: 通过'es.mapping.names' 配置筛选所需要的字段。 -
在hadoop-2.8版本下导出的时候经常会报Error: java.lang.ClassNotFoundException: org.apache.commons.httpclient.util.xxxx,但是hive/lib目录下确实有该类。手动添加后还是无法解决
解决: 将版本切到2.7不会出现该问题,但是根本原因还是没有找到,待解决。
20180402更新:
解决方法:
URL:
http://master:8088/taskdetails.jsp?jobid=job_1517535406767_315524&tipid=task_1517535406767_315524_m_000137
-----
Diagnostic Messages for this Task:
Error: java.lang.ClassNotFoundException: org.apache.commons.httpclient.util.DateParseException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.newInstance(Class.java:412)
at org.elasticsearch.hadoop.util.ObjectUtils.instantiate(ObjectUtils.java:41)
at org.elasticsearch.hadoop.util.ObjectUtils.instantiate(ObjectUtils.java:52)
at org.elasticsearch.hadoop.util.ObjectUtils.instantiate(ObjectUtils.java:48)
尝试在hive命令行手动添加jar失效之后,对比了hadoop2.7和hadoop2.8的jar包内容。发现hadoop-2.8.1/share/hadoop/common/lib目录下少了commons-httpclient-3.1.jar,由于本地hadoop2.8是编译安装的,所以怀疑是编译安装的时候没有将该依赖包打进去,对比官方下载的hadoop2.8二进制包发现也没有该jar包。
通过查看hadoop-2.8。0的change-log(https://issues.apache.org/jira/browse/HADOOP-13382)
接下来,只需将es-hadoop依赖的commons-httpclient jar加入到所有的hadoop节点即可。
- hadoop classpath | tr ':' '\n'
- es中导出的Date类型数据既有时间戳也有日期字符串,在存取到hive的时候发现以下问题;
1)时间戳无法正常转换为时间戳
37304150-01-31 14:25:515:51.616
37301390-01-10 14:25:515:51.616
37300635-01-05 14:25:515:51.616
37289863-10-16 14:25:515:51.616
2)时间字符串无法正常转换为时间戳,会报类转换异常 TextWritable unable cast to TimestampWritable
解决: 通过指定'es.ser.reader.value.class' ,自定义reader处理该异常,将所有时间格式字符串统一格式输出。
通过查看elasticsearch-hadoop的源码,发现es-hadoop在解析Date类型的时候使用的是DatatypeConverter.parseDateTime(value)。(无法应对我们自定义的各种时间格式,甚至包括UTC时区的时间。)
@Override
protected Object parseDate(Long value, boolean richDate) {
return (richDate ? new TimestampWritable(new Timestamp(value)) : processLong(value));
}
@Override
protected Object parseDate(String value, boolean richDate) {
return (richDate ? new TimestampWritable(new Timestamp(DatatypeConverter.parseDateTime(value).getTimeInMillis())) : parseString(value));
}
源码修改(默认情况下richDate为true,如果为false,则时间戳会当作Long处理,时间字符串当作Text处理)
@Override
protected Object parseDate(String value, boolean richDate) {
Date d = null;
if(StringUtil.isNotEmpty(dateFormat)) {
try {
if(value.length() == 13){
return new TimestampWritable(new Timestamp(Long.valueOf(value)));
}else{
d = DateUtil.parseDate(value, Arrays.asList(dateFormat.split("\\|\\|")));
}
} catch (DateParseException e) {
e.printStackTrace();
System.out.println("parse failed please check the dateFormat");
}
} else {
d = DdfatatypeConverter.parseDateTime(value).getTime();
}
return new TimestampWritable(new Timestamp(d.getTime()));
}
protected Object parseDate(long value, boolean richDate) {
Date d = new Date(value);
return new TimestampWritable(new Timestamp(d.getTime()));
}
- 最后遇到一个小问题,idea打包jar的时候太大,上传服务器速度较慢,将一些依赖去除打包后解决。
解决: 在pom.xml中指定scope为proviede,打包的时候忽略。
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-hadoop</artifactId>
<version>5.5.0</version>
<scope>provided</scope>
</dependency>
总结:
es国内资料比较杂乱,很多都还是2.x的。
建议研读官方文档,es这块配置项比较多,而且很多功能也许在配置项中已经可以找到解决方案了,缺乏的是仔细去研读和加深理解。这个我自己也正在学习的路上,哈哈!
另外,es-hadoop导出性能一般,20m/s左右,3个节点。有待提升,有时间可以对比下es-spark和java api效果。