Hive直接读取Hbase及MySQL数据
0.概述
Hive对外提供了StorageHandler接口,提供了访问各种存储组件中的数据的能力。Hbase提供了HbaseStorageHandler,使得hive可以通过建立外部映射表访问hbase中的数据。但是,公司CDH集群的版本比较低,不支持新版hive原生的JdbcStorageHandler。因而要访问JDBC数据源中的数据,只能通过添加第三方类库实现。
1.Hive 访问Hbase
use ods_sdb;
create external table if not exists ods_sdb.$v_table(
ajbs string comment '标识',
hytcyqdrq string comment '合议庭成员确定日期',
splcbgkyy string comment '审判流程不公开原因',
ajgyxx_stm string comment '实体码',
bygksplc string comment '不宜公开审判流程',
jbfy string comment '经办法院',
labmbs string comment '立案部门标识',
splcygk string comment '审判流程已公开',
ajgyxx_ajbs string comment '案件标识',
ajmc string comment '案件名称',
stm string comment '实体码',
cbbmbs string comment '承办部门标识'
) comment '概要信息'
stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
with serdeproperties (
'hbase.columns.mapping' = ':key,f:anjiangaiyaoxinxi.heyitingchengyuanquedingriqi,f:anjiangaiyaoxinxi.shenpanliuchengbugongkaiyuanyin,f:anjiangaiyaoxinxi.shitima,f:anjiangaiyaoxinxi.buyigongkaishenpanliucheng,f:anjiangaiyaoxinxi.jingbanfayuan,f:anjiangaiyaoxinxi.lianbumenbiaozhi,f:anjiangaiyaoxinxi.shenpanliuchengyigongkai,f:anjiangaiyaoxinxi.anjianbiaozhi,f:anjiangaiyaoxinxi.anjianmingcheng,f:shitima,f:anjiangaiyaoxinxi.chengbanbumenbiaozhi'
) tblproperties ( 'hbase.table.name' = 'aj_15_baseinfo')
stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler':底层数据在hbase中存储的话,需要指定该类进行处理。
with serdeproperties 中指定hbase中的字段与hive外部表的对应映射关系,其中::key为hbase的rowkey,其他字段按照外部表的定义顺序,依次以列族:字段名的顺序排列,用半角逗号分隔。
tblproperties 中指定对应的hbase表名。
需要注意的是:不要对该表进行复杂的条件查询,where中最好只使用rowkey对应的字段进行条件判断。
建议:只对该表进行数据的导出操作,即从hbase中把数据导出到hive实体表中。
2.Hive 访问MySQL
如同前面的描述,hive从 HIVE-1555 才开始支持自带的JdbcStorageHandler,在低版本的hive中要直接访问jdbc中的数据,只能通过第三方的JdbcStorageHandler实现。
第三方源码:https://github.com/qubole/Hive-JDBC-Storage-Handler
使用方式:
- git clone https://github.com/qubole/Hive-JDBC-storage-Handler.git
- mvn clean install -Phadoop-1
- add jar 注意需要加入mysql jdbc驱动
add jar /home/csc/20190729/qubole-hive-JDBC.jar;
add jar /home/csc/20190729/udf-1.0.jar;
use ods_sdb;
create external table if not exists ods_sdb.$v_table(
id string comment 'id',
fdm string comment '案件标识',
cBh string comment '当事人主键',
cCxm string comment '案件查询码',
nBgrpxh string comment '被告人排序号',
nFzje string comment '犯罪金额',
nSf string comment '特殊身份',
cSf string comment '特殊身份中文',
nZy string comment '职业',
cZy string comment '职业中文',
create_time string comment '创建时间'
) comment '当事人情况'
stored by 'org.apache.hadoop.hive.jdbc.storagehandler.JdbcStorageHandler'
tblproperties (
'mapred.jdbc.driver.class'='com.mysql.jdbc.Driver',
'mapred.jdbc.url'='jdbc:mysql://ip:port/fb_data?characterEncoding=utf8',
'mapred.jdbc.username'='username',
'mapred.jdbc.input.table.name'='fb_15_dsr',
'mapred.jdbc.password'='password',
'mapred.jdbc.hive.lazy.split'= 'false'
);
tblproperties中的各项配置,可以参考git上的描述。
问题定位解决:
(涉及到具体机器资源、环境,非重复复现的问题,修改方案,也只是临时性解决)
在实际使用过程中,处理当事人数据时,反复出现jdbc链接超时的情况,导致数据导出任务失败。
问题定位过程如下:
1.非标任务首次执行比较耗时,执行成功后,重跑速度相对较快;
2.show processlist;发现,在执行非标数据导出任务时,会优先执行一个count()的sql,比较耗时。分析非标数据在mysql中的存储,首先量级已达千万,其次,存储引擎采用的是InnoDB,对count()的执行需要遍历全表;
3.真正执行数据导出任务时,分为两个mapper执行select xxx的操作,时间消耗较少。
结合mysql的运行日志及数据导出任务的日志,基本定位到为count(*)导致的会话超时。
问题解决过程如下:
1.首先阅读JdbcStorageHandler的源码,定位count(*)的来源:
/*
* Copyright 2013-2015 Qubole
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hive.wrapper;
import java.io.IOException;
import org.apache.hadoop.mapred.RecordReader;
import org.apache.hadoop.mapred.InputSplit;
import org.apache.hadoop.mapred.FileSplit;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapreduce.lib.db.DBConfiguration;
import org.apache.hadoop.mapreduce.InputFormat;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.TaskAttemptID;
import org.apache.hadoop.hive.shims.ShimLoader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.sql.*;
import org.apache.hadoop.hive.jdbc.storagehandler.Constants;
import org.apache.hadoop.hive.jdbc.storagehandler.JdbcDBInputSplit;
public class RecordReaderWrapper<K, V> implements RecordReader<K, V> {
private static final Log LOG = LogFactory.getLog(RecordReaderWrapper.class);
private org.apache.hadoop.mapreduce.RecordReader<K, V> realReader;
private long splitLen; // for getPos()
// expect readReader return same Key & Value objects (common case)
// this avoids extra serialization & deserialazion of these objects
private K keyObj = null;
protected V valueObj = null;
private boolean firstRecord = false;
private boolean eof = false;
private Connection conn = null;
private String tblname = null;
private DBConfiguration delegate = null;
private long taskIdMapper = 0;
private boolean lazySplitActive = false;
private long count = 0;
private int chunks = 0;
public RecordReaderWrapper(InputFormat<K, V> newInputFormat,
InputSplit oldSplit, JobConf oldJobConf, Reporter reporter)
throws IOException {
TaskAttemptID taskAttemptID = TaskAttemptID.forName(oldJobConf
.get("mapred.task.id"));
if (taskAttemptID !=null) {
LOG.info("Task attempt id is >> " + taskAttemptID.toString());
}
if(oldJobConf.get(Constants.LAZY_SPLIT) != null &&
(oldJobConf.get(Constants.LAZY_SPLIT)).toUpperCase().equals("TRUE")){
lazySplitActive = true;
ResultSet results = null;
Statement statement = null;
delegate = new DBConfiguration(oldJobConf);
try{
conn = delegate.getConnection();
statement = conn.createStatement();
results = statement.executeQuery("Select Count(*) from " + oldJobConf.get("mapred.jdbc.input.table.name"));
results.next();
count = results.getLong(1);
chunks = oldJobConf.getInt("mapred.map.tasks", 1);
LOG.info("Total numer of records: " + count + ". Total number of mappers: " + chunks );
splitLen = count/chunks;
if((count%chunks) != 0)
splitLen++;
LOG.info("Split Length is "+ splitLen);
results.close();
statement.close();
}
catch(Exception e){
// ignore Exception
}
}
org.apache.hadoop.mapreduce.InputSplit split;
if(lazySplitActive){
((JdbcDBInputSplit)(((InputSplitWrapper)oldSplit).realSplit)).setStart(splitLen);
((JdbcDBInputSplit)(((InputSplitWrapper)oldSplit).realSplit)).setEnd(splitLen);
}
if (oldSplit.getClass() == FileSplit.class) {
split = new org.apache.hadoop.mapreduce.lib.input.FileSplit(
((FileSplit) oldSplit).getPath(),
((FileSplit) oldSplit).getStart(),
((FileSplit) oldSplit).getLength(), oldSplit.getLocations());
} else {
split = ((InputSplitWrapper) oldSplit).realSplit;
}
// create a MapContext to pass reporter to record reader (for counters)
TaskAttemptContext taskContext = ShimLoader.getHadoopShims()
.newTaskAttemptContext(oldJobConf,
new ReporterWrapper(reporter));
try {
realReader = newInputFormat.createRecordReader(split, taskContext);
realReader.initialize(split, taskContext);
// read once to gain access to key and value objects
if (realReader.nextKeyValue()) {
firstRecord = true;
keyObj = realReader.getCurrentKey();
valueObj = realReader.getCurrentValue();
} else {
eof = true;
}
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}
results = statement.executeQuery("Select Count(*) from " + oldJobConf.get("mapred.jdbc.input.table.name"));
可以看到在执行数据导出任务前,首先会获取该表的总行数,用于进行任务的分割。但是,这里的触发条件是
'mapred.jdbc.hive.lazy.split'= 'true'
但是,该操作配置为false的情况下,仍然会默认执行count(*)的操作。
- 添加自定义表量级的阈值定义:
count = oldJobConf.getInt("mapred.jdbc.input.table.count", 20000000);
// results = statement.executeQuery("Select Count("+ (key==null?"*":key) + ") from " + oldJobConf.get("mapred.jdbc.input.table.name"));
// results.next();
// count = results.getLong(1);
3.外部表定义修改:
添加 'mapred.jdbc.input.table.count'='3000000'
4.重新打包,并上传;
5.问题搞定!
Hive直接读取Hbase及MySQL数据的更多相关文章
- Spark读取Hbase中的数据
大家可能都知道很熟悉Spark的两种常见的数据读取方式(存放到RDD中):(1).调用parallelize函数直接从集合中获取数据,并存入RDD中:Java版本如下: JavaRDD<Inte ...
- hive和hbase本质区别——hbase本质是OLTP的nosql DB,而hive是OLAP 底层是hdfs,需从已有数据库同步数据到hdfs;hive可以用hbase中的数据,通过hive表映射到hbase表
对于hbase当前noSql数据库的一种,最常见的应用场景就是采集的网页数据的存储,由于是key-value型数据库,可以再扩展到各种key-value应用场景,如日志信息的存储,对于内容信息不需要完 ...
- Spark 读取HBase和SolrCloud数据
Spark1.6.2读取SolrCloud 5.5.1 //httpmime-4.4.1.jar // solr-solrj-5.5.1.jar //spark-solr-2.2.2-20161007 ...
- Hive综合HBase——经Hive阅读/书写 HBase桌子
社论: 本文将Hive与HBase整合在一起,使Hive能够读取HBase中的数据,让Hadoop生态系统中最为经常使用的两大框架互相结合.相得益彰. watermark/2/text/aHR0cDo ...
- 使用Hive或Impala执行SQL语句,对存储在HBase中的数据操作
CSSDesk body { background-color: #2574b0; } /*! zybuluo */ article,aside,details,figcaption,figure,f ...
- 使用Hive读取ElasticSearch中的数据
本文将介绍如何通过Hive来读取ElasticSearch中的数据,然后我们可以像操作其他正常Hive表一样,使用Hive来直接操作ElasticSearch中的数据,将极大的方便开发人员.本文使用的 ...
- IDEA中Spark读Hbase中的数据
import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.io.ImmutableBytesWr ...
- 关于mapreducer 读取hbase数据 存入mysql的实现过程
mapreducer编程模型是一种八股文的代码逻辑,就以用户行为分析求流存率的作为例子 1.map端来说:必须继承hadoop规定好的mapper类:在读取hbase数据时,已经有现成的接口 Tabl ...
- 使用MapReduce读取HBase数据存储到MySQL
Mapper读取HBase数据 package MapReduce; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hba ...
随机推荐
- CDN WAF功能开放公测 提升网络应用安全性能
阿里云CDN WAF功能,是指CDN融合了云盾Web应用防火墙(Web Application Firewall,简称 WAF)能力,在CDN节点上提供安全防护的功能,该功能目前已经开放公测. WAF ...
- Xib设计UITableViewCell然后动态加载
转自: http://www.2cto.com/kf/201202/120764.html (注:环境Mac OS X Lion 10.7.3 + Xcode 4.2.1 + iOS SDK 5.0. ...
- python2和python3一些不同
文件写法: #python2fp=file(filepath,'wb')#python3# fp = open(filepath, 'wb') 关于乱码问题: #python2 #coding=utf ...
- @noi.ac - 170@ 数数
目录 @description@ @solution@ @accepted code@ @details@ @description@ 求有多少对 1 ∼ n 的排列 (a, b) 满足 \(m \l ...
- Python os.getcwd() 方法
Python os.getcwd() 方法 Python OS 文件/目录方法 概述 os.getcwd() 方法用于返回当前工作目录. 语法 getcwd()方法语法格式如下: os.getcwd ...
- supersockets接收过滤器(ReceiveFilter)
接收过滤器(ReceiveFilter)用于将接收到的二进制数据转化成请求实例(RequestInfo). 实现一个接收过滤器(ReceiveFilter), 你需要实现接口 IReceiveFilt ...
- SQL 三个表练习(student,teacher,score)
- tp5 thinkphp5 多表关联查询 join查询
model下: $res = \think\Db::name('article') ->alias("a") //取一个别名 ->join('admin ad','a. ...
- 怎么实现Web聊天
如果你对web聊天这个事情没什么概念,那么最佳做法可能是:openfire+jsjac openfire是java做的开源xmpp服务器,jsjac是javascript做的开源的网页版xmpp客户端 ...
- Chrome 里的请求报错 " Provisional headers are shown"
之所以会出现这个警告,是因为去获取该资源的请求其实并(还)没有真的发生; 背景:提交表单,按钮点击<button>标签,触发事件,ajax发送请求,服务器返回信息; <button& ...