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 ...
随机推荐
- CH1401 兔子与兔子
#include<bits/stdc++.h> using namespace std; ,p=; typedef unsigned long long ULL;//自然溢出 ULL f[ ...
- python 处理图像出现The lower bounary is neither an array of the same size and same type as src, nor a scalar in function inRange
在用python处理图像过程中出现如下错误 导致这个错误的原因是im是二维,而lower_green和upper_green是三维,所以无法用inRange处理. 由上图可以看出image本来是具有高 ...
- 初识 Knative: 跨平台的 Serverless 编排框架
Knative 是什么 Knative 是 Google 在 2018 的 Google Cloud Next 大会上发布的一款基于 Kubernetes 的 Serverless 框架.Knativ ...
- 深度学习——Xavier初始化方法
“Xavier”初始化方法是一种很有效的神经网络初始化方法,方法来源于2010年的一篇论文<Understanding the difficulty of training deep feedf ...
- input上传图片并预览
首先说一下input 大家都知道上传文件,图片是通过input 的file进行上传的. 1. 首先是样式 大家都知道input在HTML的代码为 <input type="file&q ...
- Asp.net MVC中如何获取控制器的名称
如果在代码中 当前controller.action的获取RouteData.Route.GetRouteData(this.HttpContext).Values["controller& ...
- SuperSocket通过本地证书仓库的证书来启用 TLS/SSL
你也可以通过本地证书仓库的证书,而不是使用一个物理文件. 你只需要在配置中设置你要使用的证书的storeName和thumbprint: <server name="EchoServe ...
- C# 序列类为 xml 可以使用的特性大全
本文告诉大家如何使用序列类,以及序列时可以用到的特性,特性的作用和一些容易被问的问题 最近我在把项目文件修改为 VisualStudio 2017 的格式,请看从以前的项目格式迁移到 VS2017 新 ...
- 【codeforces 789A】Anastasia and pebbles
[题目链接]:http://codeforces.com/contest/789/problem/A [题意] 有n种物品,每种物品有wi个; 你有两个口袋,每个口袋最多装k个物品; 且口袋里面只能装 ...
- 【b703】矩阵取数游戏
Time Limit: 1 second Memory Limit: 50 MB [问题描述] 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规 ...