Hbase
- 一、NoSQL非关系型数据库简介
-
- [1.NoSQL 的起因](#1.NoSQL 的起因)
- [2.NoSQL 的特点](#2.NoSQL 的特点)
- [3.NoSQL 面临的挑战](#3.NoSQL 面临的挑战)
- [4.NoSQL 的分类](#4.NoSQL 的分类)
- 二、HBase数据库概述
- [三、Hbase Shell:DDL语句操作](#三、Hbase Shell:DDL语句操作)
- [四、Hbase Shell:DML语句操作](#四、Hbase Shell:DML语句操作)
- 五、eclipse操作hbase
- 六、ORC文件转HFILE文件
- 七、Hbase表的预分Region
- 八、Hbase数据的批量导入+预分Region
- 九、HFILE文件转ORC文件
- 十、MR读写AVRO文件
- 十一、Hbase数据的批量导出
一、NoSQL非关系型数据库简介
1.NoSQL 的起因
sql
随着 Web 2.0 网站的兴起,传统的关系数据库已经无法适应 Web 2.0 网站,特别是超大规模和高并发的社交类型的 Web 2.0 纯动态网站;
NoSQL 数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。
1. 无法满足对海量数据的高效率存储和访问的需求
2. 无法满足对数据库的高可扩展性和高可用性的需求
3. 关系数据库无法存储和处理半结构化/非结构化数据
4. 关系数据库的事务特性对 Web 2.0 是不必要的
5. Web 2.0 无须进行复杂的 SQL 查询,特别是多表关联查询
2.NoSQL 的特点
sql
它所采用的数据模型并非关系型数据库的关系模型,而是类似键值、列簇、文档等的数据模型。它打破了长久以来关系型数据库与 ACID(原子性(Atomicity)、一致性(Consistency)隔离性(Isolation)和持久性(Durability))理论大一统的局面。
NoSQL 数据存储不需要固定的表结构,每一个元组可以有不一样的字段,每个元组可以根据需要增加一些自己的键值对,这样就不会局限于固定的结构,可以减少一些时间和空间的开销。
NoSQL 在大数据存取上具备关系型数据库无法比拟的性能优势。
1. 灵活的可扩展性
2. 大数据量和高性能
3. 灵活的数据模型,可以处理半结构化/非结构化的大数据
3.NoSQL 面临的挑战
sql
NoSQL 数据库的前景很被看好,但是要应用到主流的企业还有许多困难需要克服。这里是几个首先需要解决的问题。
1. 成熟度(相比关系数据库系统来说,技术还不够成熟。这种状况会随着时间而改进)
2. 支持(需要资金来维持软件的开发和维护)
3. 分析与商业智能(NoSQL 数据库缺少即时查询和数据分析工具)
4. 管理(NoSQL 的设计目标是提供零管理的解决方案,不过当今还远远没有达到这个目标。)
5. 专业(大多数 NoSQL 开发者还处于学习模式。这种状况会随着时间而改进)
4.NoSQL 的分类
sql
可以将典型的NoSQL 划分为 4 种类型,分别是键值数据库、列式数据库、文档数据库和图形数据库
1. 键值数据库:典型代表-->Redis
它使用一个哈希表,表中的 Key(键)用来定位 Value(值),即存储和检索具体的 Value。
2. 列式数据库:典型代表-->Hbase
列式数据库起源于 Google 的 BigTable,其数据模型可以看作是一个每行列数可变的数据表
3. 文档数据库:典型代表-->MongoDB
文档数据库是通过键来定位一个文档的,所以是键值数据库的一种衍生品。在文档数据库中,文档是数据库的最小单位。文档数据库可以使用模式来指定某个文档结构。
文档数据库是 NoSQL 数据库类型中出现得最自然的类型,因为它们是按照日常文档的存储来设计的,并且允许对这些数据进行复杂的查询和计算。
4. 图形数据库:典型代表--> Neo4J、OrientDB、InfoGrid、Infinite Graph 和 GraphDB
图形数据库以图论为基础,用图来表示一个对象集合,包括顶点及连接顶点的边。图形数据库使用图作为数据模型来存储数据,可以高效地存储不同顶点之间的关系。
图形数据库是 NoSQL 数据库类型中最复杂的一个,旨在以高效的方式存储实体之间的关系。
二、HBase数据库概述
1.HBase数据库简介
sql
HBase 是基于 Apache Hadoop 的面向列的 NoSQL 数据库,是 Google 的 BigTable 的开源实现。
HBase 是一个针对半结构化数据的开源的、多版本的、可伸缩的、高可靠的、高性能的、分布式的和面向列的动态模式数据库。
HBase 和传统关系数据库不同,它采用了 BigTable 的数据模型增强的稀疏排序映射表(Key/Value),其中,键由行关键字、列关键字和时间戳构成。
HBase 提供了对大规模数据的随机、实时读写访问。
HBase 的目标是存储并处理大型的数据,也就是仅用普通的硬件配置,就能够处理上千亿的行和几百万的列所组成的超大型数据库。
Hadoop 是一个高容错、高延时的分布式文件系统和高并发的批处理系统,不适用于提供实时计算,
而 HBase 是可以提供实时计算(占用大量的内存)的分布式数据库,数据被保存在 HDFS (分布式文件系统)上,由 HDFS 保证其高容错性。
HBase 上的数据是以二进制流的形式存储在 HDFS 上的数据块中的,但是,HBase 上的存储数据对于 HDFS 是透明的。
HBase 可以直接使用本地文件系统,也可以使用 Hadoop 的 HDFS。
HBase 中保存的数据可以使用 MapReduce 来处理,它将数据存储和并行计算有机地结合在一起。
HBase 是按列族进行数据存储的。每个列族会包括许多列,并且这些列是经常需要同时处理的属性。也就是说,HBase 把经常需要一起处理的列构成列族一起存放,从而避免了需要对这些列进行重构的操作。
HBase 在充分利用列式存储优势的同时,通过列族减少列连接的需求。
2.HBase数据模型简介
sql
HBase 是一个稀疏、多维度、有序的映射表。
这张表中每个单元是通过由行键、列族、列限定符和时间戳组成的索引来标识的。
每个单元的值是一个未经解释的二进制数组(byte[]),没有数据类型。当用户在表中存储数据时,每一行都有一个唯一的行键和任意多的列。
表的每一行由一个或多个列族组成,一个列族中可以包含任意多个列。在同一个表模式下,每行所包含的列族是相同的,也就是说,列族的个数与名称都是相同的,但是每一行中的每个列族中列的个数可以不同
HBase 中的同一个列族里面的数据存储在一起,列族支持动态扩展,可以随时添加新的列,无须提前定义列的数量。
HBase 执行更新操作时,并不会删除数据旧的版本,而是生成一个新的版本,原有的版本仍然保留。
如果查询的时候不提供时间戳,那么系统就会返回离当前时间最近的那一个版本的数据。
HBase 提供了两种数据版本回收方式:一种是保存数据的最后3个版本;另一种是保存最近一段时间内的版本,如最近一个月。
3.HBase数据模型基本概念
sql
HBase 中的数据被存储在表中,具有行和列,是一个多维的映射结构。
1. 表(Table)
HBase采用表来组织数据,表由许多行和列组成,列划分为多个列族。
2. 行(Row)
在表里面,每一行代表着一个数据对象。每一行都是由一个行键(Row Key)和一个或者多个列组成的。行键是行的唯一标识,行键并没有什么特定的数据类型,以二进制的字节来存储,按字母顺序排序。
3. 列族(Column Family)
在定义 HBase 表的时候需要提前设置好列族,表中所有的列都需要组织在列族里面。
4. 列限定符(Column Qualifier)
列族中的数据通过列限定符来进行映射。列限定符不需要事先定义,也不需要在不同行之间保持一致。列限定符没有特定的数据类型,以二进制字节来存储。
5. 列(Column)
列由列族(Column Family)和列限定符(Column Qualifier)联合标识,由" : "进行间隔,如 family:qualifiero
6. 单元(Cell)
行键、列族和列限定符一起标识一个单元,存储在单元里的数据称为单元数据,没有特定的数据类型,以二进制字节来存储。
HBase提供基于单元的版本管理功能,版本号默认通过timestamp来标识,并且呈倒序排列;
7. 时间戳(Timestamp)
默认情况下,每一个单元中的数据插入时都会用时间戳来进行版本标识。
读取单元数据时,如果时间戳没有被指定,则默认返回最新的数据;
写入新的单元数据时,如果没有设置时间戳,则默认使用当前时间。
每一个列族的单元数据的版本数量都被 HBase 单独维护,默认情况下,HBase 保留 3 个版本数据。
4.Hbase概念视图(逻辑视图)
上图是 HBase 的概念视图,是一个存储网页信息的表的片段。行键是一个反向 URL,如 www.cnn.com 反向成 com.cnn.www。
反向 URL 的好处就是,可以让来自同一个网站的数据内容都保存在相邻的位置,从而可以提高用户读取该网站的数据的速度。
contents 列族存储了网页的内容;anchor 列族存储了引用这个网页的链接;mime 列族存储了该网页的媒体类型。
5.Hbase物理视图
虽然从概念视图层面来看,HBase 的每个表是由许多行组成的,但是在物理存储层面来看,它是采用了基于列的存储方式,而不是像关系型据库那样用基于行的存储方式。这正是 HBase 与关系型数据库的重要区别之一。
在概念视图中,可以看到许多列是空的,也就是说,这些列上面不存在值。
在物理视图中,这些空的列并不会存储成 null,而是根本不会被存储,从而可以节省大量的存储空间。当请求这些空白的单元的时候,会返回 null 值。
6.Hbase主要组件
HBase采用Master/Slave架构搭建集群,它隶属于Hadoop生态系统,由以下类型节点组成:
HMaster节点、HRegionServer节点、ZooKeeper集群,而在底层,它将数据存储于HDFS中,因而涉及到HDFS的NameNode、DataNode等,总体结构如下:
sql
各组件说明:
1.Client:
1)使用HBase RPC机制与HMaster和HRegionServer进行通信;
2)Client与HMaster进行通信进行管理类操作;
3)Client与HRegionServer进行数据读写类操作;
2.HMaster:类似于NAMENODE RESOURCEMANAGER
HMaster 没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper保证总有一个Master在运行。
HMaster主要负责Table和Region的管理工作:
1)管理用户对表的增删改查操作;
2)管理HRegionServer的负载均衡,调整Region分布;
3)Region Split后,负责新Region的分布;
4)在HRegionServer停机后,负责失效HRegionServer上Region 的迁移;
3.HRegionServer:类似于DATANODE NODEMANAGER
HRegionServer一般和DataNode在同一台机器上运行,实现数据的本地性。
HBase中最核心的模块;
1)维护region,处理对这些region的IO请求;
2)Regionserver负责切分在运行过程中变得过大的region;
Region的概念:
Region是HBase数据管理的基本单位。数据的move,数据的balance,数据的split,都是按照region来进行操作的。
region中存储这用户的真实数据,而为了管理这些数据,HBase使用了RegionSever来管理region。
一个表中可以包含一个或多个Region。
每个Region只能被一个RS(RegionServer)提供服务,RS可以同时服务多个Region,来自不同RS上的Region组合成表格的整体逻辑视图。
HRegionServer详解:
HRegionServer一般和DataNode在同一台机器上运行,实现数据的本地性。
1)HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个Region。
2)一个Table可以有多个Region,他们可以在一个相同的HRegionServer上,也可以分布在不同的HRegionServer上,一个HRegionServer可以有多个HRegion,他们分别属于不同的Table。
3)HRegion由多个Store(HStore)构成,每个HStore对应了一个Table在这个HRegion中的一个Column Family,即每个Column Family就是一个集中的存储单元,因而最好将具有相近IO特性的Column存储在一个Column Family,以实现高效读取。
4)每个HRegionServer中都会有一个HLog对象。HLog对象:数据库的日志。
每个HRegionServer中都会有一个HLog对象。用户进行数据写入的时候,会先把所有数据到HLog文件,同时在HDFS进行备份,然后再写入MemStore中,写到一定程度由MemStore根据一定的算法将数据Flush到底层HDFS文件中(HFile)。
引入HLog原因:灾难恢复。在分布式系统环境中,无法避免系统出错或者宕机,一旦HRegionServer意外退出,MemStore中的内存数据就会丢失,引入HLog就是防止这种情况。
5)一个HStore由一个MemStore 和0个或多个StoreFile组成。
MemStore:
是一个写缓存(In Memory Sorted Buffer),的写在完成WAL日志写后,会 写入MemStore中,由MemStore根据一定的算法将数据Flush到底层HDFS文件中(HFile),通常每个HRegion中的每个 Column Family有一个自己的MemStore。
StoreFile(Hfile):
用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。
4.Zookeeper:
1)ZooKeeper为HBase集群提供协调服务,它管理着HMaster和HRegionServer的状态(available/alive等),并且保证集群中只有一个HMaster,会在它们宕机时通知给HMaster,从而HMaster可以实现HMaster之间的故障转移;
2)实时监控HRegionServer的上线和下线信息,并实时通知给HMaster;
3)存储HBase的Meta Table(hbase:meta)的位置,Meta Table表存储了集群中所有用户HRegion的位置信息,且不能split;
4)Zookeeper的引入使得Master不再是单点故障 HMaster虽然可以开启多个 但是不是越多越好 两个 --> 只开一个 nn2 贡献出来保存region nn2 没有 block --> 尴尬
7.Hbase安装
sql
1.Hbase启动
#第一步:先启动集群zookeeper
[hadoop@nn1 conf]$ sh ~/zk_base_op/zk_ssh_all.sh /usr/local/zookeeper/bin/zkServer.sh status
[hadoop@nn1 conf]$ sh ~/zk_base_op/zk_ssh_all.sh /usr/local/zookeeper/bin/zkServer.sh start
#第二步:启动集群hdfs
start-dfs.sh
#第三步:启动集群hbase
start-hbase.sh
#启动后查看进程
[hadoop@nn1 conf]$ sh ~/hadoop_base_op/ssh_all.sh jps
#开启hbase shell
hbase shell
#查看hbase状态
status
#查看hbase版本
version
2.Hbase关闭
#关闭集群hbase
stop-hbase.sh
3.开启、关闭hbase单独
hbase-daemon.sh start/stop regionserver
hbase-daemon.sh start/stop master
4.查看hbase的web界面
http://nn1.hadoop:60010
5.查看Hbase日志
[hadoop@nn1 conf]$ cd /usr/local/hbase/logs/
[hadoop@nn1 logs]$ ll
[hadoop@nn1 logs]$ tail -300 hbase-hadoop-master-nn1.hadoop.log
6.1在zookeeper中查看hbase meta 表所在的服务器
[hadoop@nn1 ~]$ zkCli.sh -server nn1.hadoop
[zk: nn1.hadoop(CONNECTED) 0] ls /
[zk: nn1.hadoop(CONNECTED) 1] ls /hbase
[zk: nn1.hadoop(CONNECTED) 2] get /hbase/meta-region-server
6.2在zookeeper中查看hbase的 hmaster的所在主机
[zk: nn1.hadoop(CONNECTED) 3] get /hbase/master
8.Hbase的数据读写流程
1.首先客户端寻找HRegionServer,及缓存位置信息
sql
1.第一步:先从缓存中或zookeeper中获取hbase:meta表的位置(元数据信息位置)
2.第二步:再从缓存中或hbase:meta表中查询用户table对应请求的RowKey所在的HRegionServerde的位置
2.写数据流程
sql
1)通过2.8.1找到该写数据最终需要去的HRegionServer;
2)然后客户端将写请求发送给相应的HRegionServer,在HRegionServer中它首先会将该写操作写入WAL(Hlog)日志文件中(Flush到磁盘中)。
3)写完WAL日志文件后,HRegionServer根据Put中的TableName和RowKey,startkey、endkey找到对应的HRegion,并根据Column Family找到对应的HStore,并将Put写入到该HStore的MemStore中。
4)此时写成功,并返回通知客户端。
5)写入MemStore后的操作:
1.触发MemStore Flush操作:数据写入MemStore后,一直到MemStore满 → Flush成一个StoreFile,直至增长到一定阈值
2.触发StoreFiles Compact操作:达到一定阈值后,将多个StoreFile小文件合并成一个大StoreFile大文件,逐步形成越来越大的StoreFile,同时进行版本合并和数据删除
3.触发Region split操作:当单个StoreFile大文件超过一定阈值(Region split 阈值)后,触发Split操作,把当前Region Split成2个Region,Region会下线, 新Split出的2个子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上;
说明:Region拆分策略:
1.Region大小考量的因素
1)Region大,数目太少就会妨碍可扩展性,降低并行能力,导致压力不够分散;
2)region小,数目太多就会造成性能下降;
2.HRegionServer拆分region的步骤:是先将该region下线,然后拆分,将其子region加入到hbase:meta表中,再将他们加入到原本的HRegionServer中,最后汇报Master。
3.hbase在写入数据 为了防止regionserver热点问题,我们需要进行Region的拆分(分为split前、split中、split后)
可以通过设置RegionSplitPolicy的实现类来指定拆分策略,RegionSplitPolicy类的实现类有:
ConstantSizeRegionSplitPolicy
IncreasingToUpperBoundRegionSplitPolicy
DelimitedKeyPrefixRegionSplitPolicy
KeyPrefixRegionSplitPolicy
其中:
1.ConstantSizeRegionSplitPolicy
仅当region大小超过常量值(hbase.hregion.max.filesize大小,默认为10G)时,才进行拆分。
2. IncreasingToUpperBoundRegionSplitPolicy
默认region split策略。即当同一table在同一regionserver上的region数量在[0,100)之间时按照如下的计算公式算,否则按照上一策略计算:
Min (R^3* "hbase.hregion.memstore.flush.size"*2, "hbase.hregion.max.filesize")
R为同一个table中在同一个regionserver中region的个数,
hbase.hregion.memstore.flush.size默认为128M,
hbase.hregion.max.filesize默认为10G。
3. DelimitedKeyPrefixRegionSplitPolicy
保证以分隔符前面的前缀为splitPoint,保证相同RowKey前缀的数据在一个Region中。如果定义rowkey时,采用'_'作为字段分隔符(如:userid_eventid),则采用该策略拆分之后,能够确保具有相同userid的记录隶属于同一Region。
4. KeyPrefixRegionSplitPolicy
保证具有相同前缀的row在一个region中(要求设计中前缀具有同样长度)。指定rowkey前缀位数划分region,通过读取table的prefix_split_key_policy.prefix_length属性,该属性为数字类型,表示前缀长度,在进行split时,按此长度对splitPoint进行截取。
3.读数据流程
sql
1)通过2.8.1找到要读的数据的HRegionServer。
2)根据读取的TableName和RowKey的startkey 、 endkey 找到对应的HRegion。
3)每个regionserver只有一个blockcache(读缓存),读取数据时,先到memestore上读数据,找不到再到blockcahce上找数据,再查不到则到磁盘(storefile)查找,并把读入的数据同时放入blockcache。
9.Hbase的数据结构
sql
1.传统数据库存数据储结构:采用B+树的方式
B+树最大的性能问题是会产生大量的随机IO,随着新数据的插入,叶子节点会慢慢分裂,逻辑上连续的叶子节点在物理上往往不连续,甚至分离的很远,但做范围查询时,会产生大量读随机IO;
2.Hbase数据存储结构:为了克服B+树的弱点,引入了LSM树的概念,即Log-Structured Merge-Trees;
LSM树本质上就是在读写之间取得平衡,和B+树相比,它牺牲了部分读性能,用来大幅提高写性能。
LSM树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。
3.B+树与LSM-Tree树本质区别
他们本质不同点在于他们使用现代硬盘的方式,尤其是磁盘。从磁盘使用方面讲,RDBMS是寻道型,它需要随机读写数据。LSM-Tree则属于传输型,它会顺序读写数据。
10.Hbase的rowkey设计
sql
Row Key特点:
1.Row Key可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes;1 1000000001 1000000002 19 1a 1z 20 2z
2.RowKey是按照字典序存储,因此,设计row key时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块;
3.region的拆分策略和Row Key主键设计有很大关系,Hregionserver的热点问题就是MapReduce和hive的数据倾斜问题,所以hbase也要求数据平衡。
Row Key支持三种检索方式
1)通过单个row key访问:即按照某个row key键值进行get操作;
2)通过row key的范围进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行扫描
3)全表扫描:即直接扫描整张表中所有行记录,只适合小数据量情况。
Hbase中没有join的概念,大表的结构可以使得不需要join。
RowKey设计原则:
1)要保证rowkey的唯一性。
2)wkey 长度建议是越短越好,不要超过16个字节。
3)Rowkey的散列原则
如果Rowkey是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将Rowkey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个Regionserver实现负载均衡的几率。
三、Hbase Shell:DDL语句操作
sql
Meta:hbase:meta(全限定名称;存储元数据信息表)
Hbase shell操作是hbase通过jruby把所有的命令转成java方法执行
一般操作:
查询服务器状态:status
查询版本:version
Hbase的命名空间-->MySQL数据库
Hbase的表-->MySQL的数据库别名
Hbase的列簇-->MySQL的表
Hbase的列限定符-->MySQL的字段
Hbase的但愿-->MySQL的字段值
DDL语句操作:
1.命名空间操作
# 创建命名空间
create_namespace 'myns1'
# 显示命名空间
list_namespace
#查看命名空间下有什么表
list_namespace_tables 'myns1'
# 删除命名空间, 在删除一个命名空间时,该命名空间不能包含任何的表,否则会报错
drop_namespace '命名空间名'
2.创建表
# 创建默认命名空间的表
create '表名称','列族名称1','列族名称2','列族名称N'
# 创建带有命名空间的表
create '命名空间:表名称','列族名称1','列族名称2','列族名称N'
# 创建带有命名空间的表示例
create 'myns1:table1','cf1','cf2','cf3'
3.列出某个命名空间下的所有表
# 列出某个命名空间下的所有表
list 'myns1.*'
4.获得表的描述
# 获得表的描述(查看表结构)
desc '命名空间:表名'
5.删除一个列族
# 删除一个列族(myns1:table1 表的 cf2 列族)
alter 'myns1:table1',{NAME=>'cf2',METHOD=>'delete'}
# 删除多个列族 列族属性操作的时候 属性名 是区分大小写 也就是说 NAME --> 方法 METHOD --> 方法 delete 参数
alter 'myns1:table2', { NAME => 'cf3', METHOD => 'delete'},{ NAME => 'cf2', METHOD => 'delete'}
6.将表下线/下线(注意 : 下线后,该表不能进行查询)
检测表是否下线:is_disabled "myns1:tb1"
将表下线:disable "myns1:tb1"
检测表是否上线:is_enabled "myns1:tb1"
将表上线: enable "myns1:tb1"
7.删除一个表
# 删除一个表(在线表,会报错)
drop 'myns1:table1'
# 删除一个表(将表下线,再删除)
将表下线:disable "myns1:tb1"
再删除表:drop "myns1:tb2"
8.判断表是否存在
# 判断表是否存在
exists "tb1" //公司常用
exists "myns1:tb1"
四、Hbase Shell:DML语句操作
sql
DML语句操作:
1. 添加数据
# 语法:put <table>,<rowkey>,<family:column>,<value>,<timestamp>
# 如果不写timestamp,则系统默认
# 插入数据前要保证表是enable状态
# 重新创建表
create 'myns1:table2', 'cf1', 'cf2'
#基本信息的列族
put 'myns1:table2','hb_1', 'cf1:name','zhaowenming'
put 'myns1:table2','hb_1', 'cf1:age','25'
put 'myns1:table2','hb_1', 'cf1:sex','M'
#证件信息的列族
put 'myns1:table2','hb_1', 'cf2:cert_type','身份证'
put 'myns1:table2','hb_1', 'cf2:cert_no','130102199405286392'
2. 获取数据
get: 获取表中一行数据,不能扫描全表
# 语法:get <table>,<rowkey>,[<family:column>,....]
#获取一行数据
get 'myns1:table2','hb_1'
#获取一行,一个列族的所有数据
get 'myns1:table2','hb_1','cf1'
#获取一行,一个列族中一个列的数据
get 'myns1:table2','hb_1','cf1:name'
#获取一行的列族
get 'myns1:table2', 'hb_1', {COLUMN => 'cf1'}
#获取一行的某列族的列
get 'myns1:table2', 'hb_1', {COLUMN => 'cf1:name'}
#获取一行某列族的列并匹配时间戳
get 'myns1:table2', 'hb_1', {COLUMN => 'cf1:name', TIMESTAMP => 1562742642169}
#获取一行中,FILTER筛选器,binary二进制数组的值为110125的列
get 'myns1:table2', 'hb_1', {FILTER => "ValueFilter(=, 'binary:130102199405286392')"}
3. 更新数据
#语法:重新put,put时会覆盖原来的数据
-- 修改年龄
put 'myns1:table2','hb_1','cf1:age','25'
-- 查看年龄
get 'myns1:table2','hb_1','cf1:age'
4. 通过timestamp来获取两个版本的数据
get 'myns1:table2','hb_1',{COLUMN=>'cf1:age',TIMESTAMP=>1562746800144}
get 'myns1:table2','hb_1',{COLUMN=>'cf1:age',TIMESTAMP=>1562746813618}
5. scan扫描
# 语法:scan <table> ,{COLUMNS => [ <family:column>,.... ], LIMIT => num}
# 添加数据
put 'myns1:table2','hb_2', 'cf2:cert_type','身份证'
put 'myns1:table2','hb_2', 'cf2:cert_no','130102199805236580'
put 'myns1:table2','hb_3', 'cf2:cert_type','身份证'
put 'myns1:table2','hb_3', 'cf2:cert_no','130102199706237795'
put 'myns1:table2','hb_4', 'cf2:cert_type','身份证'
put 'myns1:table2','hb_4', 'cf2:cert_no','130102199705142396'
#扫描全表,大表操作不可取
scan 'myns1:table2'
#获取表中前两行
scan 'myns1:table2', {LIMIT => 2}
#扫描表中指定列族数据
scan 'myns1:table2', {COLUMNS => 'cf1'}
#扫描表中执行列族中列的数据
scan 'myns1:table2', {COLUMNS => 'cf2:cert_no'}
#扫描表中值=130102199705142396 的数据
scan 'myns1:table2', FILTER=>"ValueFilter(=,'binary:130102199705142396')"
#扫描表的范围 包含 STARTROW 的范围 但是不包含 STOPROW 的范围
scan 'myns1:table2' , {STARTROW => 'hb_2', STOPROW => 'hb_3'}
6. 删除行中某列数据
# 语法:delete <table>, <rowkey>, <family:column>
# 修改数据
put 'myns1:table2', 'hb_4','cf2:cert_type', 'shenfenzheng'
get 'myns1:table2', 'hb_4','cf2:cert_type'
put 'myns1:table2', 'hb_4','cf2:cert_type', 'idcard'
get 'myns1:table2', 'hb_4','cf2:cert_type'
# 删除行中某列数据(指定列名,会删除执行列的所有版本数据)
delete 'myns1:table2', 'hb_4', 'cf2:cert_type'
get 'myns1:table2', 'hb_4','cf2:cert_type'(获取数据 所有版本都删除)
get 'myns1:table2','hb_4',{TIMESTAMP=>1733829623580}(验证一下 数据已删除)
get 'myns1:table2','hb_4',{TIMESTAMP=>1733829650481}(验证一下 数据已删除)
scan 'myns1:table2'(删除后发现操作的列已经被删除了)
7. 删除整行
# 语法:deleteall <table>, <rowkey>
# 删除hb_5行数据
put 'myns1:table2','hb_5', 'cf2:cert_type','身份证'
put 'myns1:table2','hb_5', 'cf2:cert_no','130103199605261159'
put 'myns1:table2','hb_5', 'cf1:name','sunjianguo'
get 'myns1:table2','hb_5'
# 删除hb_5行数据
deleteall 'myns1:table2','hb_5'
get 'myns1:table2','hb_5'
8. 清空表数据
#保留 region信息的语法:truncate <table>
#不保留 region信息的语法:truncate_preserve <table>
create 'myns1:table3','cf1'
put 'myns1:table3','id1','cf1:name','zhaowenming'
# 1.清空表(不保留region信息)
truncate 'myns1:table3'
# 2.清空表(保留region信息)
truncate_preserve 'myns1:table3'
9. 查询表中有多少行
# 1.小表count()方法
# 语法:count <table>, {INTERVAL => intervalNum, CACHE => cacheNum}
# INTERVAL设置多少行显示一次及对应的rowkey,默认1000;
# CACHE每次去取的缓存区大小,默认是10,调整该参数可提高查询速度
这种方式效率很低,如果表行数很大的话不建议采用这种方式。
#查询表中数据行数
count 'myns1:table2'
#按照2行显示一次,查询
count 'myns1:table2', {INTERVAL => 2}
# 2. 大表count方法:
调用的hbase jar中自带的统计行数的类。
hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'tablename'
hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'myns1:table2'
10. 计数器操作
Hbase计数器可以用于统计用户数,点击量等信息。
# 语法: incr <tablename>,<rowkey>,<family:column>,long n
# 创建表
create 'myns1:table4','cf1'
# 给表的指定行指定列族的指定列设置计数器(给 'app_page1' 这个行健增加 'c_f1:click_volume' 字段,并使用 counter 实现递增)
#默认递增1
incr 'myns1:table4','app_page1', 'cf1:click_volume'
#指定递增 5
incr 'myns1:table4','app_page1', 'cf1:click_volume',5
#指定递增 1000
incr 'myns1:table4','app_page1', 'cf1:click_volume',1000
#获取'app_page1' 数据
get 'myns1:table4','app_page1'
#获取当前count的值
get_counter 'myns1:table4', 'app_page1', 'cf1:click_volume'
11. 获取多个版本的数据
# 修改列族的版本数量
alter 'myns1:table2',{ NAME =>'cf1', VERSIONS => 3 }
put 'myns1:table2','hb_1','cf1:age','30'
put 'myns1:table2','hb_1','cf1:age','40'
put 'myns1:table2','hb_1','cf1:age','50'
# 此时,可以查询出1个版本的数据
get 'myns1:table2', 'hb_1', { COLUMN =>'cf1:age', VERSIONS => 1}
# 此时,可以查询出2个版本的数据
get 'myns1:table2', 'hb_1', { COLUMN =>'cf1:age', VERSIONS => 2}
# 此时,可以查询出3个版本的数据
get 'myns1:table2', 'hb_1', { COLUMN =>'cf1:age', VERSIONS => 3}
说明:
列族的版本 和 列的版本是不一样的
列族就是我们mysql的表 所以它就一个版本就可以了 不需要多个版本(默认一个版本)
列族中的列是根据 时间戳 在内存中分出来的3个版本
内存 向 硬盘溢出数据的时候 列有3个版本的数据 --> 只写时间最新的
五、eclipse操作hbase
eclipse操作hbase:用java连接虚拟机的hbase**
1.开发环境配置
1.配置pom.xml文件
pom.xml-->dependencies-->add-->引入junit-4.12的jar包
xml
<properties>
<!-- maven项目整体编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 项目编译的时候,源码(.java)使用哪个版本JDK -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 项目编译的时候,可执行文件(.class)使用哪个版本JDK -->
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.新建hbase-site.xml文件,放到资源文件目录下
xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!-- Master -->
<property>
<name>hbase.master</name>
<value>nn1.hadoop:60000</value>
</property>
<!-- Client参数 -->
<property>
<name>hbase.client.scanner.caching</name>
<value>10000</value>
<description>客户端参数,HBase scanner一次从服务端抓取的数据条数</description>
</property>
<property>
<name>hbase.client.scanner.timeout.period</name>
<value>900000</value>
<description>scanner过期时间</description>
</property>
<!-- Zookeeper -->
<property>
<name>hbase.zookeeper.quorum</name>
<value>nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181</value>
</property>
<property>
<name>zookeeper.session.timeout</name>
<value>1200000</value>
</property>
</configuration>
3.新建mapred-site.xml文件,放到资源文件目录下
xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
<description>运行模式</description>
</property>
</configuration>
4.新建log4j.properties文件,放到资源文件目录下
2.JAVA-HBASE:DDL操作
java
package com.shanhai.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
public class DDLTest {
//定义配置对象
private static Configuration conf = HBaseConfiguration.create();
//定义表名
private static TableName tableName = TableName.valueOf("myns1:table4");
/*创建表的方法测试*/
@Test
public void createTable() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取数据库的管理对象
HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
//定义表的描述信息
HTableDescriptor td = new HTableDescriptor(tableName);
//描述表的列族信息
HColumnDescriptor cd1 = new HColumnDescriptor(Bytes.toBytes("cf1"));
HColumnDescriptor cd2 = new HColumnDescriptor(Bytes.toBytes("cf2"));
HColumnDescriptor cd3 = new HColumnDescriptor(Bytes.toBytes("cf3"));
td.addFamily(cd1);
td.addFamily(cd2);
td.addFamily(cd3);
//判断一下,如果表存在就删除
if (admin.tableExists(tableName)) {
//下线表
admin.disableTable(tableName);
//删除表
admin.deleteTable(tableName);
}
//创建这张表
admin.createTable(td);
//输出提示
System.out.println("msg :Table Create Is OK!");
//释放连接
if (null != admin) {
admin.close();
}
if (null != conn) {
conn.close();
}
}
/*删除列族的方法测试*/
@Test
public void deleteByColumnFamily() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取数据库的管理对象
HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
//指定列族并删除
admin.deleteColumn(tableName, Bytes.toBytes("cf3"));
//输出提示
System.out.println("msg : The ColumnFamily Is Deleted!");
//释放连接
if (null != admin) {
admin.close();
}
if (null != conn) {
conn.close();
}
}
/*删除表的方法测试*/
@Test
public void deleteTable() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取数据库的管理对象
HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
//判断一下,如果表存在就删除
if (admin.tableExists(tableName)) {
//下线表
admin.disableTable(tableName);
//删除表
admin.deleteTable(tableName);
}
//输出提示
System.out.println("msg : The Table Is Deleted!");
//释放连接
if (null != admin) {
admin.close();
}
if (null != conn) {
conn.close();
}
}
}
3.JAVA-HBASE:DML操作
java
package com.shanhai.hbase;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
public class DMLTest {
//定义配置对象
private static Configuration conf = HBaseConfiguration.create();
//定义表名
private static TableName tableName = TableName.valueOf("myns1:table4");
/*添加单条数据的方法测试*/
@Test
public void addOneData() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//设置数据
Put put = new Put(Bytes.toBytes("hb_1"));
put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("name"), Bytes.toBytes("赵文明"));
put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("sex"), Bytes.toBytes("男"));
put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"), Bytes.toBytes("25"));
put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("birthday"), Bytes.toBytes("1994-06-01"));
put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("np"), Bytes.toBytes("河北省"));
//添加这条数据
mytable.put(put);
//输出提示
System.out.println("msg : One Data is Inserted!");
//释放连接
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*添加多条数据的方法测试*/
@Test
public void addSomeDatas() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//设置多条数据数据
Put put1 = new Put(Bytes.toBytes("hb_2"));
put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("name"), Bytes.toBytes("孙建国"));
put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("sex"), Bytes.toBytes("男"));
put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"), Bytes.toBytes("36"));
put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("birthday"), Bytes.toBytes("1983-06-01"));
put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("np"), Bytes.toBytes("北京市"));
Put put2 = new Put(Bytes.toBytes("hb_3"));
put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("name"), Bytes.toBytes("王小花"));
put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("sex"), Bytes.toBytes("女"));
put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"), Bytes.toBytes("19"));
put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("birthday"), Bytes.toBytes("2000-06-01"));
put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("np"), Bytes.toBytes("湖南省"));
Put put3 = new Put(Bytes.toBytes("hb_4"));
put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("name"), Bytes.toBytes("赵佳佳"));
put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("sex"), Bytes.toBytes("女"));
put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"), Bytes.toBytes("18"));
put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("birthday"), Bytes.toBytes("2001-06-01"));
put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("np"), Bytes.toBytes("河北省"));
List<Put> puts = new ArrayList<Put>();
puts.add(put1);
puts.add(put2);
puts.add(put3);
//添加多条数据
mytable.put(puts);
//输出提示
System.out.println("msg : Some Datas is Inserted!");
//释放连接
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*删除行的方法测试*/
@Test
public void deleteOneData() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建删除对象,同时指定行
Delete delete = new Delete(Bytes.toBytes("hb_4"));
//删除数据
mytable.delete(delete);
//输出提示
System.out.println("msg:One Row Is Deleted!");
//释放连接
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*删除列的方法测试*/
@Test
public void deleteByColumn() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建删除对象,同时指定行
Delete delete = new Delete(Bytes.toBytes("hb_1"));
//删除指定列的所有数据
delete.addColumns(Bytes.toBytes("cf1"), Bytes.toBytes("age"));
//删除指定列的指定时间及指定时间前的数据
//delete.addColumns(Bytes.toBytes("cf1"), Bytes.toBytes("age"), 1734004118538L);
//删除指定列最新时间的数据
//delete.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"));
//删除指定列指定时间的数据
//delete.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"), 1734003627689L);
//删除指定列族的所有数据
//delete.addFamily(Bytes.toBytes("cf1"));
//删除指定列族的指定时间及指定时间前的数据
//delete.addFamily(Bytes.toBytes("cf1"), 1734066009457L);
//删除数据
mytable.delete(delete);
//输出提示
System.out.println("msg : The Column Is Deleted");
//释放连接
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
}
4.JAVA-HBASE:DQL操作
java
package com.shanhai.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
public class DQLTest {
//定义配置对象
private static Configuration conf = HBaseConfiguration.create();
//定义表名
private static TableName tableName = TableName.valueOf("myns1:table4");
/*获取单行数据的方法测试*/
@Test
public void getOneRowData() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建数据的获取对象
Get get = new Get(Bytes.toBytes("hb_1"));
//指定列族查询
get.addFamily(Bytes.toBytes("cf1"));
//指定列族+列名查询
get.addColumn(Bytes.toBytes("cf1"),Bytes.toBytes("age"));
//指定时间戳查询
//get.setTimeStamp(1733994220289L);
//指定获取的版本个数
get.setMaxVersions(3);
//获取数据
Result result = mytable.get(get);
//获取这行数据
Cell[] cells = result.rawCells();
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow())+
"\t\tcolumn="+new String(CellUtil.cloneFamily(cell))+":"+
new String(CellUtil.cloneQualifier(cell))+", timestamp="+
cell.getTimestamp()+", value="+
new String(CellUtil.cloneValue(cell)));
}
//输出提示
System.out.println("msg : One Data Is Geted!");
//释放连接
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*获取批量数据的方法测试*/
@Test
public void getSomeRowDatas() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建扫描器对象
Scan scan = new Scan();
//设置开始行和结束行
// scan.setStartRow(Bytes.toBytes("hb_1"));
// scan.setStopRow(Bytes.toBytes("hb_4"));
//扫描数据,获得多行数据
ResultScanner rs = mytable.getScanner(scan);
Cell[] cells = null;
//遍历数据
for (Result result : rs) {
cells = result.rawCells();//获取这行数据
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow())+
"\t\tcolumn="+new String(CellUtil.cloneFamily(cell))+":"+
new String(CellUtil.cloneQualifier(cell))+", timestamp="+
cell.getTimestamp()+", value="+
new String(CellUtil.cloneValue(cell)));
}
}
System.out.println("msg : Table Data Is Scaned!");
//释放连接
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
}
5.JAVA-HBASE:filter用法
1.SingleColumnValueFilter:单列值筛选器(影响查询性能,在处理大量数据的时候速度可能会慢)
2.RegexStringComparator:正则表达式字符串比较器
3.SubstringComparator: 字符包含比较器
4.FilterList:多条件筛选器
5.PageFilter:分页查询
java
package com.shanhai.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.FilterList.Operator;
import org.apache.hadoop.hbase.filter.PageFilter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
public class FilterTest {
//定义配置对象
private static Configuration conf = HBaseConfiguration.create();
//定义表名
private static TableName tableName = TableName.valueOf("myns1:table4");
/*单列值筛选器的方法测试*/
//shell的写法:scan 'myns1:table4',{ FILTER =>"SingleColumnValueFilter('cf1','name',>=,'binary:赵文明',true,true)"}
@Test
public void queryTableBySingleFilter() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建扫描器对象
Scan scan = new Scan();
//创建过滤器对象,通过条件筛选,返回整行数据
SingleColumnValueFilter scvf = new SingleColumnValueFilter(Bytes.toBytes("cf1"), Bytes.toBytes("name"),
CompareOp.EQUAL, Bytes.toBytes("赵文明"));
//当某行的该列不存在时,设置条件是否过滤。true过滤;false不过滤(默认)
scvf.setFilterIfMissing(true);
//是否只对最新版本有效。取值范围如下:true(默认):只检测最新版本的值是否满足条件。false:检测所有版本的值是否满足条件。
scvf.setLatestVersionOnly(true);
//绑定筛选条件
scan.setFilter(scvf);
//扫描数据
ResultScanner rs = mytable.getScanner(scan);
Cell[] cells = null;
//遍历数据
for (Result result : rs) {
cells = result.rawCells();//获取这行数据
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow()) + "\t\tcolumn=" + new String(CellUtil.cloneFamily(cell))
+ ":" + new String(CellUtil.cloneQualifier(cell)) + ", timestamp=" + cell.getTimestamp()
+ ", value=" + new String(CellUtil.cloneValue(cell)));
}
}
//释放连接
if (null != rs) {
rs.close();
}
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*正则表达式字符串比较器的方法测试*/
//shell的写法:scan 'myns1:table4',{ FILTER =>"SingleColumnValueFilter('cf1','name',=,'regexstring:^赵',true,true)"}
@Test
public void queryTableByRegexFilter() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建扫描器对象
Scan scan = new Scan();
//设置表达式
RegexStringComparator rsc = new RegexStringComparator("^赵");
//创建过滤器对象,通过条件筛选,返回整行数据
SingleColumnValueFilter scvf = new SingleColumnValueFilter(Bytes.toBytes("cf1"), Bytes.toBytes("name"),
CompareOp.EQUAL, rsc);
//当某行的该列不存在时,设置条件是否过滤。true过滤;false不过滤(默认)
scvf.setFilterIfMissing(true);
//是否只对最新版本有效。取值范围如下:true(默认):只检测最新版本的值是否满足条件。false:检测所有版本的值是否满足条件。
scvf.setLatestVersionOnly(true);
//绑定筛选条件
scan.setFilter(scvf);
//扫描数据
ResultScanner rs = mytable.getScanner(scan);
Cell[] cells = null;
//遍历数据
for (Result result : rs) {
cells = result.rawCells();//获取这行数据
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow()) + "\t\tcolumn=" + new String(CellUtil.cloneFamily(cell))
+ ":" + new String(CellUtil.cloneQualifier(cell)) + ", timestamp=" + cell.getTimestamp()
+ ", value=" + new String(CellUtil.cloneValue(cell)));
}
}
//释放连接
if (null != rs) {
rs.close();
}
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*字符包含比较器的方法测试*/
//shell的写法:scan 'myns1:table4',{ FILTER =>"SingleColumnValueFilter('cf1','name',=,'substring:文',true,true)"}
@Test
public void queryTableBySubstringFilter() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建扫描器对象
Scan scan = new Scan();
//设置表达式
SubstringComparator sc = new SubstringComparator("文");
//创建过滤器对象,通过条件筛选,返回整行数据
SingleColumnValueFilter scvf = new SingleColumnValueFilter(Bytes.toBytes("cf1"), Bytes.toBytes("name"),
CompareOp.EQUAL, sc);
//当某行的该列不存在时,设置条件是否过滤。true过滤;false不过滤(默认)
scvf.setFilterIfMissing(true);
//是否只对最新版本有效。取值范围如下:true(默认):只检测最新版本的值是否满足条件。false:检测所有版本的值是否满足条件。
scvf.setLatestVersionOnly(true);
//绑定筛选条件
scan.setFilter(scvf);
//扫描数据
ResultScanner rs = mytable.getScanner(scan);
Cell[] cells = null;
//遍历数据
for (Result result : rs) {
cells = result.rawCells();//获取这行数据
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow()) + "\t\tcolumn=" + new String(CellUtil.cloneFamily(cell))
+ ":" + new String(CellUtil.cloneQualifier(cell)) + ", timestamp=" + cell.getTimestamp()
+ ", value=" + new String(CellUtil.cloneValue(cell)));
}
}
//释放连接
if (null != rs) {
rs.close();
}
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*多条件筛选器的方法测试*/
//shell的写法:scan 'myns1:table4', { FILTER => "(SingleColumnValueFilter('cf1','age',<,'binary:20',true,true)) AND (SingleColumnValueFilter('cf1','job',=,'binary:学生',true,true))"}
@Test
public void queryTableByListFilter() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建扫描器对象
Scan scan = new Scan();
//查询20岁以下的学生
SingleColumnValueFilter scvf1 = new SingleColumnValueFilter(Bytes.toBytes("cf1"), Bytes.toBytes("age"),
CompareOp.LESS, Bytes.toBytes("20"));
SingleColumnValueFilter scvf2 = new SingleColumnValueFilter(Bytes.toBytes("cf1"), Bytes.toBytes("job"),
CompareOp.EQUAL, Bytes.toBytes("学生"));
//设置过滤器关系
FilterList list = new FilterList(Operator.MUST_PASS_ALL);
//绑定过滤条件
list.addFilter(scvf1);
list.addFilter(scvf2);
//绑定筛选条件
scan.setFilter(list);
//扫描数据
ResultScanner rs = mytable.getScanner(scan);
Cell[] cells = null;
//遍历数据
for (Result result : rs) {
cells = result.rawCells();//获取这行数据
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow()) + "\t\tcolumn=" + new String(CellUtil.cloneFamily(cell))
+ ":" + new String(CellUtil.cloneQualifier(cell)) + ", timestamp=" + cell.getTimestamp()
+ ", value=" + new String(CellUtil.cloneValue(cell)));
}
}
//释放连接
if (null != rs) {
rs.close();
}
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
/*分页查询的方法测试*/
//shell的写法:scan 'myns1:table4',{STARTROW => 'row1',FILTER=>"PageFilter(2)"}
@Test
public void queryTableByPageFilter() throws Exception {
//获取数据库的连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取要操作的表
HTable mytable = (HTable) conn.getTable(tableName);
//创建扫描器对象
Scan scan = new Scan();
// 设置分页参数
int totalRowCount = 0; // 总行数
int pageSize = 2; // 一次展示多少行
// 创建pageFilter
PageFilter pf = new PageFilter(pageSize);
// 封装查询条件
scan.setFilter(pf);
byte[] tmprow = null;
byte[] pb = Bytes.toBytes("z");
ResultScanner scanner = null;
Cell[] cells = null;
//判断是否有数据量
int scanRowCount = 0;
while (true) {
// 设置每次的起始startrow
if(null != tmprow){
// 计算rowkey
tmprow = Bytes.add(tmprow, pb);
System.out.println("startrow : " + Bytes.toString(tmprow));
// 设置每行的起始rowkey
scan.setStartRow(tmprow);
}
// 定义每次查询的行数(用作是否退出的判断条件)
scanRowCount = 0;
// 获取数据
scanner = mytable.getScanner(scan);
//遍历数据
for (Result result : scanner) {
cells = result.rawCells();//获取这行数据
System.out.println("ROW\t\tCOLUMN+CELL");
//遍历数据
for (Cell cell : cells) {//遍历这行所有列族的数据
System.out.println(new String(result.getRow())+
"\t\tcolumn="+new String(CellUtil.cloneFamily(cell))+":"+
new String(CellUtil.cloneQualifier(cell))+", timestamp="+
cell.getTimestamp()+", value="+
new String(CellUtil.cloneValue(cell)));
}
// 计数器叠加
scanRowCount++;
totalRowCount++;
// 获取当先循环的最后一行
tmprow = result.getRow();
}
// 执行判断条件
if(scanRowCount == 0){
// 退出while循环
break;
}
}
// 展示消息
System.out.println("msg : The Data is Queryed! TotalRowCount : " + totalRowCount);
//释放连接
if (null != scanner) {
scanner.close();
}
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
}
}
六、ORC文件转HFILE文件
1.数据准备
java
-- 通过查询limit表创建orc表
create table user_orc STORED AS orc as select aid, pkgname, uptime, type, country, gpcategory from user_install_status_limit;
-- 查询表的orc结构
hive --orcfiledump /hive/warehouse/db1.db/user_orc/000000_0
-- 创建hbase表
create 'myns1:user_install_status',{NAME=>'cf1',VERSION=>1,COMPRESSION=>'snappy'}
2.项目的整体架构
完成-->hive的ORC文件转hbase的HFILE文件
1.配置文件
1.配置pom.xml文件
xml
<properties>
<!-- maven项目整体编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 项目编译的时候,源码(.java)使用哪个版本JDK -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 项目编译的时候,可执行文件(.class)使用哪个版本JDK -->
<maven.compiler.target>1.8</maven.compiler.target>
<!-- 设置运行主类 -->
<mainClass>com.hnxy.bin.MRDriver</mainClass>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-shell</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-cli</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-common</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>${mainClass}</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<skip>true</skip>
<forkMode>once</forkMode>
<excludes>
<exclude>**/**</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2.新建assembly.xml文件,放到资源文件目录下
xml
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<!-- TODO: a jarjar format would be better -->
<!-- 添加到生成文件名称的后缀符 -->
<id>hsanhai</id>
<!-- 打包类型 -->
<formats>
<format>jar</format>
</formats>
<!-- 指定是否包含打包层目录 -->
<includeBaseDirectory>false</includeBaseDirectory>
<!-- 指定要包含的文件集 -->
<fileSets>
<fileSet>
<!-- 指定目录 -->
<directory>${project.build.directory}/classes</directory>
<!-- 指定文件集合的输出目录,该目录是相对于根目录 -->
<outputDirectory>/</outputDirectory>
<!-- 排除文件 -->
<excludes>
<exclude>*.xml</exclude>
<exclude>*.properties</exclude>
</excludes>
</fileSet>
</fileSets>
<!-- 用来定制工程依赖 jar 包的打包方式 -->
<dependencySets>
<dependencySet>
<!-- 指定包依赖目录,该目录是相对于根目录 -->
<outputDirectory>/</outputDirectory>
<!-- 当前项目构件是否包含在这个依赖集合里 -->
<useProjectArtifact>false</useProjectArtifact>
<!-- 是否将第三方jar包解压到该依赖中 false 直接引入jar包 true解压引入 -->
<unpack>true</unpack>
<!-- 将scope为runtime的依赖包打包到lib目录下。 -->
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
3.拷贝hbase-site.xml、文件,放到资源文件目录下
4.拷贝mapred-site.xml文件,放到资源文件目录下
5.拷贝log4j.properties文件,放到资源文件目录下
2.com.shanhai.util
1.BaseMR
java
package com.shanhai.util;
import java.util.Map.Entry;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
/*
* 所有MapReduce任务的父类。都应该继承它,然后在进行开发
* 抽取公用的东西,完成代码复用。
*/
public abstract class BaseMR {
//定义所有任务公共的配置文件加载对象(公共配置)
public static Configuration conf;
//获得配置文件的加载对象
public static void setConf(Configuration conf) {
BaseMR.conf = conf;
}
//配置任务工作链
public ControlledJob getCtrlJob() throws Exception{
//创建任务工作链对象
ControlledJob ctrlJob = new ControlledJob(conf);
//设置输出目录
Path out = this.getPath(getJobNameWithTaskID());
FileSystem fs = FileSystem.get(conf);
if (fs.exists(out)) {
//hadoop fs -rm -r out true-->递归删除
fs.delete(out, true);
System.out.println(this.getJobName() + "的输出目录已经被删除了!");
}
//设置每个任务私有的conf
Configuration jobConf = new Configuration();
//拷贝公共配置给当前任务使用
for (Entry<String, String> entry : conf) {
jobConf.set(entry.getKey(), entry.getValue());
}
//创建Job任务
Job job = getJob(jobConf);
//设置工作链的任务
ctrlJob.setJob(job);
//返回
return ctrlJob;
}
//每个独立job
public abstract Job getJob(Configuration jobConf)throws Exception;
//获取输出主目录
public String getWorkPath(){
return MyConstant.TASK_WORK_BASE_PATH;
}
//获取输出地址
public Path getPath(String jobName) {
return new Path(getWorkPath() + jobName);
}
//获取任务名称
public abstract String getJobName();
//获取任务名称和任务ID-->wordcount_luo
public String getJobNameWithTaskID() {
return getJobName()+"_"+conf.get(MyConstant.TASK_ID);
}
}
2.JobRunnerResult
java
package com.shanhai.util;
/*
* 主要对任务工作链的执行结果进行封装
* 1.任务工作链的执行状态
* 2.任务工作链的执行时间
* 3.任务工作链的所有Counter数据
*/
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.Counters;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
public class JobRunnerResult {
private boolean isSuccess;
private String runTime;
private Map<String, Counters> counterMap = new HashMap<String, Counters>();
public Boolean getIsSuccess() {
return isSuccess;
}
public void setIsSuccess(Boolean isSuccess) {
this.isSuccess = isSuccess;
}
public String getRunTime() {
return runTime;
}
public void setRunTime(String runTime) {
this.runTime = runTime;
}
public Map<String, Counters> getCmap() {
return counterMap;
}
public void setCmap(Map<String, Counters> counterMap) {
this.counterMap = counterMap;
}
/**
* 根据任务链名称 获取counters
*/
public Counters getCounters(String jobName){
return counterMap.get(jobName);
}
/**
* 根据工作链控制的任务名称 获取counters
*/
public Counters getCounters(ControlledJob colJob){
return counterMap.get(colJob.getJobName());
}
/**
* 获取指定的计数器
* @param job 工作链名称
* @param gname 组名称
* @param cname 计数器名称
* @return TODO(这里描述每个参数,如果有返回值描述返回值,如果有异常描述异常)
*/
public Counter getCounter(ControlledJob job,String gname,String cname){
Counter counter = getCounters(job).findCounter(gname, cname);
return counter;
}
/**
* 获取指定counter的值
* @param job 任务名称
* @param gname 组名称
* @param cname 计数器名称
*/
public long getCounterVal(ControlledJob job,String gname,String cname){
// 获取这个技术器
Counter counter = getCounter(job, gname, cname);
return Utils.isEmpty(counter)?0L:counter.getValue();
}
}
3.JobRunnerUtil
java
package com.shanhai.util;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
/*
* 执行任务的工具类:执行任务和封装执行结果
*/
public class JobRunnerUtil {
//创建一个定长线程池来进行线程的管理
public static ExecutorService es = Executors.newFixedThreadPool(1);
//创建自己的线程类
private static class MyCallable implements Callable<JobRunnerResult>{
// 定义工作链对象
private JobControl jobc;
// 构造函数赋值
public MyCallable(JobControl jobc) {
super();
this.jobc = jobc;
}
@Override
public JobRunnerResult call() throws Exception {
//创建方法的返回值
JobRunnerResult result = new JobRunnerResult();
//监控任务工作链的执行
//定义开始时间
Long startTime = System.currentTimeMillis();
//监控执行情况
while (!jobc.allFinished()) {
//任务没有全部完成
Thread.sleep(1000);
}
//定义结束时间
Long endTime = System.currentTimeMillis();
//判断任务是否成功
result.setIsSuccess(jobc.getFailedJobList().size() == 0);
//任务执行成功获取counters数据
for (ControlledJob job : jobc.getSuccessfulJobList()) {
//按照任务名称保存所有Counters数据
result.getCmap().put(job.getJob().getJobName(), job.getJob().getCounters());
}
//获取运行时间
Long tmp = endTime - startTime;
String runTime = getLifeTime(tmp);
result.setRunTime(runTime);
jobc.stop();
//返回
return result;
}
private String getLifeTime(Long mss){
// 计算用了多少天
Long days = mss / (1000*60*60*24);
// 计算用了多少个小时 取余数算出不足一天的毫秒数 然后除以小时的毫秒 转换成小时单位
Long hours = (mss%(1000*60*60*24))/(1000*60*60);
// 计算用了多少分钟
Long minutes = (mss%(1000*60*60))/(1000*60);
// 计算用了所少秒钟
Long seconds = (mss%(1000*60))/1000;
// 开始拼接时间
StringBuilder sb = new StringBuilder();
// 判断
if(days != 0){
sb.append(days).append("天");
}
if(hours != 0){
sb.append(hours).append("小时");
}
if(minutes != 0){
sb.append(minutes).append("分钟");
}
if(seconds != 0){
sb.append(seconds).append("秒");
}
return sb.toString();
}
}
public static JobRunnerResult run(JobControl jobc) throws Exception{
Thread thread = new Thread(jobc);
thread.start();
Future<JobRunnerResult> f = es.submit(new MyCallable(jobc));
return f.get();
}
}
4.MyConstant
java
package com.shanhai.util;
/*
* 封装我们自己的常量类
*/
public class MyConstant {
//hbase:rowkey的分隔符
public static final String SQE1 = "_";
//任务ID
//-Dmymr.task.id=luo wordcount-->outpath wordcount_luo
public static final String TASK_ID = "mymr.task.id";
//任务的输入地址
//-Dmymr.task.input=D:\hdfs\input\hn16\mr1 固定我们任务的输入目录
public static final String TASK_INPUT = "mymr.task.input";
//任务的工作目录(windows下制定的绝对路径 如果扔到集群上需要修改成HDFS路径)
//-Dmymr.task.output=D:/user/shanhai/task/wordcount_luo/part-r-000000 固定我们任务的输出目录
//本地:D:/hdfs/user/shanhai/task/
//线上:/user/shanhai/task/
public static final String TASK_WORK_BASE_PATH = "/user/shanhai/task/";
}
5.ORCFormat
java
package com.shanhai.util;
/*
* 保存ORC结构的工具类
*/
public class ORCFormat {
//定义ORC的结构
public static final String MY_ORC_TYPE1 = "struct<aid:string,pkgname:string,uptime:bigint,type:int,country:string,gpcategory:string>";
}
6.ORCUtil
java
package com.shanhai.util;
import org.apache.hadoop.hive.ql.io.orc.OrcStruct;
import org.apache.hadoop.hive.serde2.objectinspector.StructField;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
/*
* 解析ORC的一行数据工具类
*/
public class ORCUtil {
//一行ORC数据对象
private OrcStruct os = null;
//一行ORC数据结构对象
private StructObjectInspector soi = null;
//赋予ORC数据结构
public void setOrcType(String orcType){
//获取ORC的结构数据
TypeInfo info = TypeInfoUtils.getTypeInfoFromTypeString(orcType);
//进行类型的转换
soi = (StructObjectInspector) OrcStruct.createObjectInspector(info);
}
//获取一行ORC数据
public void setOs(OrcStruct os) {
this.os = os;
}
//获取一行ORC的列的数据
public String getDataByKey(String key){
//创建方法的返回值
String val = null;
//根据结构获取节点
StructField fild = soi.getStructFieldRef(key);
//获取数据
val = String.valueOf(soi.getStructFieldData(os, fild));
//返回
return Utils.isEmpty(val)||val.toLowerCase().equals("null")?null:val;
}
}
7.Utils
java
package com.shanhai.util;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
/*
* 判断对象是否为空的工具类
*/
public class Utils {
/*
* 判断各类型的对象是否为:空
*/
public static Boolean isEmpty(Object obj){
Boolean boo = false;
if (obj == null) {
boo = true;
}else if (obj instanceof String) {
boo = "".equals(obj.toString().trim());
}else if (obj instanceof Map<?, ?>) {
boo = ((Map<?, ?>)obj).isEmpty();
}else if (obj instanceof Collection<?>) {
boo = ((Collection<?>)obj).isEmpty();
}else if (obj.getClass().isArray()) {
boo = Array.getLength(obj)==0;
}
return boo;
}
/*
* 判断各类型的对象是否为:非空
*/
public static Boolean isNotEmpty(Object obj){
return !isEmpty(obj);
}
}
3.com.shanhai.mr
1.HbaseImportData
java
package com.shanhai.mr;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hive.ql.io.orc.OrcNewInputFormat;
import org.apache.hadoop.hive.ql.io.orc.OrcStruct;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import com.shanhai.util.BaseMR;
import com.shanhai.util.MyConstant;
import com.shanhai.util.ORCFormat;
import com.shanhai.util.ORCUtil;
import com.shanhai.util.Utils;
public class HbaseImportData extends BaseMR {
private static class MyMapper extends Mapper<NullWritable, OrcStruct, ImmutableBytesWritable, Put>{
//定义map需要用到的变量
private ORCUtil ou = new ORCUtil();
//定义列族
private byte[] family = Bytes.toBytes("cf1");
//定义列
private byte[] pkgnameQ = Bytes.toBytes("pkgname");
private byte[] typeQ = Bytes.toBytes("type");
private byte[] countryQ = Bytes.toBytes("country");
private byte[] gpcategoryQ = Bytes.toBytes("gpcategory");
//定义时间格式化类
private DateFormat df = new SimpleDateFormat("yyyyMMdd");
//定义输出的key
ImmutableBytesWritable outkey = new ImmutableBytesWritable();
@Override
protected void setup(Mapper<NullWritable, OrcStruct, ImmutableBytesWritable, Put>.Context context)
throws IOException, InterruptedException {
ou.setOrcType(ORCFormat.MY_ORC_TYPE1);
}
@Override
protected void map(NullWritable key, OrcStruct value,
Mapper<NullWritable, OrcStruct, ImmutableBytesWritable, Put>.Context context)
throws IOException, InterruptedException {
//解析ORC对象
ou.setOs(value);
//获取列的数据
String aid,pkgname,uptime,type,country,gpcategory;
aid = ou.getDataByKey("aid");
pkgname = ou.getDataByKey("pkgname");
uptime = ou.getDataByKey("uptime");
type = ou.getDataByKey("type");
country = ou.getDataByKey("country");
gpcategory = ou.getDataByKey("gpcategory");
//设置rowkey
String rowkey = aid+MyConstant.SQE1+df.format(new Date(Long.parseLong(uptime.trim())*1000));
//进行put对象的封装
Put put = new Put(Bytes.toBytes(rowkey));
if(Utils.isNotEmpty(pkgname)){
put.addColumn(family, pkgnameQ, Bytes.toBytes(pkgname));
}
if(Utils.isNotEmpty(type)){
put.addColumn(family, typeQ, Bytes.toBytes(type));
}
if(Utils.isNotEmpty(country)){
put.addColumn(family, countryQ, Bytes.toBytes(country));
}
if(Utils.isNotEmpty(gpcategory)){
put.addColumn(family, gpcategoryQ, Bytes.toBytes(gpcategory));
}
//设置输出
outkey.set(Bytes.toBytes(rowkey));
context.write(outkey, put);
}
}
@Override
public Job getJob(Configuration jobConf) throws Exception {
//创建方法的返回值
Job job = Job.getInstance(conf, getJobName());
//设置打包类
job.setJarByClass(HbaseImportData.class);
job.setMapperClass(MyMapper.class);
//设置map的格式化类
job.setInputFormatClass(OrcNewInputFormat.class);
//设置map的输入与输出类型
job.setMapOutputKeyClass(ImmutableBytesWritable.class);
job.setMapOutputValueClass(Put.class);
//获取数据连接
Connection conn = ConnectionFactory.createConnection(jobConf);
//获取要操作的表
HTable mytable = (HTable)conn.getTable(TableName.valueOf("myns1:user_install_status"));
//根据Hbase写好的putSortReducer结合
HFileOutputFormat2.configureIncrementalLoad(job, mytable, mytable.getRegionLocator());
//设置输入与输出目录
FileInputFormat.addInputPath(job, new Path(jobConf.get(MyConstant.TASK_INPUT)));
FileOutputFormat.setOutputPath(job, this.getPath(this.getJobNameWithTaskID()));
//释放资源
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
//返回
return job;
}
@Override
public String getJobName() {
return "hbase_import_data";
}
}
4.com.shanhai.bin
1.MyJobRunner
java
package com.shanhai.bin;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import com.shanhai.mr.HbaseImportData;
import com.shanhai.util.JobRunnerResult;
import com.shanhai.util.JobRunnerUtil;
public class MyJobRunner extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
//创建任务工作链
JobControl jobc = new JobControl("job1");
//获取Hadoop的配置文件对象
Configuration conf = this.getConf();
//设置公共的配置文件加载对象conf,只需加载一次就可以
HbaseImportData.setConf(conf);
HbaseImportData hid = new HbaseImportData();
//获取工作链的任务
ControlledJob job1 = hid.getCtrlJob();
//设置工作链的任务
jobc.addJob(job1);
//运行任务
JobRunnerResult result = JobRunnerUtil.run(jobc);
if (result.getIsSuccess()) {
//这里可以打印Counters的信息
System.out.println("JOB_STATUS : OK!");
System.out.println("THE PROGRAM IS RUN " + result.getRunTime());
} else {
System.out.println("JOB_STATUS : FALL!");
}
return 0;
}
public static void main(String[] args) {
try {
System.exit(ToolRunner.run(new MyJobRunner(), args));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.MyJobDriver
java
package com.shanhai.bin;
import org.apache.hadoop.util.ProgramDriver;
public class MyJobDriver {
public static void main(String[] args) {
//创建驱动程序对象
ProgramDriver driver = new ProgramDriver();
try {
//加载要执行的类
driver.addClass("workjob1", MyJobRunner.class, "hive的ORC文件转hbase的HFILE文件");
//反射执行
ProgramDriver.class.getMethod("driver", new Class[]{String[].class}).invoke(driver, new Object[]{args});
} catch (Throwable e) {
e.printStackTrace();
}
}
}
3.集群配置
xml
修改集群环境让其能找到hive和hbase的jar包:
1.操作机:修改 hadoop-env.sh 添加如下配置 :
[hadoop@nn1 ~]$ vim /usr/local/hadoop/etc/hadoop/hadoop-env.sh
添加配置:export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HADOOP_HOME/lib/*:$HBASE_HOME/lib/*
2) 所有机器:把hive的jar拷贝到lib目录下 /usr/local/hadoop/lib
[hadoop@nn1 ~]$ ll /usr/local/hive/lib/ | grep hive-exec-2.1.1.jar
[hadoop@nn1 ~]$ cp /usr/local/hive/lib/hive-exec-2.1.1.jar /tmp/
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/scp_all.sh /tmp/hive-exec-2.1.1.jar /tmp/
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh cp /tmp/hive-exec-2.1.1.jar /usr/local/hadoop/lib
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh ls -l /usr/local/hadoop/lib | grep hive-exec
3) 操作机:maprd-site.xml
mr 任务能找到hbase的jar包是因为 maprd-site.xml 里把hbase的lib加到了 mapreduce.application.classpath 中
[hadoop@nn1 ~]$ vim /usr/local/hadoop/etc/hadoop/mapred-site.xml
<property>
<name>mapreduce.application.classpath</name>
末尾添加/usr/local/hbase/lib/*(我们之前已经添加了)
<value>,/usr/local/hbase/lib/*</value>
</property>
4) 操作机:hbase的lib目录下的slf4j移出来
[hadoop@nn1 ~]$ ll /usr/local/hbase/lib/ | grep log4j
[hadoop@nn1 ~]$ mv /usr/local/hbase/lib/slf4j-log4j12-1.7.5.jar /tmp/
5) 操作机:在/usr/local/hadoop/etc/hadoop目录下增加hbase配置的软链接
[hadoop@nn1 ~]$ ln -s /usr/local/hbase/conf/hbase-site.xml /usr/local/hadoop/etc/hadoop/hbase-site.xml
[hadoop@nn1 ~]$ cd /usr/local/hadoop/etc/hadoop
[hadoop@nn1 hadoop]$ ll
4.集群运行
java
1.打jar包
打包命令:clean assembly:assembly
2.集群运行:hive的ORC文件转hbase的HFILE文件
[hadoop@nn1 ~]$ mkdir myhbase
[hadoop@nn1 ~]$ cd myhbase/
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ rz
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ hadoop jar hn16_hbase_tp2-0.0.1-SNAPSHOT-luoxichuan.jar workjob1 -Dmymr.task.id=luo -Dmymr.task.input=hdfs://ns1/hive/warehouse/db1.db/user_orc -Dhbase.zookeeper.quorum=nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181
常报错误:
1.job的输入与输出
2.pom.xml文件的打包类
3.-D参数前缀
4.属性大小写
5.判断属性是否非空
6.返回值空
3.集群运行:hive数据导入
执行上面的命令时,hdfs目录不能带上列族,如果带上,执行导入的时候,会报错。
hadoop jar /usr/local/hbase/lib/hbase-shell-1.3.1.jar completebulkload hdfs://ns1//user/luoxichuan/task/hbase_import_data_luo myns1:user_install_status
4.检查数据:查看hbase表的导入结果
1)方法一:使用rowcounter执行mr统计工具查看hbase中有多少数据
hadoop jar /usr/local/hbase/lib/hbase-shell-1.3.1.jar rowcounter myns1:user_install_status
2)方法二:使用hbase的scan扫描
hbase shell
scan myns1:hainiu_user_install_status
七、Hbase表的预分Region
1.小批量预分Region
java
1.在hive中分析这300条数据:
# 查看数据发现每个aid 的前5位
select aid fron user_install_status;
# 执行hql,统计前5位
select t.sub, count(1) from
(select substring(aid, 1, 5) as sub from user_install_status) t group by t.sub;
2.分析完后,生成region的key划分配置文件
# 将分配内容写入
[hadoop@nn1 ~]$ cd myhbase/
[hadoop@nn1 myhbase]$ vim split_data
文件内容(为SQL语句分析的结果)
8d000
8d001
8d002
8d003
8d004
8d005
8d006
8d007
8d008
8d009
8d00a
8d00b
8d00c
8d00d
3.创建带有预留region的表;
#创建带有预留region的表,split_data 是region的key划分配置文件
create '表名','列簇信息',{SPLITS_FILE => '保存了rowkey的信息文件'}
create 'myns1:tb5','cf1',{SPLITS_FILE => '/home/hadoop/myhbase/split_data'}
# 查看region分配情况
http://nn1.hadoop:60010/table.jsp?name=myns1:tb6
4.执行mapreduce,将orc文件转成hfile,并导入到hbase表;
# 需要在代码上修改表名称,然后打包上传集群运行。
hadoop jar hn16_hbase_tp2-0.0.1-SNAPSHOT-luoxichuan.jar workjob1 -Dmyhbase.tablename=myns1:tb5 -Dmymr.task.id=luo -Dmymr.task.input=hdfs://ns1/hive/warehouse/db1.db/user_orc -Dhbase.zookeeper.quorum=nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181
# 将生成在MapReduce上的HFile文件导入到Habse的表里去。(相当于mv移动)
执行完成的hdfs目录,cf 列族目录下没有数据,导入时已经被load走了:
hadoop jar /usr/local/hbase/lib/hbase-shell-1.3.1.jar completebulkload /user/luoxichuan/task/hbase_import_data_luo myns1:tb5
# 在页面看region的分配情况
http://nn1.hadoop:60010/table.jsp?name=myns1:tb6
2.大批量预分Region
预分region实际上就是制定每个region的rowkey的区间,让rowkey更加均匀的分布到每个region上,所以最主要的就是对rowkey的数据分布进行分析。
shell
一、获取预分Region的key划分配置文件。
1.在hive中分析数据
# 首先分析第一位字母的数据量占比
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,1,1) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_1_1 2> /dev/null &
# 分析前两位字母组合的数据量占比
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,1,2) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_1_2 2> /dev/null &
# 分析前三位字母组合的数据量占比
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,1,3) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_1_3 2> /dev/null &
# 分析前四位字母组合的数据量占比
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,1,4) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_1_4 2> /dev/null &
vim split_1_1
vim split_1_2
vim split_1_3
vim split_1_4
从结果中看第四种组合能使rowkey的分布更加均衡,所以采用第四种结果,设置好每个region的超始key和终止key。
所以采用前4位字母的组合对rowkey的区间进行划分。
2.用hive进行分析前4位每位数据量的占比情况
分析第一位数据量的占比情况:数据大多分布在字母013456789abcdef
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,1,1) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_1_1 2> /dev/null &
分析第二位数据量的占比情况:数据大多分布在字母013456789abcdef
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,2,1) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_2_1 2> /dev/null &
分析第三位数据量的占比情况:数据大多分布在字母013456789abcdef
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,3,1) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_3_1 2> /dev/null &
分析第四位数据量的占比情况:数据大多分布在字母013456789abcdef
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,4,1) as sub from user_install_status_orc) a group by sub order by n desc;" 1> ~/split_4_1 2> /dev/null &
3.生成rowkey的start和end组合的配置文件
按第二步情况进行组合就会形成16*16*16*16=65536个region,有多少个region就有多少个reducer,这样会把5个主机内存耗光。
所以可以把最后一位划分成两个区间即【0到7】,【8到f】,这样会有16*16*16*2=8192个region。
根据以上情况生成region的key划分配置文件。
public static void main(String[] args) {
char[] arr = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
for (int i1 = 0; i1 <= 15; i1++) {
for (int i2 = 0; i2 <= 15; i2++) {
for (int i3 = 0; i3 <= 15; i3++) {
for (int i4 = 0; i4 <= 8; i4 += 8) {
if (i4 == 0) {
System.out.println("" + arr[i1] + arr[i2] + arr[i3]);
} else {
System.out.println("" + arr[i1] + arr[i2] + arr[i3] + arr[i4]);
}
}
}
}
}
}
4.上面的rowkey范围要仔细的再检查一遍看看是否有局部的rowkey保存了太多的数据,需要对局部偏移数据的再次划分
# 分析前四位字母组合的数据量占比
nohup hive -e "use db1;select sub,count(1) n from (select substring(aid,1,4) as sub from user_install_status where dt='20141228') a group by sub order by n desc;" 1> ~/split_4_1 2> /dev/null &
5.由于前四位数据分布不均匀的问题,我们需要再次划分
-- 统计前三位为200 201 的前5位汇总
select sub1,count(1) n from
(select substring(aid,1,3) as sub,substring(aid,1,5) as sub1 from user_install_status_orc) a
where a.sub >='200' and a.sub <='201'
group by a.sub1 order by n asc;
--由于5位数据也比较集中,需要看第6位的情况,统计前5位为20000 20001 的前6位汇总
select sub1,count(1) n from
(select substring(aid,1,5) as sub,substring(aid,1,6) as sub1 from user_install_status_orc) a
where a.sub >='20000' and a.sub <='20130'
group by a.sub1 order by n asc;
--由于6位数据也比较集中,需要看第7位的情况,统计前5位为20014 20015 的前6位汇总
select sub1,count(1) n from
(select substring(aid,1,6) as sub,substring(aid,1,7) as sub1 from user_install_status where dt='20141228') a
where a.sub >='20000b' and a.sub <='200010'
group by a.sub1 order by n asc;
可以看到于前七位数据分布比较均匀,我们用前七位就可以了
20000b7
20000b6
20000b4
20000b3
20000b8
20000b5
然后将检查分析后的结果追加到region的配置文件split_data中
二、JAVA实现预分region的代码
0.打包命令:clean assembly:assembly
1.注意只需要包含pom.xml文件、assembly.xml文件split_data文件和类即可
package com.shanhai.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm;
public class MySplit implements SplitAlgorithm {
public Set<String> readSplitData(){
//创建方法的返回值
Set<String> result = new HashSet<String>();
//将文件加载到内存中
InputStream in = this.getClass().getClassLoader().getResourceAsStream("split_data");
//进行字符流的读取
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
String line = null;
while ((line = reader.readLine()) != null) {
if (Utils.isEmpty(line)) {
continue;
}
line = line.trim();
result.add(line);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
@Override
public byte[][] split(int numRegions) {
//创建方法的返回值
List<byte[]> result = new ArrayList<byte[]>();
//数据转换
for (String s : readSplitData()) {
result.add(Bytes.toBytes(s));
}
//返回
return result.toArray(new byte[0][]);
}
@Override
public byte[] split(byte[] start, byte[] end) {
// TODO Auto-generated method stub
return null;
}
@Override
public byte[] firstRow() {
// TODO Auto-generated method stub
return null;
}
@Override
public byte[] lastRow() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setFirstRow(String userInput) {
// TODO Auto-generated method stub
}
@Override
public void setLastRow(String userInput) {
// TODO Auto-generated method stub
}
@Override
public byte[] strToRow(String input) {
// TODO Auto-generated method stub
return null;
}
@Override
public String rowToStr(byte[] row) {
// TODO Auto-generated method stub
return null;
}
@Override
public String separator() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setFirstRow(byte[] userInput) {
// TODO Auto-generated method stub
}
@Override
public void setLastRow(byte[] userInput) {
// TODO Auto-generated method stub
}
}
三、配置运行环境:
1.上传到服务器的指定目录:/home/hadoop/myhbase
2.并把这个目录配置到操作机hbase-env.sh的HBASE_CLASSPATH里
vim /usr/local/hbase/conf/hbase-env.sh
export HBASE_CLASSPATH=$HBASE_CLASSPATH:$HADOOP_HOME/lib/*:/home/hadoop/myhbase/*
四、根据自定义split 类创建带有预分region的表
使用以下命令创建表并根据你的自定义split类进行region的预分
hbase org.apache.hadoop.hbase.util.RegionSplitter myns1:table10 com.shanhai.util.MySplit -c 2 -f cf1
其中:
-c:是指定分多少个region但是在我的自定义的split中并没有使用,不过这是命令的必填项所以要必须写上。只要大于1就可以。
-f:是指定列族的名称。
五、执行mapreduce,将orc文件转成hfile文件
由于有8199个region,所以这个MR任务会生成8199个reducer用于生成hfile文件
hadoop jar hn16_hbase_tp2-0.0.1-SNAPSHOT-luoxichuan.jar workjob1 -Dmyhbase.tablename=myns1:table10 -Dmymr.task.id=luo -Dmymr.task.input=hdfs://ns1/hive/warehouse/db1.db/user_orc -Dhbase.zookeeper.quorum=nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181
六、将生成的hfile文件导入到表中去
hadoop jar /usr/local/hbase/lib/hbase-shell-1.3.1.jar completebulkload /user/luoxichuan/task/hbase_import_data_luo myns1:table10
3.如何修改HBase表的压缩格式
java
1.如何修改HBase表的压缩格式
步骤:
1)下线表(该动作耗时较长,会对hbase的服务造成很大的影响,可以选择在一个服务不忙的时间来做。或者是在往表里插入数据之前就指定好每个列族的压缩算法)
disable 'myns1:user_hbase_split'
2)设置压缩算法
alter 'myns1:user_hbase_split', NAME => 'cf1', COMPRESSION => 'snappy'
3)重新上线表
enable 'myns1:user_hbase_split'
4)生效压缩配置
major_compact 'myns1:user_hbase_split'
注意:
在实际使用中应该在创建完预分region的表之后就把列簇修改成snappy压缩,不要等到数据导入之后再修改,因为会让regionserver把大量老的region下线再加载压缩完成的新region,这个过程会造成regionserver很繁忙从而影响其它表的使用。
2. 正常工作流程:
1.创建预分region表。
hbase org.apache.hadoop.hbase.util.RegionSplitter myns1:user_hbase_split1 com.hnxy.util.UserInstallStatusAidSplit -c 2 -f cf
2.下线刚才创建的预分region表。
disable 'myns1:user_hbase_split2'
3.优化刚才使用预分region命令创建的表,这里是修改了"f"列簇的文件压缩格式为"snappy"。
alter 'myns1:user_hbase_split2', NAME => 'cf1', COMPRESSION => 'snappy'
4.上线优化之后预分region表。
enable 'myns1:user_hbase_split'
5.生成hfile文件。
hadoop jar ~/hbase_bk_tp2-0.0.1-SNAPSHOT-hainiu.jar hbi -Dhainiu.task.id=su -Dhainiu.task.input=hdfs://ns1/hive/warehouse/db1.db/user_orc -Dhbase.zookeeper.quorum=nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181
6.把生成的hfile文件导出到预分region表,这时数据已经snappy格式的,所以不用再major_compact,这样表就可以直接使用了,并且不会造成regionserver的region的频繁操作。
hadoop jar /usr/local/hbase/lib/hbase-shell-1.3.1.jar completebulkload hdfs://ns1//user/suniu/task/hbi_su/ myns1:user_hbase_split1
7. 刷新
major_compact 'myns1:user_hbase_split'
八、Hbase数据的批量导入+预分Region
1.需求
在hbase不需要提前做任何操作的前提下进行表的批量数据导入,并且要包括以下功能 :
1.创建表
2.进行预分region
3.进行适当的表的压缩设置
4.生成数据
5.自动导入数据
2.项目的整体架构
1.配置文件
0.新建split_data文件
java
8d000
8d001
8d002
8d003
8d004
8d005
8d006
8d007
8d008
8d009
8d00a
8d00b
8d00c
8d00d
1.拷贝pom.xml文件
xml
<properties>
<!-- maven项目整体编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 项目编译的时候,源码(.java)使用哪个版本JDK -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 项目编译的时候,可执行文件(.class)使用哪个版本JDK -->
<maven.compiler.target>1.8</maven.compiler.target>
<!-- 设置运行主类 -->
<mainClass>com.shanhai.bin.NHIDJobDriver</mainClass>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-cli</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-common</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>${mainClass}</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<skip>true</skip>
<forkMode>once</forkMode>
<excludes>
<exclude>**/**</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2.新建assembly.xml文件,放到资源文件目录下
xml
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<!-- TODO: a jarjar format would be better -->
<!-- 添加到生成文件名称的后缀符 -->
<id>shanhai</id>
<!-- 打包类型 -->
<formats>
<format>jar</format>
</formats>
<!-- 指定是否包含打包层目录 -->
<includeBaseDirectory>false</includeBaseDirectory>
<!-- 指定要包含的文件集 -->
<fileSets>
<fileSet>
<!-- 指定目录 -->
<directory>${project.build.directory}/classes</directory>
<!-- 指定文件集合的输出目录,该目录是相对于根目录 -->
<outputDirectory>/</outputDirectory>
<!-- 排除文件 -->
<excludes>
<exclude>*.xml</exclude>
<exclude>*.properties</exclude>
</excludes>
</fileSet>
</fileSets>
<!-- 用来定制工程依赖 jar 包的打包方式 -->
<dependencySets>
<dependencySet>
<!-- 指定包依赖目录,该目录是相对于根目录 -->
<outputDirectory>/</outputDirectory>
<!-- 当前项目构件是否包含在这个依赖集合里 -->
<useProjectArtifact>false</useProjectArtifact>
<!-- 是否将第三方jar包解压到该依赖中 false 直接引入jar包 true解压引入 -->
<unpack>true</unpack>
<!-- 将scope为runtime的依赖包打包到lib目录下。 -->
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
3.拷贝hbase-site.xml、文件,放到资源文件目录下
xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!-- Master -->
<property>
<name>hbase.master</name>
<value>nn1.hadoop:60000</value>
</property>
<!-- Client参数 -->
<property>
<name>hbase.client.scanner.caching</name>
<value>10000</value>
<description>客户端参数,HBase scanner一次从服务端抓取的数据条数</description>
</property>
<property>
<name>hbase.client.scanner.timeout.period</name>
<value>900000</value>
<description>scanner过期时间</description>
</property>
<!-- Zookeeper -->
<property>
<name>hbase.zookeeper.quorum</name>
<value>nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181</value>
</property>
<property>
<name>zookeeper.session.timeout</name>
<value>1200000</value>
</property>
</configuration>
4.拷贝mapred-site.xml文件,放到资源文件目录下
xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
<description>运行模式</description>
</property>
</configuration>
5.拷贝log4j.properties文件,放到资源文件目录下
2.com.shanhai.util
1.BaseMR
java
package com.shanhai.util;
import java.util.Map.Entry;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
/*
* 所有MapReduce任务的父类。都应该继承它,然后在进行开发
* 抽取公用的东西,完成代码复用。
*/
public abstract class BaseMR {
//定义所有任务公共的配置文件加载对象(公共配置)
public static Configuration conf;
//获得配置文件的加载对象
public static void setConf(Configuration conf) {
BaseMR.conf = conf;
}
//配置任务工作链
public ControlledJob getCtrlJob() throws Exception{
//创建任务工作链对象
ControlledJob ctrlJob = new ControlledJob(conf);
//设置输出目录
Path out = this.getPath(getJobNameWithTaskID());
FileSystem fs = FileSystem.get(conf);
if (fs.exists(out)) {
//hadoop fs -rm -r out true-->递归删除
fs.delete(out, true);
System.out.println(this.getJobName() + "的输出目录已经被删除了!");
}
//设置每个任务私有的conf
Configuration jobConf = new Configuration();
//拷贝公共配置给当前任务使用
for (Entry<String, String> entry : conf) {
jobConf.set(entry.getKey(), entry.getValue());
}
//创建Job任务
Job job = getJob(jobConf);
//设置工作链的任务
ctrlJob.setJob(job);
//返回
return ctrlJob;
}
//每个独立job
public abstract Job getJob(Configuration jobConf)throws Exception;
//获取输出主目录
public String getWorkPath(){
return MyConstant.TASK_WORK_BASE_PATH;
}
//获取输出地址
public Path getPath(String jobName) {
return new Path(getWorkPath() + jobName);
}
//获取任务名称
public abstract String getJobName();
//获取任务名称和任务ID-->wordcount_luo
public String getJobNameWithTaskID() {
return getJobName()+"_"+conf.get(MyConstant.TASK_ID);
}
}
2.JobRunnerResult
java
package com.shanhai.util;
/*
* 主要对任务工作链的执行结果进行封装
* 1.任务工作链的执行状态
* 2.任务工作链的执行时间
* 3.任务工作链的所有Counter数据
*/
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.Counters;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
public class JobRunnerResult {
private Boolean isSuccess;
private String runTime;
private Map<String, Counters> counterMap = new HashMap<String, Counters>();
public Boolean getIsSuccess() {
return isSuccess;
}
public void setIsSuccess(Boolean isSuccess) {
this.isSuccess = isSuccess;
}
public String getRunTime() {
return runTime;
}
public void setRunTime(String runTime) {
this.runTime = runTime;
}
public Map<String, Counters> getCmap() {
return counterMap;
}
public void setCmap(Map<String, Counters> counterMap) {
this.counterMap = counterMap;
}
/**
* 根据任务链名称 获取counters
*/
public Counters getCounters(String jobName){
return counterMap.get(jobName);
}
/**
* 根据工作链控制的任务名称 获取counters
*/
public Counters getCounters(ControlledJob colJob){
return counterMap.get(colJob.getJobName());
}
/**
* 获取指定的计数器
* @param job 工作链名称
* @param gname 组名称
* @param cname 计数器名称
* @return TODO(这里描述每个参数,如果有返回值描述返回值,如果有异常描述异常)
*/
public Counter getCounter(ControlledJob job,String gname,String cname){
Counter counter = getCounters(job).findCounter(gname, cname);
return counter;
}
/**
* 获取指定counter的值
* @param job 任务名称
* @param gname 组名称
* @param cname 计数器名称
*/
public long getCounterVal(ControlledJob job,String gname,String cname){
// 获取这个技术器
Counter counter = getCounter(job, gname, cname);
return Utils.isEmpty(counter)?0L:counter.getValue();
}
}
3.JobRunnerUtil
java
package com.shanhai.util;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
/*
* 执行任务的工具类:执行任务和封装执行结果
*/
public class JobRunnerUtil {
//创建一个定长线程池来进行线程的管理
public static ExecutorService es = Executors.newFixedThreadPool(1);
//创建自己的线程类
private static class MyCallable implements Callable<JobRunnerResult>{
// 定义工作链对象
private JobControl jobc;
// 构造函数赋值
public MyCallable(JobControl jobc) {
super();
this.jobc = jobc;
}
@Override
public JobRunnerResult call() throws Exception {
//创建方法的返回值
JobRunnerResult result = new JobRunnerResult();
//监控任务工作链的执行
//定义开始时间
Long startTime = System.currentTimeMillis();
//监控执行情况
while (!jobc.allFinished()) {
//任务没有全部完成
Thread.sleep(1000);
}
//定义结束时间
Long endTime = System.currentTimeMillis();
//判断任务是否成功
result.setIsSuccess(jobc.getFailedJobList().size() == 0);
//任务执行成功获取counters数据
for (ControlledJob job : jobc.getSuccessfulJobList()) {
//按照任务名称保存所有Counters数据
result.getCmap().put(job.getJob().getJobName(), job.getJob().getCounters());
}
//获取运行时间
Long tmp = endTime - startTime;
String runTime = getLifeTime(tmp);
result.setRunTime(runTime);
jobc.stop();
//返回
return result;
}
private String getLifeTime(Long mss){
// 计算用了多少天
Long days = mss / (1000*60*60*24);
// 计算用了多少个小时 取余数算出不足一天的毫秒数 然后除以小时的毫秒 转换成小时单位
Long hours = (mss%(1000*60*60*24))/(1000*60*60);
// 计算用了多少分钟
Long minutes = (mss%(1000*60*60))/(1000*60);
// 计算用了所少秒钟
Long seconds = (mss%(1000*60))/1000;
// 开始拼接时间
StringBuilder sb = new StringBuilder();
// 判断
if(days != 0){
sb.append(days).append("天");
}
if(hours != 0){
sb.append(hours).append("小时");
}
if(minutes != 0){
sb.append(minutes).append("分钟");
}
if(seconds != 0){
sb.append(seconds).append("秒");
}
return sb.toString();
}
}
public static JobRunnerResult run(JobControl jobc) throws Exception{
Thread thread = new Thread(jobc);
thread.start();
Future<JobRunnerResult> f = es.submit(new MyCallable(jobc));
return f.get();
}
}
4.MyConstant
java
package com.shanhai.util;
/*
* 封装我们自己的常量类
*/
public class MyConstant {
//hbase:rowkey的分隔符
public static final String SQE1 = "_";
//任务ID
//-Dmymr.task.id=luo wordcount-->outpath wordcount_luo
public static final String TASK_ID = "mymr.task.id";
//任务的输入地址
//-Dmymr.task.input=D:\hdfs\input\hn16\mr1 固定我们任务的输入目录
public static final String TASK_INPUT = "mymr.task.input";
//任务的工作目录(windows下制定的绝对路径 如果扔到集群上需要修改成HDFS路径)
//-Dmymr.task.output=D:/user/luoxichuan/task/wordcount_luo/part-r-000000 固定我们任务的输出目录
//本地:D:/hdfs/user/luoxichuan/task/
//线上:/user/luoxichuan/task/
public static final String TASK_WORK_BASE_PATH = "/user/luoxichuan/task/";
}
5.MyRegionSpliter
java
package com.shanhai.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm;
public class MyRegionSpliter implements SplitAlgorithm {
public Set<String> readSplitData(){
//创建方法的返回值
Set<String> result = new HashSet<String>();
//将文件加载到内存中
InputStream in = this.getClass().getClassLoader().getResourceAsStream("split_data");
//进行字符流的读取
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
String line = null;
while ((line = reader.readLine()) != null) {
if (Utils.isEmpty(line)) {
continue;
}
result.add(line.trim());
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
@Override
public byte[][] split(int numRegions) {
//创建方法的返回值
List<byte[]> result = new ArrayList<byte[]>();
//数据转换
for (String s : readSplitData()) {
result.add(Bytes.toBytes(s));
}
//返回
return result.toArray(new byte[0][]);
}
@Override
public byte[] split(byte[] start, byte[] end) {
// TODO Auto-generated method stub
return null;
}
@Override
public byte[] firstRow() {
// TODO Auto-generated method stub
return null;
}
@Override
public byte[] lastRow() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setFirstRow(String userInput) {
// TODO Auto-generated method stub
}
@Override
public void setLastRow(String userInput) {
// TODO Auto-generated method stub
}
@Override
public byte[] strToRow(String input) {
// TODO Auto-generated method stub
return null;
}
@Override
public String rowToStr(byte[] row) {
// TODO Auto-generated method stub
return null;
}
@Override
public String separator() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setFirstRow(byte[] userInput) {
// TODO Auto-generated method stub
}
@Override
public void setLastRow(byte[] userInput) {
// TODO Auto-generated method stub
}
}
6.ORCFormat
java
package com.shanhai.util;
/*
* 保存ORC结构的工具类
*/
public class ORCFormat {
//定义ORC的结构
public static final String MY_ORC_TYPE1 = "struct<aid:string,pkgname:string,uptime:bigint,type:int,country:string,gpcategory:string>";
}
7.ORCUtil
java
package com.shanhai.util;
import org.apache.hadoop.hive.ql.io.orc.OrcStruct;
import org.apache.hadoop.hive.serde2.objectinspector.StructField;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
/*
* 解析ORC的一行数据工具类
*/
public class ORCUtil {
//一行ORC数据对象
private OrcStruct os = null;
//一行ORC数据对象的结构
private StructObjectInspector soi = null;
//赋予ORC数据结构
public void setOrcType(String orcType){
//获取ORC的结构数据
TypeInfo info = TypeInfoUtils.getTypeInfoFromTypeString(orcType);
//进行类型转换
soi = (StructObjectInspector) OrcStruct.createObjectInspector(info);
}
//获取一行ORC数据
public void setOs(OrcStruct os) {
this.os = os;
}
//获取一行ORC的列的数据
public String getDataByKey(String key){
//创建方法的返回值
String result = null;
//根据结构获取想要的列
StructField fild = soi.getStructFieldRef(key);
//获取数据
result = String.valueOf(soi.getStructFieldData(os, fild));
if (Utils.isEmpty(result)||result.toLowerCase().equals("null")) {
return null;
}
//返回
return result;
}
}
8.Utils
java
package com.shanhai.util;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
/*
* 判断对象是否为空的工具类
*/
public class Utils {
/*
* 判断各类型的对象是否为:空
*/
public static Boolean isEmpty(Object obj){
Boolean boo = false;
if (obj == null) {
boo = true;
}else if (obj instanceof String) {
boo = "".equals(obj.toString().trim());
}else if (obj instanceof Map<?, ?>) {
boo = ((Map<?, ?>)obj).isEmpty();
}else if (obj instanceof Collection<?>) {
boo = ((Collection<?>)obj).isEmpty();
}else if (obj.getClass().isArray()) {
boo = Array.getLength(obj)==0;
}
return boo;
}
/*
* 判断各类型的对象是否为:非空
*/
public static Boolean isNotEmpty(Object obj){
return !isEmpty(obj);
}
}
3.com.shanhai.mr
1.NewHbaseDataImport
java
package com.shanhai.mr;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hive.ql.io.orc.OrcNewInputFormat;
import org.apache.hadoop.hive.ql.io.orc.OrcStruct;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import com.shanhai.util.BaseMR;
import com.shanhai.util.MyConstant;
import com.shanhai.util.ORCFormat;
import com.shanhai.util.ORCUtil;
import com.shanhai.util.Utils;
public class NewHbaseDataImport extends BaseMR {
private static class MyMapper extends Mapper<NullWritable, OrcStruct, ImmutableBytesWritable, Put>{
//定义map需要用到的变量
private ORCUtil ou = new ORCUtil();
//定义列族
private byte[] family = Bytes.toBytes("cf1");
//定义列
private byte[] pkgnameQ = Bytes.toBytes("pkgname");
private byte[] typeQ = Bytes.toBytes("type");
private byte[] countryQ = Bytes.toBytes("country");
private byte[] gpcategoryQ = Bytes.toBytes("gpcategory");
//定义时间格式化类
private DateFormat df = new SimpleDateFormat("yyyyMMdd");
//定义输出的key
private ImmutableBytesWritable outkey = new ImmutableBytesWritable();
private Put outval = null;
@Override
protected void setup(Mapper<NullWritable, OrcStruct, ImmutableBytesWritable, Put>.Context context)
throws IOException, InterruptedException {
//获取ORC表结构
ou.setOrcType(ORCFormat.MY_ORC_TYPE1);
}
@Override
protected void map(NullWritable key, OrcStruct value,
Mapper<NullWritable, OrcStruct, ImmutableBytesWritable, Put>.Context context)
throws IOException, InterruptedException {
//解析ORC对象
ou.setOs(value);
//获取列的数据
String aid,pkgname,uptime,type,country,gpcategory;
aid = ou.getDataByKey("aid");
pkgname = ou.getDataByKey("pkgname");
uptime = ou.getDataByKey("uptime");
type = ou.getDataByKey("type");
country = ou.getDataByKey("country");
gpcategory = ou.getDataByKey("gpcategory");
//设置rowkey
String rowkey = aid+MyConstant.SQE1+df.format(new Date(Long.parseLong(uptime.trim())*1000));
//进行put对象的封装
outval = new Put(Bytes.toBytes(rowkey));
if(Utils.isNotEmpty(pkgname)){
outval.addColumn(family, pkgnameQ, Bytes.toBytes(pkgname));
}
if(Utils.isNotEmpty(type)){
outval.addColumn(family, typeQ, Bytes.toBytes(type));
}
if(Utils.isNotEmpty(country)){
outval.addColumn(family, countryQ, Bytes.toBytes(country));
}
if(Utils.isNotEmpty(gpcategory)){
outval.addColumn(family, gpcategoryQ, Bytes.toBytes(gpcategory));
}
//设置输出
outkey.set(outval.getRow());
context.write(outkey, outval);
}
}
@Override
public Job getJob(Configuration jobConf) throws Exception {
//创建方法的返回值
Job job = Job.getInstance(conf, this.getJobNameWithTaskID());
//设置打包类
job.setJarByClass(NewHbaseDataImport.class);
job.setMapperClass(MyMapper.class);
//设置map的格式化类
job.setInputFormatClass(OrcNewInputFormat.class);
//设置map的输入与输出类型
job.setMapOutputKeyClass(ImmutableBytesWritable.class);
job.setMapOutputValueClass(Put.class);
//设置输入与输出目录
FileInputFormat.addInputPath(job, new Path(jobConf.get(MyConstant.TASK_INPUT)));
FileOutputFormat.setOutputPath(job, this.getPath(this.getJobNameWithTaskID()));
//获取数据连接
Connection conn = ConnectionFactory.createConnection(jobConf);
//获取要操作的表
HTable mytable = (HTable)conn.getTable(TableName.valueOf(jobConf.get("myhbase.table.name")));
//让我们的任务与Hbase写好的putSortReducer整合
HFileOutputFormat2.configureIncrementalLoad(job, mytable, mytable.getRegionLocator());
//释放资源
if (null != mytable) {
mytable.close();
}
if (null != conn) {
conn.close();
}
//返回
return job;
}
@Override
public String getJobName() {
return "auto_hbase_import_data";
}
}
4.com.shanhai.bin
1.NHIDJobRunner
java
package com.shanhai.bin;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.io.compress.Compression;
import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.RegionSplitter;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import com.shanhai.mr.NewHbaseDataImport;
import com.shanhai.util.JobRunnerResult;
import com.shanhai.util.JobRunnerUtil;
import com.shanhai.util.MyRegionSpliter;
public class NHIDJobRunner extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
//获取Hadoop的配置文件对象
Configuration conf = this.getConf();
//设置公共的配置文件加载对象conf,只需加载一次就可以
NewHbaseDataImport.setConf(conf);
NewHbaseDataImport nhdi = new NewHbaseDataImport();
//创建hbase表并预分region
RegionSplitter.main(new String[]{
conf.get("myhbase.table.name"),
MyRegionSpliter.class.getName(),"-c","2","-f","cf1"
});
//修改表 HBASE表的数据压缩
modifyTable(conf,nhdi);
//创建任务工作链
JobControl jobc = new JobControl("NHID");
//获取工作链的任务
ControlledJob job1 = nhdi.getCtrlJob();
//设置工作链的任务
jobc.addJob(job1);
//执行任务 ORC文件转HFILE文件
JobRunnerResult result = JobRunnerUtil.run(jobc);
//HBASE导入数据
if (result.getIsSuccess()) {
//HBASE数据导入对象
LoadIncrementalHFiles load = new LoadIncrementalHFiles(conf);
//导入数据
load.run(new String[]{nhdi.getPath(nhdi.getJobNameWithTaskID()).toString(),conf.get("myhbase.table.name")});
}
//输出任务状态信息
if (result.getIsSuccess()) {
//这里可以打印Counters的信息
System.out.println("JOB_STATUS : OK!");
System.out.println("THE PROGRAM IS RUN " + result.getRunTime());
} else {
System.out.println("JOB_STATUS : FALL!");
}
return 0;
}
private void modifyTable(Configuration conf, NewHbaseDataImport nhdi) {
//创建数据库连接
Connection conn = null;
//创建DDL表的管理对象
HBaseAdmin admin = null;
try {
//获取数据库连接
conn = ConnectionFactory.createConnection(conf);
//获取DDL表的管理对象
admin = (HBaseAdmin)conn.getAdmin();
//定义要操作的表名
TableName tableName = TableName.valueOf(conf.get("myhbase.table.name"));
//下线这张表
admin.disableTable(tableName);
//定义列族描述器
@SuppressWarnings("deprecation")
HColumnDescriptor descriptor = new HColumnDescriptor(
Bytes.toBytes("cf1"),
HColumnDescriptor.DEFAULT_VERSIONS, // 保留原来的版本
Compression.Algorithm.SNAPPY.getName(), // 设置数据的snappy压缩
HColumnDescriptor.DEFAULT_IN_MEMORY, // 是否尝试从内存中提供此列族的默认设置。 默认false
HColumnDescriptor.DEFAULT_BLOCKCACHE, // 是否使用块缓存的默认设置。 默认true
HColumnDescriptor.DEFAULT_TTL, // 单元格内容的默认生存时间。默认 Integer的最大值
HColumnDescriptor.DEFAULT_BLOOMFILTER); // 是否使用布隆的默认设置。(行键的哈希在每次插入行时将被添加到布隆)
//确定修改 数据的压缩算法
admin.modifyColumn(tableName, descriptor);
//上线这张表
admin.enableTable(tableName);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (null != admin) {
try {
admin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != conn) {
try {
conn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
try {
System.exit(ToolRunner.run(new NHIDJobRunner(), args));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.NHIDJobDriver
java
package com.shanhai.bin;
import org.apache.hadoop.util.ProgramDriver;
public class NHIDJobDriver {
public static void main(String[] args) {
//创建驱动程序对象
ProgramDriver driver = new ProgramDriver();
try {
//加载要执行的类
driver.addClass("NHID", NHIDJobRunner.class, "HBASE批量数据导入!");
//反射执行
ProgramDriver.class.getMethod("driver", new Class[]{String[].class}).invoke(driver, new Object[]{args});
} catch (Throwable e) {
e.printStackTrace();
}
}
}
3.集群环境配置
shell
1.操作机配置hadoop-env.sh环境,否则会报找不到类。
vim /usr/local/hadoop/etc/hadoop/hadoop-env.sh
export HADOOP_CLASSPATH=末尾添加:【:/home/hadoop/myhbase/*】
4.集群运行
shell
1.打包项目:
打包命令:clean assembly:assembly
2.上传Jar包:
[hadoop@nn1 ~]$ cd myhbase/
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ rz
3.集群运行
[hadoop@nn1 myhbase]$ hadoop jar hn16_hbase_tp4-0.0.1-SNAPSHOT-luoxichuan.jar NHID -Dmyhbase.table.name=myns1:table11 -Dmymr.task.id=su -Dmymr.task.input=hdfs://ns1/hive/warehouse/db1.db/user_orc -Discreate.table=true -Dhbase.zookeeper.quorum=nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181
九、HFILE文件转ORC文件
1.HFILE-BY-ORC项目的整体架构
1.配置文件
1.新建assembly.xml文件,放到资源文件目录下
2.com.shanhai.util
6.ORCWriteUtil
java
package com.shanhai.util;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hive.ql.io.orc.OrcSerde;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
import org.apache.hadoop.io.Writable;
/*
* ORC记录写入工具类:传入相应的参数,写入到orc文件中去
*/
public class ORCWriteUtil {
//1.定义ORC结构的解析对象
private ObjectInspector oi = null;
//2.定义一个List集合,用于存储ORC的字段
private List<Object> list = null;
//3.定义ORC文件的序列化对象
private OrcSerde os = null;
/*
* 初始化ORC结构的解析对象
*/
public void setOrcWriteType(String type) {
//获取ORC结构信息
TypeInfo typeInfo = TypeInfoUtils.getTypeInfoFromTypeString(type);
//获取ORC结构的解析对象
oi = TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(typeInfo);
}
/*
* 添加要写入的ORC数据
*/
public ORCWriteUtil addAttr(Object... params) {
//判断一下保存数据的集合是否为空,如果为空就重新创建
if (Utils.isEmpty(list)) {
list = new ArrayList<Object>();
}
//解析参数,进行数据的添加
for (Object o : params) {
list.add(o);
}
//返回
return this;
}
/*
* 将ORC数据进行hadoop序列化
*/
public Writable serialize() {
//判断是否为空
if (Utils.isEmpty(os)) {
os = new OrcSerde();
}
//进行数据的序列化
Writable result = os.serialize(list, oi);
//清空保存数据的集合
list = new ArrayList<Object>();
return result;
}
}
3.com.shanhai.mr
1.HfileByOrc
java
package com.shanhai.mr;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hive.ql.io.orc.CompressionKind;
import org.apache.hadoop.hive.ql.io.orc.OrcNewOutputFormat;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import com.shanhai.util.BaseMR;
import com.shanhai.util.MyConstant;
import com.shanhai.util.ORCFormat;
import com.shanhai.util.ORCWriteUtil;
import com.shanhai.util.Utils;
public class HfileByOrc extends BaseMR {
private static class MyMapper extends TableMapper<NullWritable, Writable>{
//定义map需要的变量
private ORCWriteUtil owu = new ORCWriteUtil();
//定义列族
private byte[] family = Bytes.toBytes("cf1");
//定义列
//aid,pkgname,uptime,type,country,gpcategory
private byte[] pkgnameQ = Bytes.toBytes("pkgname");
private byte[] typeQ = Bytes.toBytes("type");
private byte[] countryQ = Bytes.toBytes("country");
private byte[] gpcategoryQ = Bytes.toBytes("gpcategory");
//定义时间格式化类
private DateFormat df = new SimpleDateFormat("yyyyMMdd");
@Override
protected void setup(Mapper<ImmutableBytesWritable, Result, NullWritable, Writable>.Context context)
throws IOException, InterruptedException {
//设定ORC的格式
owu.setOrcWriteType(ORCFormat.MY_ORC_TYPE1);
}
//定义中间变量
private String rowkey = "";
private String aid;
private Long uptime;
private String tmptime;
private Integer type;
private String tmptype;
private String pkgname,country,gpcategory;
private String[] strs = null;
@Override
protected void map(ImmutableBytesWritable key, Result value,
Mapper<ImmutableBytesWritable, Result, NullWritable, Writable>.Context context)
throws IOException, InterruptedException {
//获取rowkey
rowkey = Bytes.toString((key.get()));
//拆分rowkey
strs = rowkey.split(MyConstant.SQE1);
//截取aid
aid = strs[0];
//截取时间
tmptime = strs[1];
try {
uptime = df.parse(tmptime).getTime()/1000;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取数据
pkgname = Bytes.toString(value.getValue(family, pkgnameQ));
tmptype = Bytes.toString(value.getValue(family, typeQ));
type = Integer.parseInt(Utils.isEmpty(tmptype)||tmptype.toLowerCase().equals("null")?"-1":tmptype);
country = Bytes.toString(value.getValue(family, countryQ));
gpcategory = Bytes.toString(value.getValue(family, gpcategoryQ));
//添加数据
aid,pkgname,uptime,type,country,gpcategory
owu.addAttr(aid,pkgname,uptime,type,country,gpcategory);
//输出
context.write(NullWritable.get(), owu.serialize());
}
}
@Override
public Job getJob(Configuration jobConf) throws Exception {
//1.定义hadoop相关属性
//关闭推测执行:本次任务是写任务,防止数据重复写入
jobConf.set("mapreducer.map.spedulative", "false");
//设置压缩算法
jobConf.set("orc.compress", CompressionKind.SNAPPY.name());
//索引创建
jobConf.set("orc.create.index", "true");
//2.定义任务
//定义job
Job job = Job.getInstance(jobConf, this.getJobNameWithTaskID());
//设置不需要reduce
job.setNumReduceTasks(0);
//设置打包类
job.setJarByClass(HfileByOrc.class);
//3.创建数据扫描器和数据过滤器
//创建扫描器
Scan scan = new Scan();
//设置扫描器
scan.addFamily(Bytes.toBytes("cf1"));
scan.setCaching(100);//设置抓取一起跳数据
scan.setCacheBlocks(false);//设置查询的数据不放入缓存
//创建rowkey过滤器
RowFilter rf = new RowFilter(CompareOp.EQUAL, new RegexStringComparator("^8d008"));
scan.setFilter(rf);
//4.设置读取HBASE表的M-R配置
TableMapReduceUtil.initTableMapperJob(
jobConf.get("myhbase.table.name"),
scan,
MyMapper.class,
NullWritable.class,
Writable.class,
job);
//设置输出格式化类
job.setOutputFormatClass(OrcNewOutputFormat.class);
//设置输出目录
FileOutputFormat.setOutputPath(job, this.getPath(this.getJobNameWithTaskID()));
return job;
}
@Override
public String getJobName() {
return "hfile_by_orc";
}
}
4.com.shanhai.bin
1.HBORunner
java
package com.shanhai.bin;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import com.shanhai.mr.HfileByOrc;
import com.shanhai.util.JobRunnerResult;
import com.shanhai.util.JobRunnerUtil;
public class HBORunner extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
//创建任务工作链
JobControl jobc = new JobControl("jw1");
//获取Hadoop的配置文件对象
Configuration conf = this.getConf();
//设置公共的配置文件加载对象conf,只需加载一次就可以
HfileByOrc.setConf(conf);
HfileByOrc hde = new HfileByOrc();
//获取工作链的任务
ControlledJob job1 = hde.getCtrlJob();
//设置工作链的任务
jobc.addJob(job1);
//运行任务
JobRunnerResult result = JobRunnerUtil.run(jobc);
if (result.getIsSuccess()) {
//这里可以打印Counters的信息
System.out.println("JOB_STATUS : OK!");
System.out.println("THE PROGRAM IS RUN " + result.getRunTime());
} else {
System.out.println("JOB_STATUS : FALL!");
}
return 0;
}
public static void main(String[] args) {
try {
System.exit(ToolRunner.run(new HBORunner(), args));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.HBODriver
java
package com.shanhai.bin;
import org.apache.hadoop.util.ProgramDriver;
public class HBODriver {
public static void main(String[] args) {
//创建驱动程序对象
ProgramDriver driver = new ProgramDriver();
try {
//加载要执行的类
driver.addClass("hbo", HBORunner.class, "hbase数据导出!");
//反射执行
ProgramDriver.class.getMethod("driver", new Class[]{String[].class}).invoke(driver, new Object[]{args});
} catch (Throwable e) {
e.printStackTrace();
}
}
}
2.集群运行
shell
1.打包项目:
打包命令:clean assembly:assembly
2.上传Jar包:
[hadoop@nn1 ~]$ cd myhbase/
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ rz
3.集群运行
hadoop jar hn15_edfh-0.0.1-SNAPSHOT-su.jar hde -Dhainiu.table.name=myns1:table5 -Dhainiu.task.id=su
3.创建Hive-ORC-外部表
sql
use db1;
CREATE TABLE `user_install_status_hfile_by_orc`(
`aid` string COMMENT 'from deserializer',
`pkgname` string COMMENT 'from deserializer',
`uptime` bigint COMMENT 'from deserializer',
`type` int COMMENT 'from deserializer',
`country` string COMMENT 'from deserializer',
`gpcategory` string COMMENT 'from deserializer')
stored as orc
LOCATION '/user/luoxichuan/task/hfile_by_orc_luo'
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.create.index'='true');
4.验证数据
sql
select * from user_install_status_hfile_by_orc;
十、MR读写AVRO文件
1.项目需求:
需求:MR读写AVRO文件。(也就是AVRO文件转新AVRO文件)
2.生成测试数据
sql
用hive SQL语句转Avro/Orc的语法:
ORC转AVRO:create table user_avro stored as avro as select * from user_orc;
AVRO转ORC:create table user_orc stored as avro as select * from user_avro;
1.从原来ORC表中导出条数据生成avro表用于测试
create table user_avro stored as avro as select * from user_orc;
2.找到Avro文件下载下来
show create table user_avro;
3.提取avro数据格式 : 编写mr任务用于读取AVRO格式数据并生成新的AVRO格式数据,并根据新的AVRO格式创建外部表.
{"type":"record","name":"user_avro","namespace":"db1","fields":[{"name":"aid","type":["null","string"],"default":null},{"name":"pkgname","type":["null","string"],"default":null},{"name":"uptime","type":["null","long"],"default":null},{"name":"type","type":["null","int"],"default":null},{"name":"country","type":["null","string"],"default":null},{"name":"gpcategory","type":["null","string"],"default":null}]}
{\"type\":\"record\",\"name\":\"user_avro\",\"namespace\":\"db1\",\"fields\":[{\"name\":\"aid\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"pkgname\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"uptime\",\"type\":[\"null\",\"long\"],\"default\":null},{\"name\":\"type\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"country\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"gpcategory\",\"type\":[\"null\",\"string\"],\"default\":null}]}
3.项目的整体架构:
1.com.shanhai.mr
1.AvroToAvro
java
package com.shanhai.mr;
import java.io.IOException;
import org.apache.avro.Schema;
import org.apache.avro.Schema.Parser;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.mapred.AvroKey;
import org.apache.avro.mapreduce.AvroJob;
import org.apache.avro.mapreduce.AvroKeyInputFormat;
import org.apache.avro.mapreduce.AvroKeyOutputFormat;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import com.shanhai.util.BaseMR;
import com.shanhai.util.MyConstant;
import com.shanhai.util.Utils;
/*
* Avro文件转新的Avro文件
*/
public class AvroToAvro extends BaseMR{
//定义AVRO结构的解析对象
private static Parser parser1 = new Schema.Parser();
//读取原始数据的AVRO结构
private static Schema schema1 = parser1.parse("{\"type\":\"record\",\"name\":\"user_avro\",\"namespace\":\"db1\",\"fields\":[{\"name\":\"aid\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"pkgname\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"uptime\",\"type\":[\"null\",\"long\"],\"default\":null},{\"name\":\"type\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"country\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"gpcategory\",\"type\":[\"null\",\"string\"],\"default\":null}]}");
//定义AVRO结构的解析对象
private static Parser parser2 = new Schema.Parser();
//读取导出数据的AVRO结构
private static Schema schema2 = parser2.parse("{\"type\":\"record\",\"name\":\"user_avro\",\"namespace\":\"db1\",\"fields\":[{\"name\":\"aid\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"pkgname\",\"type\":[\"null\",\"string\"],\"default\":null}]}");
public static class MyMapper extends Mapper<AvroKey<GenericRecord>, NullWritable, AvroKey<GenericRecord>, NullWritable>{
//定义map需要的变量
private String aid = "";
private String pkgname = "";
@Override
protected void map(AvroKey<GenericRecord> key, NullWritable value,
Mapper<AvroKey<GenericRecord>, NullWritable, AvroKey<GenericRecord>, NullWritable>.Context context)
throws IOException, InterruptedException {
//获取AVRO表的一行数据
GenericRecord record1 = key.datum();
//解析数据
aid = String.valueOf(record1.get("aid"));
pkgname = String.valueOf(record1.get("pkgname"));
//判断数据是否为空
aid = Utils.isEmpty(aid)||aid.toLowerCase().equals("null")?null:aid;
pkgname = Utils.isEmpty(pkgname)||pkgname.toLowerCase().equals("null")?null:pkgname;
//保存数据
GenericRecord record2 = new GenericData.Record(schema2);
record2.put("aid", aid);
record2.put("pkgname", pkgname);
//输出
context.write(new AvroKey<GenericRecord>(record2), NullWritable.get());
}
}
@Override
public Job getJob(Configuration jobConf) throws Exception {
Job job = Job.getInstance(jobConf, this.getJobNameWithTaskID());
job.setJarByClass(AvroToAvro.class);
job.setMapperClass(MyMapper.class);
job.setNumReduceTasks(0);
job.setOutputKeyClass(AvroKey.class);
job.setOutputValueClass(NullWritable.class);
job.setInputFormatClass(AvroKeyInputFormat.class);
job.setOutputFormatClass(AvroKeyOutputFormat.class);
//指定使用哪个结构进行数据的导出
AvroJob.setOutputKeySchema(job, schema2);
//设置输入与输出的地址
FileInputFormat.addInputPath(job, new Path(jobConf.get(MyConstant.TASK_INPUT)));
FileOutputFormat.setOutputPath(job, this.getPath(this.getJobNameWithTaskID()));
return job;
}
@Override
public String getJobName() {
return "avro_to_avro";
}
}
1.com.shanhai.bin
1.ATARunner
java
package com.shanhai.bin;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import com.shanhai.mr.AvroToAvro;
import com.shanhai.util.JobRunnerResult;
import com.shanhai.util.JobRunnerUtil;
public class ATARunner extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
//创建任务工作链
JobControl jobc = new JobControl("jw1");
//获取Hadoop的配置文件对象
Configuration conf = this.getConf();
//设置公共的配置文件加载对象conf,只需加载一次就可以
AvroToAvro.setConf(conf);
AvroToAvro hde = new AvroToAvro();
//获取工作链的任务
ControlledJob job1 = hde.getCtrlJob();
//设置工作链的任务
jobc.addJob(job1);
//运行任务
JobRunnerResult result = JobRunnerUtil.run(jobc);
if (result.getIsSuccess()) {
//这里可以打印Counters的信息
System.out.println("JOB_STATUS : OK!");
System.out.println("THE PROGRAM IS RUN " + result.getRunTime());
} else {
System.out.println("JOB_STATUS : FALL!");
}
return 0;
}
public static void main(String[] args) {
try {
System.exit(ToolRunner.run(new ATARunner(), args));
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.线下运行:
powershell
1.运行命令:
-Dmymr.task.id=luo -Dmymr.task.input=D:\hdfs\input\hn16\mr10
2.找到输出文件,打开文件 整理avro的JSON建表描述,创建avro_to_avro.avro文件
{
"type":"record",
"name":"user_avro",
"namespace":"db1",
"fields":[{
"name":"aid",
"type":["null","string"],
"default":null
},{
"name":"pkgname",
"type":["null","string"],
"default":null
}
]
}
{"type":"record","name":"user_avro","namespace":"db1","fields":[{"name":"aid","type":["null","string"],"default":null},{"name":"pkgname","type":["null","string"],"default":null}}
3.将avro_to_avro.avro文件,上传到/user/luoxichuan/avroconf/目录
[hadoop@nn1 ~]$ cd ~/myhbase
[hadoop@nn1 myhbase]$ rz
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ hadoop fs -put avro_to_avro.avro /user/luoxichuan/avroconf/
4.创建新的avro表并导入数据
use db1;
CREATE TABLE IF NOT EXISTS user_install_status_avro_other
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
WITH SERDEPROPERTIES ('avro.schema.url'='/user/luoxichuan/avroconf/avro_to_avro.avro')
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat';
5.查看表的位置
hive (db1)> show create table user_install_status_avro_other;
6.上传数据
[hadoop@nn1 myhbase]$ hadoop fs -put part-m-00000.avro hdfs://ns1/hive/warehouse/db1.db/user_install_status_avro_other
7.查询验证
hive (db1)> select * from user_install_status_avro_other;
5.集群运行
shell
1.在集群运行之前的说明
1.注意,在集群上2.7.3的hadoop版本,avrojar包只有1.7.4版本的;
2.在集群运行会报错,报错内容如下:缺少createDatumWriter()方法;
3.但是1.7.7 版本的avro有此方法。所以,需要你删除集群上1.7.4 版本avro的jar
2.替换集群所有机器的Jar包
# 删除/usr/local/hadoop/share/hadoop/common/lib/下的avro-1.7.4.jar
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh rm -rf /usr/local/hadoop/share/hadoop/common/lib/avro-1.7.4.jar
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh ls -l /usr/local/hadoop/share/hadoop/common/lib/ | grep avro
# 删除/usr/local/hbase/lib/下的avro-1.7.4.jar
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh rm -rf /usr/local/hbase/lib/avro-1.7.4.jar
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh ls -l /usr/local/hbase/lib/ | grep avro
# 上传avro-1.7.7.jar
[hadoop@nn1 ~]$ rz
[hadoop@nn1 ~]$ ll
# 分发avro-1.7.7.jar
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/scp_all.sh ~/avro-1.7.7.jar /tmp/
# 复制avro-1.7.7.jar到/usr/local/hadoop/share/hadoop/common/lib/
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh cp /tmp/avro-1.7.7.jar /usr/local/hadoop/share/hadoop/common/lib/
# 复制avro-1.7.7.jar到/usr/local/hbase/lib/
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh cp /tmp/avro-1.7.7.jar /usr/local/hbase/lib/
# 复制avro-1.7.7.jar到/usr/local/hadoop/share/hadoop/yarn
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh cp /tmp/avro-1.7.7.jar /usr/local/hadoop/share/hadoop/yarn
# 复制avro-1.7.7.jar到/usr/local/hadoop/lib
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh cp /tmp/avro-1.7.7.jar /usr/local/hadoop/lib
3.检查/usr/local/hadoop/lib 目录下是否有hive-exec-2.1.1.jar(hbase批量导入时,配置的集群环境),如果没有,把该jar包分发到所有机器的/usr/local/hadoop/lib 目录下。
[hadoop@nn1 ~]$ sh ~/hadoop_base_op/ssh_all.sh ls -l /usr/local/hadoop/lib | grep hive-exec
1.打包项目:
打包命令:clean assembly:assembly
2.上传Jar包:
[hadoop@nn1 ~]$ cd myhbase/
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ rz
3.集群运行
hadoop jar hn16_hbase_tp6-0.0.1-SNAPSHOT-luoxichuan.jar com.shanhai.bin.ATARunner -Dmymr.task.id=luo -Dmymr.task.input=/hive/warehouse/db1.db/user_avro
3.找到输出文件,下载文件 整理avro的JSON建表描述,创建avro_to_avro.avro文件
{
"type":"record",
"name":"user_avro",
"namespace":"db1",
"fields":[{
"name":"aid",
"type":["null","string"],
"default":null
},{
"name":"pkgname",
"type":["null","string"],
"default":null
}
]
}
{"type":"record","name":"user_avro","namespace":"db1","fields":[{"name":"aid","type":["null","string"],"default":null},{"name":"pkgname","type":["null","string"],"default":null}}
4.将avro_to_avro.avro文件,上传到/user/luoxichuan/avroconf/目录
[hadoop@nn1 ~]$ cd ~/myhbase
[hadoop@nn1 myhbase]$ rz
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ hadoop fs -put avro_to_avro.avro /user/luoxichuan/avroconf/
5.创建外部-AVRO表
CREATE EXTERNAL TABLE IF NOT EXISTS user_install_status_avro_other2
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
WITH SERDEPROPERTIES ('avro.schema.url'='/user/luoxichuan/avroconf/avro_to_avro.avro')
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
LOCATION 'hdfs://ns1/user/luoxichuan/task/avro_to_avro_luo';
6.验证数据
hive (db1)> select * from user_install_status_avro_other1;
十一、Hbase数据的批量导出
1.项目需求:
sql
Hbase数据的批量导出:自定义inputformat实现数据转换
准备数据:
scan 'myns1:user_install_status'
2.项目的整体架构:
1.配置文件
1.pom.xml文件
xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.shanhai</groupId>
<artifactId>hn16_hbase_tp7</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- maven项目整体编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 项目编译的时候,源码(.java)使用哪个版本JDK -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 项目编译的时候,可执行文件(.class)使用哪个版本JDK -->
<maven.compiler.target>1.8</maven.compiler.target>
<!-- 设置运行主类 -->
<mainClass>com.shanhai.bin.NHDEDriver</mainClass>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-cli</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-common</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>${mainClass}</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<skip>true</skip>
<forkMode>once</forkMode>
<excludes>
<exclude>**/**</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.新建assembly.xml文件,放到资源文件目录下
xml
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<!-- TODO: a jarjar format would be better -->
<!-- 添加到生成文件名称的后缀符 -->
<id>shanhai</id>
<!-- 打包类型 -->
<formats>
<format>jar</format>
</formats>
<!-- 指定是否包含打包层目录 -->
<includeBaseDirectory>false</includeBaseDirectory>
<!-- 指定要包含的文件集 -->
<fileSets>
<fileSet>
<!-- 指定目录 -->
<directory>${project.build.directory}/classes</directory>
<!-- 指定文件集合的输出目录,该目录是相对于根目录 -->
<outputDirectory>/</outputDirectory>
<!-- 排除文件 -->
<excludes>
<exclude>*.xml</exclude>
<exclude>*.properties</exclude>
</excludes>
</fileSet>
</fileSets>
<!-- 用来定制工程依赖 jar 包的打包方式 -->
<dependencySets>
<dependencySet>
<!-- 指定包依赖目录,该目录是相对于根目录 -->
<outputDirectory>/</outputDirectory>
<!-- 当前项目构件是否包含在这个依赖集合里 -->
<useProjectArtifact>false</useProjectArtifact>
<!-- 是否将第三方jar包解压到该依赖中 false 直接引入jar包 true解压引入 -->
<unpack>true</unpack>
<!-- 将scope为runtime的依赖包打包到lib目录下。 -->
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
2.com.shanhai.util
1.ORCWriteUtil
java
package com.shanhai.util;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hive.ql.io.orc.OrcSerde;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
import org.apache.hadoop.io.Writable;
/*
* ORC记录写入工具类:传入相应的参数,写入到orc文件中去
*/
public class ORCWriteUtil {
//1.定义ORC结构的解析对象
private ObjectInspector oi = null;
//2.定义一个List集合,用于存储ORC的字段
private List<Object> list = null;
//3.定义ORC文件的序列化对象
private OrcSerde os = null;
/*
* 初始化ORC结构的解析对象
*/
public void setOrcWriteType(String type) {
//获取ORC结构信息
TypeInfo typeInfo = TypeInfoUtils.getTypeInfoFromTypeString(type);
//获取ORC结构的解析对象
oi = TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(typeInfo);
}
/*
* 添加要写入的ORC数据
*/
public ORCWriteUtil addAttr(Object... params) {
//判断一下保存数据的集合是否为空,如果为空就重新创建
if (Utils.isEmpty(list)) {
list = new ArrayList<Object>();
}
//解析参数,进行数据的添加
for (Object o : params) {
list.add(o);
}
//返回
return this;
}
/*
* 将ORC数据进行hadoop序列化
*/
public Writable serialize() {
//判断是否为空
if (Utils.isEmpty(os)) {
os = new OrcSerde();
}
//进行数据的序列化
Writable result = os.serialize(list, oi);
//清空保存数据的集合
list = new ArrayList<Object>();
return result;
}
}
2.HFileInputFormat:自定义输入格式化类
java
package com.shanhai.util;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
import org.apache.hadoop.hbase.io.hfile.HFile;
import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
import org.apache.hadoop.hbase.io.hfile.HFile.Reader;
import org.apache.hadoop.hbase.io.hfile.HFileScanner;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
public class HFileInputFormat extends FileInputFormat<ImmutableBytesWritable, KeyValue> {
@Override
public RecordReader<ImmutableBytesWritable, KeyValue> createRecordReader(InputSplit split,
TaskAttemptContext context) throws IOException, InterruptedException {
return new HFileRecordReader();
}
/*
*创建自己的数据记录读取器
*/
private static class HFileRecordReader extends RecordReader<ImmutableBytesWritable, KeyValue>{
//创建HFile读取器对象
private Reader reader;
//创建HFile扫描器对象
private HFileScanner scanner;
//获取的数据的个数
private Long entryNum = 0L;;
/*
* 进行读取器的初始化
*/
@Override
public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
//获取HFile的数据分片对象
FileSplit fs = (FileSplit)split;
//获取数据分片的路径
Path path = fs.getPath();
//获得HFile读取器
Configuration conf = context.getConfiguration();
reader = HFile.createReader(FileSystem.get(conf), path, new CacheConfig(conf), conf);
//获取HFile文件的元数据信息
FileInfo info = (FileInfo)reader.loadFileInfo();
//输出数据长度
System.out.println("文件长度:"+info.size());
//获取HFile扫描器
scanner = reader.getScanner(false, false);
//HFile扫描器归位
scanner.seekTo();
}
/*
* 判断是否有下一个数据
*/
@Override
public boolean nextKeyValue() throws IOException, InterruptedException {
entryNum++;
return scanner.next();
}
/*
* 获取Hfile中每行记录的Rowkey,这里的一行不是指Hbase完整的一行记录,是Hfile文件中存储的行
*/
@SuppressWarnings("deprecation")
@Override
public ImmutableBytesWritable getCurrentKey() throws IOException, InterruptedException {
return new ImmutableBytesWritable(scanner.getKeyValue().getRow());
}
/*
* 获取Hfile中的每行记录的value,包括列名,版本和值
*/
@Override
public KeyValue getCurrentValue() throws IOException, InterruptedException {
return (KeyValue)scanner.getKeyValue();
}
/*
* 获取整体的处理进度
*/
@Override
public float getProgress() throws IOException, InterruptedException {
if (Utils.isNotEmpty(reader)) {
return entryNum/reader.getEntries();
}
return 1;
}
/*
* 关闭HFile读取器
*/
@Override
public void close() throws IOException {
if (Utils.isNotEmpty(reader)) {
reader.close();
}
}
}
}
3.com.shanhai.entity
1.HFileItem
java
package com.shanhai.entity;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
/*
* Hbase单元格信息的定义
*/
public class HFileItem implements Writable{
//定义属性
private String value = ""; // HFile存储中物理行的值
private Long version = -1L; // 版本
private boolean isDelete = false; // 是否被删除
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public boolean isDelete() {
return isDelete;
}
public void setDelete(boolean isDelete) {
this.isDelete = isDelete;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeUTF(value);
out.writeLong(version);
out.writeBoolean(isDelete);
}
@Override
public void readFields(DataInput in) throws IOException {
this.value = in.readUTF();
this.version = in.readLong();
this.isDelete = in.readBoolean();
}
}
2.HFileRecord
java
package com.shanhai.entity;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
/*
* Hbase的实体类
*/
public class HFileRecord implements Writable {
//定义需要的属性
//Hbase表中的国家字段
private HFileItem country = new HFileItem();
//Hbase表中的包名字段
private HFileItem pkgname = new HFileItem();
//Hbase表中的类型字段
private HFileItem type = new HFileItem();
//Hbase表中的包的分类,比如游戏、社交
private HFileItem gpcategory = new HFileItem();
public HFileItem getCountry() {
return country;
}
public void setCountry(HFileItem country) {
this.country = country;
}
public HFileItem getPkgname() {
return pkgname;
}
public void setPkgname(HFileItem pkgname) {
this.pkgname = pkgname;
}
public HFileItem getType() {
return type;
}
public void setType(HFileItem type) {
this.type = type;
}
public HFileItem getGpcategory() {
return gpcategory;
}
public void setGpcategory(HFileItem gpcategory) {
this.gpcategory = gpcategory;
}
@Override
public void write(DataOutput out) throws IOException {
country.write(out);
pkgname.write(out);
type.write(out);
gpcategory.write(out);
}
@Override
public void readFields(DataInput in) throws IOException {
country.readFields(in);
pkgname.readFields(in);
type.readFields(in);
gpcategory.readFields(in);
}
}
4.com.shanhai.mr
1.NewHbaseDateExport
java
package com.shanhai.mr;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hive.ql.io.orc.OrcNewOutputFormat;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import com.shanhai.entity.HFileItem;
import com.shanhai.entity.HFileRecord;
import com.shanhai.util.BaseMR;
import com.shanhai.util.HFileInputFormat;
import com.shanhai.util.MyConstant;
import com.shanhai.util.ORCFormat;
import com.shanhai.util.ORCWriteUtil;
import com.shanhai.util.Utils;
public class NewHbaseDateExport extends BaseMR {
public static class MyMapper extends Mapper<ImmutableBytesWritable, KeyValue, Text, HFileRecord>{
//定义map需要用到的变量
private Text outkey = new Text();
private HFileRecord outval = null;
private HFileItem item = new HFileItem();
private String rowkey = "";
private byte[] buffers = null;
private String cn = "";
private String cv = "";
@Override
protected void map(ImmutableBytesWritable key, KeyValue value,
Mapper<ImmutableBytesWritable, KeyValue, Text, HFileRecord>.Context context)
throws IOException, InterruptedException {
//获取rowkey
rowkey = Bytes.toString(key.get());
//判断这个rowkey是否有效
if (Utils.isEmpty(rowkey) && rowkey.split(MyConstant.SQE1).length != 2) {
return;
}
//获取数据
buffers = value.getBuffer();
//获取列名和值
cn = Bytes.toString(byteCopy(buffers, value.getQualifierOffset(), value.getQualifierLength()));
cv = Bytes.toString(byteCopy(buffers, value.getValueOffset(), value.getValueLength()));
//封装我们这个数据的属性
item.setValue(cv);
item.setVersion(value.getTimestamp());
item.setDelete(value.isDelete());
//判断一下我们封装的哪个数据
outval = new HFileRecord();
if (cn.equals("country")) {
outval.setCountry(item);
}else if (cn.equals("pkgname")) {
outval.setPkgname(item);
}else if (cn.equals("type")) {
outval.setType(item);
}else if (cn.equals("gpcategory")) {
outval.setGpcategory(item);
}
//输出
outkey.set(rowkey);
context.write(outkey, outval);
}
public byte[] byteCopy(byte[] buff, int offset, int length) {
byte[] copy1 = new byte[length];
System.arraycopy(buff, offset, copy1, 0, length);
return copy1;
}
}
/*
* 将相同的key在map中进行局部聚合,获取一整行数据的部分内容
*/
public static class MyCombiner extends Reducer<Text, HFileRecord, Text, HFileRecord>{
//创建需要的变量
private HFileRecord record = null;
@Override
protected void reduce(Text key, Iterable<HFileRecord> values,
Reducer<Text, HFileRecord, Text, HFileRecord>.Context context) throws IOException, InterruptedException {
//创建中间变量
record = new HFileRecord();
//判断这个rowkey是否有效
if (Utils.isEmpty(key.toString()) && key.toString().split(MyConstant.SQE1).length != 2) {
return;
}
//数据局部聚合
for (HFileRecord r : values) {
if (r.getCountry().getVersion() >= record.getCountry().getVersion()) {
record.getCountry().setValue(r.getCountry().getValue());
record.getCountry().setVersion(r.getCountry().getVersion());
record.getCountry().setDelete(r.getCountry().isDelete());
}
if (r.getPkgname().getVersion() >= record.getPkgname().getVersion()) {
record.getPkgname().setValue(r.getPkgname().getValue());
record.getPkgname().setVersion(r.getPkgname().getVersion());
record.getPkgname().setDelete(r.getPkgname().isDelete());
}
if (r.getType().getVersion() >= record.getType().getVersion()) {
record.getType().setValue(r.getType().getValue());
record.getType().setVersion(r.getType().getVersion());
record.getType().setDelete(r.getType().isDelete());
}
if (r.getGpcategory().getVersion() >= record.getGpcategory().getVersion()) {
record.getGpcategory().setValue(r.getGpcategory().getValue());
record.getGpcategory().setVersion(r.getGpcategory().getVersion());
record.getGpcategory().setDelete(r.getGpcategory().isDelete());
}
}
//输出
context.write(key, record);
}
}
private static class MyReducer extends Reducer<Text, HFileRecord, NullWritable, Writable>{
//定义需要用到的变量
private ORCWriteUtil owu = new ORCWriteUtil();
@Override
protected void setup(Reducer<Text, HFileRecord, NullWritable, Writable>.Context context)
throws IOException, InterruptedException {
owu.setOrcWriteType(ORCFormat.MY_ORC_TYPE1);
}
//定义需要用到的变量
private HFileRecord record = null;
private String[] strs = null;
private String aid = "";
private Long uptime = -1L;
private String tmpuptime = "";
private DateFormat df = new SimpleDateFormat("yyyyMMdd");
private String pkgname,country,gpcategory;
private Integer type;
@Override
protected void reduce(Text key, Iterable<HFileRecord> values,
Reducer<Text, HFileRecord, NullWritable, Writable>.Context context)
throws IOException, InterruptedException {
record = new HFileRecord();
//判断这个rowkey是否有效
if (Utils.isNotEmpty(key.toString()) && key.toString().split(MyConstant.SQE1).length == 2) {
strs = key.toString().split(MyConstant.SQE1);
aid = strs[0];
tmpuptime = strs[1];
try {
uptime = df.parse(tmpuptime).getTime()/1000;
} catch (ParseException e) {
e.printStackTrace();
}
for (HFileRecord r : values) {
if (r.getCountry().getVersion() >= record.getCountry().getVersion()) {
record.getCountry().setValue(r.getCountry().getValue());
record.getCountry().setVersion(r.getCountry().getVersion());
record.getCountry().setDelete(r.getCountry().isDelete());
}
if (r.getPkgname().getVersion() >= record.getPkgname().getVersion()) {
record.getPkgname().setValue(r.getPkgname().getValue());
record.getPkgname().setVersion(r.getPkgname().getVersion());
record.getPkgname().setDelete(r.getPkgname().isDelete());
}
if (r.getType().getVersion() >= record.getType().getVersion()) {
record.getType().setValue(r.getType().getValue());
record.getType().setVersion(r.getType().getVersion());
record.getType().setDelete(r.getType().isDelete());
}
if (r.getGpcategory().getVersion() >= record.getGpcategory().getVersion()) {
record.getGpcategory().setValue(r.getGpcategory().getValue());
record.getGpcategory().setVersion(r.getGpcategory().getVersion());
record.getGpcategory().setDelete(r.getGpcategory().isDelete());
}
//判断数据是否被删除
if (record.getCountry().isDelete()&&record.getPkgname().isDelete()&&record.getType().isDelete()&&record.getGpcategory().isDelete()) {
return;
}
//获取数据
pkgname = record.getPkgname().getValue();
country = record.getCountry().getValue();
gpcategory = record.getGpcategory().getValue();
type = Integer.parseInt(record.getType().getValue());
//数据写入
owu.addAttr(aid,pkgname,uptime,type,country,gpcategory);
//输出
context.write(NullWritable.get(), owu.serialize());
}
}
}
}
@Override
public Job getJob(Configuration jobConf) throws Exception {
Job job = Job.getInstance(jobConf, getJobNameWithTaskID());
job.setJarByClass(NewHbaseDateExport.class);
job.setMapperClass(MyMapper.class);
job.setCombinerClass(MyCombiner.class);
job.setReducerClass(MyReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(HFileRecord.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Writable.class);
job.setInputFormatClass(HFileInputFormat.class);
job.setOutputFormatClass(OrcNewOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(jobConf.get(MyConstant.TASK_INPUT)));
FileOutputFormat.setOutputPath(job, this.getPath(this.getJobNameWithTaskID()));
return job;
}
@Override
public String getJobName() {
return "new_hbase_data_export";
}
}
5.com.shanhai.bin
1.NHDERunner
java
package com.shanhai.bin;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import com.shanhai.mr.NewHbaseDateExport;
import com.shanhai.util.JobRunnerResult;
import com.shanhai.util.JobRunnerUtil;
public class NHDERunner extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
//创建任务工作链
JobControl jobc = new JobControl("jobwork_new_hbase_data_export");
//获取Hadoop的配置文件对象
Configuration conf = this.getConf();
//设置公共的配置文件加载对象conf,只需加载一次就可以
NewHbaseDateExport.setConf(conf);
NewHbaseDateExport hde = new NewHbaseDateExport();
//获取工作链的任务
ControlledJob job1 = hde.getCtrlJob();
//设置工作链的任务
jobc.addJob(job1);
//运行任务
JobRunnerResult result = JobRunnerUtil.run(jobc);
if (result.getIsSuccess()) {
//这里可以打印Counters的信息
System.out.println("JOB_STATUS : OK!");
System.out.println("THE PROGRAM IS RUN " + result.getRunTime());
} else {
System.out.println("JOB_STATUS : FALL!");
}
return 0;
}
public static void main(String[] args) {
try {
System.exit(ToolRunner.run(new NHDERunner(), args));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.NHDEDriver
java
package com.shanhai.bin;
import org.apache.hadoop.util.ProgramDriver;
public class NHDEDriver {
public static void main(String[] args) {
//创建驱动程序对象
ProgramDriver driver = new ProgramDriver();
try {
//加载要执行的类
driver.addClass("nhde", NHDERunner.class, "hbase数据导出新方案!");
//反射执行
ProgramDriver.class.getMethod("driver", new Class[]{String[].class}).invoke(driver, new Object[]{args});
} catch (Throwable e) {
e.printStackTrace();
}
}
}
3.集群运行
java
1.打包项目:
打包命令:clean assembly:assembly
2.上传Jar包:
[hadoop@nn1 ~]$ cd myhbase/
[hadoop@nn1 myhbase]$ ll
[hadoop@nn1 myhbase]$ rz
.集群运行
hadoop jar hn16_hbase_tp7-0.0.1-SNAPSHOT-luoxichuan.jar nhde -Dmymr.task.id=luo -Dmymr.task.input=/hbase/data/myns1/user_install_status/71dc316c82245fde9f5299875f445ea6/cf1
创建外部表指定本地文件路径:
CREATE EXTERNAL TABLE `table1`(
`aid` string COMMENT 'from deserializer',
`pkgname` string COMMENT 'from deserializer',
`uptime` bigint COMMENT 'from deserializer',
`type` int COMMENT 'from deserializer',
`country` string COMMENT 'from deserializer',
`gpcategory` string COMMENT 'from deserializer')
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.orc.OrcSerde'
STORED AS INPUTFORMAT
'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'
LOCATION 'hdfs://ns1/user/luoxichuan/task/new_hbase_data_export_luo'
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.create.index'='true');
6.验证结果
select * from `table1`;
7.进阶作业:
如何进行mr任务的全表扫描:
自己写个读取HDFS文件地址列表的方法,读指定hbase表下的所有存放HFILE文件的目录,这样就可以使你的mr扫描该表下所有的hfile文件,从而达到通过hfile文件进行全表扫描数据的目的