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 ...
随机推荐
- @bzoj - 2388@ 旅行规划
目录 @description@ @solution@ @accepted code@ @details@ @description@ 请你维护一个序列,支持两种操作: (1)某个区间 [x, y] ...
- codeforces2B.The least round way 题解 动态规划/模拟
题目出处:http://codeforces.com/problemset/problem/2/B 题目描述 给你一个 \(n \times n\) 的二维数组,它包含的元素都是非负整数.你需要寻找一 ...
- 2018-5-28-win10-uwp-动态修改ListView元素布局
title author date CreateTime categories win10 uwp 动态修改ListView元素布局 lindexi 2018-05-28 15:15:54 +0800 ...
- ngRoute
ngRoute 模块中包含以下内容, 名称 所属 作用 ngView DIRECTIVE 提供不同路由模板插入的视图层 $routeProvider PROVIDER 提供路由配置 $route SE ...
- HDU 2844 混合背包、
题意:一个人想买手表,给你n个价值的硬币,然后给你n个价值硬币对应的个数.但是呢,这个人只知道这个手表的价格不超过m元.问他最多能买多少种价值的手表 思路:dp背包专题 但是- - 一直不知道该怎么d ...
- BERT的通俗理解 预训练模型 微调
1.预训练模型 BERT是一个预训练的模型,那么什么是预训练呢?举例子进行简单的介绍 假设已有A训练集,先用A对网络进行预训练,在A任务上学会网络参数,然后保存以备后用,当来一个新 ...
- Vue 用第三方的库去实现动画效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【codeforces 761C】Dasha and Password(动态规划做法)
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- Linux 内核class_simple 接口
class_simple 接口意图是易于使用, 以至于没人会抱怨没有暴露至少一个包含设备的被 分配的号的属性. 使用这个接口只不过是一对函数调用, 没有通常的和 Linux 设备模型 关联的样板. 第 ...
- 关于MySQL中查询大数据量的情况下分页limit的性能优化
https://blog.csdn.net/weixin_37848710/article/details/80772725