前言
现今互联网科技发展日新月异,大数据、云计算、人工智能等技术已经成为前瞻性产品,海量数据和超高并发让传统的 Web2.0 网站有点力不从心,暴露了很多难以克服的问题。为此,Google、Amazon 、Powerset 等各大平台纷纷推出 NoSQL 技术以应对市场的急速发展,近10年间NoSQL技术百花齐放,HBase、Redis、MongoDB、Cassandra 等技术纷纷涌现。
本文主要向各位介绍 HBase 的发展历史,基础结构与原理,应用的场景,对常用的 JAVA API 操作进行梳理,在最后一节还会详细讲述 HBase 与 MR 之间关系。
目录
一、HBase 的概述
二、HBase 的原理
三、简述 RowKey 设计原理
四、HBase 的 Java API 开发实例
五、HBase与MapReduce的相互调用
一、HBase 的概述
1.1 HBase 的发展历史
HBase(Hadoop Database)是一个高可靠性、高性能、面向列、可伸缩的分布式数据库,典型的 NoSQL(Not Only SQL)数据库。它起源于 Hadoop 的子项目,由 Powerset 公司在2007年创建,同年10 月 HBase 的第一版与 Hadoop 0.15.0 捆绑发布,初期的目标是弥补 MapReduce 在实时操作上的缺失,方便用户可随时操作大规模的数据集。随着大数据与 NoSQL 的流行和迅速发展,在 2010年5月,Apache HBase 脱离了 Hadoop,成为 Apache 基金的顶级项目。次年即 2011年1月 ZooKeeper 也脱离 Hadoop,成为 Apache 基金的顶级项目。
1.2 HBase 的特点
- 面向列设计:面向列表(簇)的存储和权限控制,列(簇)独立检索。
- 支持多版本:每个单元中的数据可以有多个版本,默认情况下,版本号可自动分配,版本号就是单元格插入时的时间戳。
- 稀疏性:为空的列不占用存储空间,表可以设计得非常稀疏。
- 高可靠性:WAL机制保证了数据写入时不会因集群异常而导致写入数据丢失,Replication机制保证了在集群出现严重的问题时,数据不会发生丢失或损坏。
- 高性能:底层的LSM数据结构和 Rowkey 有序排列等架构上的独特设计,使得Hbase具有非常高的写入性能。通过科学性地设计RowKey 可让数据进行合理的 Region 切分,主键索引和缓存机制使得Hbase 在海量数据下具备高速的随机读取性能。(下文将再作介绍)
1.3 HBase (NoSQL)与 RDBMS 的区别
1.3.1 传统的 RDBMS 具有以下特征
它是面向表格、视图设计的标准化数据,表中的数据类型也会进行预定义,数据保存后表的结构不易修改。每个表格对列的数据有所限制,最大不会超过几个百个,这将导致不同的数据可能会存放到多个表,表格之间存在一对一,一对多,多对一,多对多等复杂关系。正因如此也限制了 RDBMS 的使用场景更适合于高度结构化的的行业,例如医疗,机关,教育等行业。
1.3.2 HBase 是典型的 NoSQL代表
相对于 RDBMS ,它属于一种高效的映射嵌套型弱视图设计,以Key-Value的方式存储数据,每一行数据都可以有不同的列设计。数据依赖于行键作为唯一标识,当行数据的结构发生变生时,HBase 也能根据需求作出灵活调整。数据以文本方式保存,HBase 把数据的解释任务交给了应用程序,因此它更适合于灵活的数据结构项目。
1.4 HBase 的版本问题
HBase 对 Hadoop 和 JDK 的版本支持性有一定要求,详细内容可在官网查询 http://hbase.apache.org/book.html
Hadoop version support matrix
"S" =supported ,"X"= not supported ,“NT"=Not tested
返回目录
二、HBase 的原理
2.1 HBase 的总体结构
HBase 的架构是依托于 Hadoop 的 HDFS 作为最基本存储基础单元,在 HBase 的集群中由一个 Master 主节点管理多个 Region Server ,而 Zookeeper 进行协调操作,其关系如下图所示:
2.1.1 HMaster
HMaster 用于启动任务管理多个HRegionServer,侦测各个HRegionServer之间的状态,当一个新的HRegionServer登录到HMaster时,HMaster 会告诉它等待分配数据,平衡 HRegionServer 之间的负载。而当某个 HRegionServer 死机时,HMaster会把它负责的所有HRegion标记为未分配,然后再把它们分配到其他 HRegionServer 中,并恢复HRegionServer的故障。事实上 HMaster 的负载很轻, HBase 允许有多个 HMaster 节点共存,但同一时刻只有一个 HMaster 能为系统提供服务,其他的 HMaster 节点处于待命的状态。当正在工作的 HMaster 节点宕机时,其他的 HMaster 则会接管HBase的集群。
2.1.2 HRegionServer
HBase中的所有数据从底层来说一般都是保存在HDFS中的,用户通过一系列HRegionServer获取这些数据。集群一个节点上一般只运行一个HRegionServer,且每一个区段的HRegion只会被一个HRegionServer维护。HRegionServer主要负责响应用户I/O请求,向HDFS文件系统读写数据,是HBase中最核心的模块。
2.1.3 Zookeeper
Apache Zookeeper 起源于 Hadoop 的分布式协同服务,是负责协调集群中的分布式组件,在 2011年1月 ZooKeeper 脱离 Hadoop,成为 Apache 基金的顶级项目。经过多年的发展 Zookeeper 已经成为了分布式大数据框架中容错性的标准框架,被多个分布式开源框架所应用。HBase 的组件之间是通过心跳机制协调系统之间的状态和健康信息的,这些功能都是通过消息实现,一旦消息因外界原因丢失,系统侧需要根据不同的情况进行处理, Zookeeper 的主要作用正是监听并协调各组件的运作。它监听了多个节点的使用状态,保证了 HMaster 处于正常运行当中,一旦 HMaster 发生故障时 Zookeeper 就会发出通知,备用的 HMaster 就会进行替代。Zookeeper 也会监测 HRegionServer 的健康状态, 一旦发生故障就会通知 HMaster ,把任务重新分配给正常的 HRegionServer 进行操作,并恢复有故障的 HRegionServer。
2.2 HBase 运作原理
在介绍完 HBase 的总体结构后,下面将为大家介绍一下 HRegion、HStore、MemStore、HFile、WAL 等组件是如何进行协调操作的,HBase 的运作原理图如下:
2.2.1 HRegion
每个 HRegionServer 内部管理了一系列 HRegion ,他们可以分别属于不同的逻辑表,每个 HRegion 对应了逻辑表中的一个连续数据段。HRegionServer 只是管理表格,实现读写操作。Client 直接连接到 HRegionServer,并通信获取 HBase 中的数据。而 HRegion 则是真实存放 HBase 数据的地方,也就说 HRegion 是 HBase 可用性和分布式的基本单位。当表的大小超过预设值的时候,HBase会自动将表划分为不同的区域,每个区域就是一个HRegion,以主键(RowKey)来区分。一个HRegion会保存一个表中某段连续的数据,一张完整的表数据是保存在多个 HRegion 中的,这些 HRegion 可以在同一个HRegionServer 中,也可以来源于不同的 HRegionServer。
2.2.2 HStore
每个 HRegion 由多个HStore组成,每个 HStore 对应逻辑表在这个 HRegion 集合中的一个 Column Family,建议把具有相近 IO 特性的 Column 存储在同一个 Column Family 中,以实现高效读取 。HStore 由一个 Memstore 及一系列 HFile 组成,Memstore 存储于内存当中,而 HFiles 则是写入到 HDFS 中的持久性文件。用户写入的数据首先会放入 MemStore,当 MemStore大小到达预设值(可通过 hbase.hregion.memstore.flush.size 进行配置)后就会 Flush 成一个StoreFile(即 HFile)文件。
2.2.3 MemStore
MemStore 是一个缓存 (In Memory Sorted Buffer),当所有数据完成 WAL 日志写后,就会写入MemStore 中,由 MemStore 根据一定的算法将数据 Flush 到地层 HDFS 文件中(HFile),每个 HRegion 中的每个 Column Family 有一个自己的 MemStore。当用户从 HBase 中读取数据时,系统将尝试从 MemStore 中读取数据,如果没找到相应数据才会尝试从 HFile 中读取。当服务器宕机时,MemStore 中的数据有可能会丢失,此时 HBase 就会使用 WAL 中的记录对 MemStroe 中的数据进行恢复。
2.2.4 HFile
HFile 是最终保存 HBase 数据行的文件,一个 HFile 文件属于一张表中的某个列簇,当中的数据是按 RowKey、Column Family、Column 升序排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。HFile 的具体格式如下图:
HFile 中每条键值存储的开发都包括2个固定长度的数字,分别表示键和值的长度,目的是让客户端可根据字节的偏移访问值域中的数据。根据上图可以看到 KeyValue 类中getKey和getRow方法的区别, getKey() 方法返回的是整个键(图中绿色部分),而 getRow() 方法返回的只是行键 RowKey(图中第4格)。
HFile 文件是根据表的列簇进行区分的,在执行持久化时,记录是有序的。但当 HFile 的文件内容增长到一定阈值后就会触发合并操作,多个 HFiles 就会合并成一个更大的 HFile,由于这几个 HFile 有可能在不同的时间段产生,为保证合并后数据依然是有序排列,HFile 会通过小量压缩或全量压缩进行合并,对 HFile 文件记录进行重新排序。由于全量压缩是一个耗费资源的操作,因此应该保证在资源充足的情况下进行(由于数据压缩问题已超出本文的界限,在以后的章节将会详细介绍)。
当单个 HFile 大小超过一定阈值后,会触发 Split 拆分操作,用户可通过配置 hbase.regionserver.region.split.policy 选择拆分的策略,拆分策略由 RegionSplitPolicy 类进行处理,目前系统已支持 IncreasingToUpperBoundRegionSplitPolicy、ConstantSizeRegionSplitPolicy、DelimitedKeyPrefixRegionSplitPolicy、KeyPrefixRegionSplitPolicy 等多种拆分策略。默认情况下 HRegion 将被拆分成 2 个 HRegion,父 HRegion 会下线,新分出的 2 个子 HRegion 会被 HMaster 分配到相应的 HRegionServer。
2.2.5 WAL
WAL(Write-Ahead-Log,又名 HLog)是 HRegionServer 中的日志记录的工具,当系统发生故障时,可以通过 WAL 恢复数据。在每次用户操作将数据写入MemStore的时候,也会写一份数据到 WAL 当中,WAL 当中包含了部分还没有写入 HFile 的文件。WAL 文件会定期滚动刷新,并删除旧的文件(已持久化到 HFile中的数据)。当 HMaster 通过 Zookeeper 感知到某个HRegionServer意外终止时,HMaster首先会处理遗留的 WAL 文件,将其中不同 HRegion 的 WAL 数据进行拆分,分别放到相应 WAL 的目录下,然后再将失效的 HRegion 重新分配,领取到这些 HRegion 的 HRegionServer 在加载 HRegion的过程中,会发现有历史 WAL 需要处理,因此会把 WAL 中的数据加载到 MemStore 中,然后 Flush 到HFiles,完成数据恢复。
用户可以通过禁用 WAL 方式提高HBase 的性能,然而这将有导致数据丢失的风险,用户应该谨慎处理。一旦禁用了 WAL,系统应该在接收到 HRegionServer 宕机的消息后重新启动写入程序,然而这有可能导致数据重复输入。
返回目录
三、简述 RowKey 设计原理
由于HBase属于分布式系统,数据会根据 RowKey 进行分块存储,只要合理设计好 RowKey 让数据均匀分散多台 HRegionServer 管理,时常被同时查找的数据被存储到同一个HRegion之内,这样就能最大限度地提高系统的性能(在第四节介绍到分页查询方法中,如果能巧妙地设计 RowKey 把每页的数据存储在一个HRegion之内,将有效地提升性能)。反之,如若 RowKey 设计不合理,让大量的数据存储在同一个 HRegionServer 而其他 HRegionServer 长期处理闲置状态,就会减低系统性能,还有可能让 HRegionSever 资源耗尽会发生错误。在拜读过 Sameer Wadkar 等大师所著的《Pro Apache Hadoop》和Nick Dimiduk 所著的 《HBase In Action》等文献后,本人对关于RowKey设计原则归纳成了下面几点:
- 注意 RowKey 的长度,RowKey越长系统I/O开销越大,在上千万条数据的系统当中RowKey设置超过100个字节,光RowKey就会消耗近1G 的流量
- 可将 Rowkey 的前位作为散列字段,由程序循环生成,扩展位放时间、表格、属性、时间戳等字段,这样可提高数据均衡分布在每个HRegionServer 实现负载均衡的几率。
- 利用组合式行键方式,以主机名,事件,时间戳作为行键,根据组件的访问特性进行排序分组。
以随机的散列作为前缀这点很好理解,例如有下面的Order表
OrderId | Goods | Price |
20180802001 | iPhone X | 8000 |
20180802002 | LYNK&CO Tools | 5000 |
20180823003 | AUSU N75S | 8600 |
20180713004 | Nikon D7500 | 7300 |
若只以OrderId作为RowKey,以递增方式进行存储,数据很有可能只存储于同一个HRegionServer,此时可以用Hash函数随机生成散列,加上必要的属性(可以是辨别符、时间戳等唯一属性),生成类似于154432_180802001、879531_180802002、544688_180823003、687851_180713004等数据。此数据的分配会更均衡,但查找时会更耗资源。
我们也可以利用主机名,事件,时间戳组合键的方式定制行键,例如系统配置了4个HRegionServer,分别用A、B、C、D代表,此时可以将 RowKey 配置为类似于 A-84548454-C、B-3223265-C、C-656565-C、D-333256-C,这里只是举个简单的例子,当然实际操作上组合方式有多种。此方法好处在于用户更容易地控制数据的分布,但会增加管理的繁琐度,如果服务器有变动时,数据存储可能需要重新整理。
其实RowKey设计本来就是一个复杂的问题,在这里介绍的只是冰山一角,希望对大家的RowKey设计有所启发。
返回目录
四、HBase 的 Java API 开发实例
4.1 HBase 的基础操作类
由于各版 HBase 的 Java API 会有不同,下面以比较稳定的 hbase-client 1.2.6 为例子介绍一下 HBase 的具体操作
4.1.1 org.apache.hadoop.hbase.HBaseConfiguration 类
继承了 org.apache.hadoop.conf.Configuration 类,主要用于配置系统运行环境,管理资源池
函数 | 描述 |
static Configuration create() | 获取当前运行环境下的 Configuration 配置对象 |
void addResource(Path file) | 通过给定的路径所指的文件来添加资源 |
void clear() | 清空所有已设置的属性 |
String get(String name) | 获取属性名对应的值 |
String getBoolean(String name, boolean defaultValue) | 获取为boolean类型的属性值,如果其属性值类型部位boolean,则返回默认属性值 |
void set(String name, String value) | 通过属性名来设置值 |
void setBoolean(String name, boolean value) | 设置boolean类型的属性值 |
下面是HBaseConfiguration常用的方式
1 public class HBaseUtils { 2 public static Configuration config; 3 public static Connection connection; 4 5 static{ 6 config=HBaseConfiguration.create(); 7 8 try { 9 connection=ConnectionFactory.createConnection(config); 10 11 } catch (IOException e) { 12 // TODO 自动生成的 catch 块 13 e.printStackTrace(); 14 } 15 } 16 ...... 17 }
4.1.2 org.apache.hadoop.hbase.client.Connection 接口
与 SQL 的 Connection 连接相似,用户管理 HBase 客户端与服务端的连接,在操作完成后可通过 connetion.close ()及时释放资源。
通过 Connection 类的 Admin getAdmin()方法可获取 Admin 管理类。
通过 ConnetionFactory 类的 static Connection createConnection (config) 静态方法可获取当前连接。
4.1.3 org.apache.hadoop.hbase.client.HBaseAdmin 类
用于管理HBase数据库的表信息,它提供的方法包括:创建表,删除表,列出表,使表有效或无效,以及添加或删除表列簇成员等。
下面例子可用于判断表格是否存在
1 public class HBaseUtils { 2 public static Configuration config; 3 public static Connection connection; 4 5 static{ 6 config=HBaseConfiguration.create(); 7 8 try { 9 connection=ConnectionFactory.createConnection(config); 10 } catch (IOException e) { 11 // TODO 自动生成的 catch 块 12 e.printStackTrace(); 13 } 14 } 15 16 public static void checkTable(String tableName) 17 throws Exception { 18 HBaseAdmin admin = (HBaseAdmin) connection.getAdmin(); 19 20 if (admin.tableExists(tableName)) { 21 System.out.println(tableName + " exists!"); 22 } 23 } 24 }
4.1.4 org.apache.hadoop.hbase.HTableDescriptor 类
用于操作表名及其对应表的列簇
4.1.5 org.apache.hadoop.hbase.HColumnDescriptor 类
维护列簇的信息,例如版本号,压缩设置等,通常在创建表或者为表添加列簇的时候使用。
列簇被创建后不能直接修改,只能通过删除然后重新创建的方式。 列簇被删除的时候,列簇里面的数据也会同时被删除。
一个表通常可以包含1~5个列簇,下面例子可用于新建表,如需要包含多个列簇,可以在columnFamily参数中用 “ , ” 输入
1 //新建表 2 public static boolean createTable(String tableName, String columnFamily) 3 throws Exception { 4 HBaseAdmin admin = (HBaseAdmin) connection.getAdmin(); 5 6 if (admin.tableExists(tableName)) { 7 System.out.println(tableName + " exists!"); 8 return false; 9 } else { 10 //建立列簇 11 String[] columnFamilyArray; 12 if(columnFamily.contains(",")) 13 columnFamilyArray = columnFamily.split(","); 14 else{ 15 columnFamilyArray=new String[1]; 16 columnFamilyArray[0]=columnFamily; 17 } 18 HColumnDescriptor[] hColumnDescriptor = new HColumnDescriptor[columnFamilyArray.length]; 19 for (int i = 0; i < hColumnDescriptor.length; i++) { 20 hColumnDescriptor[i] = new HColumnDescriptor(columnFamilyArray[i]); 21 } 22 //建立表对象 23 HTableDescriptor familyDesc = new HTableDescriptor(TableName.valueOf(tableName)); 24 for (HColumnDescriptor columnDescriptor : hColumnDescriptor) { 25 familyDesc.addFamily(columnDescriptor); 26 } 27 HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf(tableName), familyDesc); 28 //新建表 29 admin.createTable(tableDesc); 30 admin.close(); 31 return true; 32 } 33 }
4.1.6 org.apache.hadoop.hbase.client.Table 接口
由于 HTable 是非线性安全类,已经被系统丢弃,在 hbase-client v1.0 版本后在数据更新删除时应该使用线性安全的 Table 接口进行操作
方法 | 说明 |
boolean checkAdnPut(byte[] row, byte[] family, byte[] qualifier, byte[] value, Put put | 自动的检查row/family/qualifier是否与给定的值匹配 |
void close() | 释放所有的资源或挂起内部缓冲区中的更新,请谨记在数据更新删除后使用以释放资源 |
boolean exists(Get get) | 检查Get实例所指定的值是否存在于Table中 |
Result get(Get get) | 获取指定行的某些单元格所对应的值 |
Result[] get(List |
获取多行的单元格所对应的值 |
ResultScanner getScanner(Scan scan) | 获取当前给定列簇的scanner实例 |
HTableDescriptor getTableDescriptor() | 获取当前表的HTableDescriptor实例 |
void delete(Delete delete) | 删除数据 |
void delete(List |
删除多行数据 |
void put(List |
向表中添加多个值 |
void put(Put put) | 向表中添加值 |
此接口是HBase最常用的对表格操作的接口,具体的使用方法将在下一节再详细介绍
4.1.7 org.apache.hadoop.hbase.client.Put 类
主要用于对单个行执行添加操作,在 hbase-client v1.0 版本后 add 方法已经被 addColumn 方法所代替
方法 | 说明 |
Put addColumn(byte[] family, byte[] qualifier, byte[] value) | 将指定的列和对应的值添加到Put实例中 |
Put addColumn(byte[] family, byte[] qualifier, long ts, byte[] value) | 将指定的列和对应的值及时间戳添加到Put实例中 |
List |
获取指定列簇和对应值的列 |
4.1.8 org.apache.hadoop.hbase.client.Get 类
用于获取单行数据的相关信息
方法 | 说明 |
Get addColumn(byte[] family, byte[] qualifier) | 获取指定列簇和列修饰符对应的Get对象 |
Get addFamily(byte[] family) | 获取指定列簇对应列的Get对象 |
Get setTimeRange(long timeStamp) | 获取指定时间戳的Get对象 |
Get setFilter(Filter filter) | 执行Get操作时设置服务器端的过滤器 |
4.1.9 org.apache.hadoop.hbase.client.Delete类
用于获取单行数据的相关信息
方法 | 说明 |
Delete addColumn(byte[] family, byte[] qualifier) | 获取指定列簇和列修饰符的Delete对象(只包含当前version) |
Delete addColumn(byte[] family, byte[] qualifier,long timestamp) | 获取指定列簇、列修饰符和时间戳的Delete对象(只包含当前version) |
Delete addColumns(byte[] family, byte[] qualifier) | 获取指定列簇和列修饰符(包含所有version) |
Delete addColumns(byte[] family, byte[] qualifier,long timestamp) | 获取指定列簇、列修饰符和时间戳的Delete对象(包含所有version) |
Delete addFamily(byte[] family) | 获取指定的列簇的Delete对象 |
Delete setTimeRange(long timeStamp) | 获取指定时间戳的Delete对象 |
4.1.10 org.apache.hadoop.hbase.client.Result 类
用于封装数据查找后的单行查询结果,用 Map 结构保存符合列的对应属性。旧版本一直使用KeyValue来封装列的属性,但自从v1.0版本后,系统都会使用 Cell 对象来封装列的属性,多个旧方法已经被弃用,各位在使用时可以注意一下
方法 | 说明 |
byte[] getRow() | 获取当前行键值 |
byte[] getValue(byte[] family, byte[] qualifier) | 获取指定列簇和列修饰符的最新数据值 |
Cell getColumnLastestCell(byte[] family, byte[] qualifier) | 获取指定列簇、列修饰符最新版本的列 |
List |
获取指定列簇、列修饰符多个版本的列 |
boolean containsColumn(byte[] family, byte[] qualifier) | 判断是否存在指定列簇、列修饰符的列 |
Cell[] listCells() | 获取多个版本所有列 |
NavigableMap |
获取批定列簇的列 |
NavigableMap |
获取所有列簇所有版本的列 |
4.1.11 org.apache.hadoop.hbase.client.Scan类
当用户想在查询时一次返回多选结果,可以通过Scan进行条件查询,通过Scan可以设定多种过滤器和起始行,从而根据用户的需要对整张表进行扫描。然而在默认情况下,Scan对象是用迭代形式进行查询一次只返回一行,为了提高内存的使用率可以通过 Scan.setCaching (int caching)方法把返回行数进行调整,从而提高系统性能。
方法 | 说明 |
Scan addFamily(byte[] family) | 获取指定列簇的Scan |
Scan addColumn(byte[] family, byte[] qualifier) | 获取指定列簇和列修饰符的Scan |
Scan setCaching(int caching) | 设置缓存数量 |
Scan setStartRow(byte[] startRow) | 设置开始行rowkey,如果不设置将由第一行开始查找 |
Scan setStopRow(byte[] stopRow) | 设置结束行rowkey |
Scan setTimeStamp(long stamp) | 获取指定时间戳的Scan对象 |
Scan setMaxResultSize(long maxResultSize) | 设置最大返回数量 |
Scan setFilter(Filter filter) | 设置查询条件 |
4.1.12 org.apache.hadoop.hbase.client.ResultScanner 接口
由于Scan 查找会返回多行数据,为了实现逐行查询功能,ResultScanner类出现,它把扫描到每一行数据封装成一个Result实例,并将所有的Result实例放入一个迭代器中。
ResultScanner 只有几个简单的方法,在有条件的情况下,使用Result[] next(int paramInt)一次获取多条数据,有利用提升系统的性能。
方法 | 说明 |
Result next() | 返回Result,转到下一行 |
Result[] next (int paramInt) | 返回多行数据的Result数组 |
void close() | 关闭连接释放资源 |
4.2 通用类 HBaseUtils
下面本人参考了一些基础文献对HBase常用的CURD操作进行了归纳,整理出一个通用类HBaseUtils,希望对大家日常开发有所帮助。
由于HBase是分布式系统,其性能与数据的存储方式有莫大的关联,所以在开发时应该与第三节RowKey的设计原理相结合进行调整,要不然有可能影响系统性能。
1 public class HBaseUtils { 2 public static Configuration config; 3 public static Connection connection; 4 5 static{ 6 config=HBaseConfiguration.create(); 7 8 try { 9 connection=ConnectionFactory.createConnection(config); 10 } catch (IOException e) { 11 // TODO 自动生成的 catch 块 12 e.printStackTrace(); 13 } 14 } 15 16 //新建表 17 public static boolean createTable(String tableName, String columnFamily) 18 throws Exception { 19 HBaseAdmin admin = (HBaseAdmin) connection.getAdmin(); 20 21 if (admin.tableExists(tableName)) { 22 System.out.println(tableName + " exists!"); 23 return false; 24 } else { 25 //建立列簇 26 String[] columnFamilyArray; 27 if(columnFamily.contains(",")) 28 columnFamilyArray = columnFamily.split(","); 29 else{ 30 columnFamilyArray=new String[1]; 31 columnFamilyArray[0]=columnFamily; 32 } 33 HColumnDescriptor[] hColumnDescriptor = new HColumnDescriptor[columnFamilyArray.length]; 34 for (int i = 0; i < hColumnDescriptor.length; i++) { 35 hColumnDescriptor[i] = new HColumnDescriptor(columnFamilyArray[i]); 36 } 37 //建立表对象 38 HTableDescriptor familyDesc = new HTableDescriptor(TableName.valueOf(tableName)); 39 for (HColumnDescriptor columnDescriptor : hColumnDescriptor) { 40 familyDesc.addFamily(columnDescriptor); 41 } 42 HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf(tableName), familyDesc); 43 //新建表 44 admin.createTable(tableDesc); 45 admin.close(); 46 return true; 47 } 48 } 49 50 //插入数据 51 public static boolean put(String tablename, String row, String columnFamily, 52 String qualifier, String data) throws Exception { 53 Table table = connection.getTable(TableName.valueOf(tablename)); 54 Put put = new Put(Bytes.toBytes(row)); 55 put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(qualifier), 56 Bytes.toBytes(data)); 57 table.put(put); 58 System.out.println("put '" + row + "', '" + columnFamily + ":" + qualifier 59 + "', '" + data + "'"); 60 table.close(); 61 return true; 62 } 63 64 //插入多列数据 65 public static boolean put(String tablename, String row, String columnFamily, 66 String[] qualifierList, String[] dataList) throws Exception { 67 Table table = connection.getTable(TableName.valueOf(tablename)); 68 Put put = new Put(Bytes.toBytes(row)); 69 for(int n=0;n){ 70 put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(qualifierList[n]), 71 Bytes.toBytes(dataList[n])); 72 } 73 table.put(put); 74 table.close(); 75 return true; 76 } 77 78 //查看某行 79 public static Map getRow(String tablename, String row) throws Exception { 80 Table table = connection.getTable(TableName.valueOf(tablename)); 81 Get get = new Get(Bytes.toBytes(row)); 82 Result result = table.get(get); 83 table.close(); 84 return resultToMap(result); 85 } 86 87 //查看全表 88 public static List
返回目录
五、HBase与MapReduce的相互调用
5.1 MapReduce 与 HBase 的关系
有朋友常说现在流程NoSQL架构 ,系统常用的是 Spark、HBase、Redis、Cassandra 等技术,MapReducer 已经没落。然而本人觉得 MapReduce 与 HBase 等NoSQL技术并非处于对立发展的关系,反而是相辅相成,互补不足的技术。由于受到数据块、IO、网络资源等限制,MapReduce 的先天架构决定其主要应用于大型文件的存取管理。然而对大量小型文件的管理方面,MapReduce 却是无能为力。因此 HBase 等技术才会诞生并迅速发展,随着 Hadoop 2.0 的发展,HBase 以 HDFS 为基础,补尝了 MapReduce 在海量文件管理方面的不足。
在很多大型的政府机关,金融管理,图书管理,交通航运等平台上,往往会以 MR 作为大型的持久性资源库,以 HBase 进行数据提取作为某个区域或阶段的研究对象,加以分析挖掘,最后形成汇总。有见及此,Hadoop 为 MR与HBase开发出一系列的数据转换工具,方便开发人员利用。
5.2 常用类简介
5.2.1 TableMapReduceUtil 绑定表关系
TableMapReduceUtil为编辑人员准备的几个常用的方法,可快速地绑定Mapper/Reducer与HBase中Table的关系
方法 | 说明 |
static void setScannerCaching(Job job, int batchSize) | 设置Scan缓存大小,默认值为1,应按运行环境设置不适宜过大 |
static void initTableReducerJob(String table,Class extends TableReducer> reducer, Job job) | 绑定从Reducer输出数据所要存入的Table |
static void initTableMapperJob(String table, Scan scan,Class extends TableMapper> mapper, Class> outputKeyClass,Class> outputValueClass, Job job) | 绑定要从Table获取到Scan数据所送往的Mapper |
static void initTableMapperJob(List |
绑定要从多个Table中获取到的List |
5.2.2 设置数据格式
在设置Job的运行条件时可使用以下方法按照需要把数据输入或输入设置为Table格式
Job.setInputFormatClass(TableOutputFormat.class);
Job.setOutputFormatClass(TableOutputFormat.class);
5.2.3 TableMapper 与 TableReducer
为提供与 HBase 的数据对接,系统提供了TableMapper与TableReducer两个常用类
public abstract class TableMapper
extends Mapper
}
TableMappler 是继承了 Mapper
public abstract class TableReducer
extends Reducer
}
TableReducer 则继承了 Reducer
5.3 常用实例
5.3.1 利用TableReducer从MapReduce中提取数据发送到HBase中的某个Table
假设某机关单位用MR存储了全国人员的申请审核文件档案,各个省镇部门需要按照自己权限获取相应的资料保存到各自的HBase中
此时,可以在Mapper 中按时地区参数 Area 进行筛选,把符合条件的资料发送到 TableReducer, 通过 TaskInputOutputContext.write(ImmutableBytesWritable arg0, Mutation arg1) 方法,把Reducer 中的数据保存到 HBase 的 Table 中去。其中 ImmutableBytesWritable 为行键 RowKey,Mutation 为行数据。
1 public class MrToHBaseExample extends Configured implements Tool{ 2 3 public static class MyMapper extends Mapper{ 4 private static String Area="nothing"; 5 6 @Override 7 public void setup(Context context){ 8 String param=context.getConfiguration().getStrings("Area")[0]; 9 if(!param.isEmpty()) 10 Area=param; 11 } 12 13 public void map(LongWritable longwritable,Text text,Context context) 14 throws IOException,InterruptedException{ 15 String[] data=text.toString().split(","); 16 ExamineWritable examine=new ExamineWritable(); 17 examine.Id=new Text(data[0]); 18 examine.Name=new Text(data[1]); 19 examine.Age=new IntWritable(new Integer(data[2])); 20 examine.Gender=new Text(data[3]); 21 examine.Area=new Text(data[4]); 22 examine.Approved=new IntWritable(new Integer(data[5])); 23 if(Area.equals(data[4])){ 24 context.write(NullWritable.get(), examine); 25 } 26 } 27 } 28 29 public static class MyReducer extends TableReducer { 30 public void reduce(NullWritable key,Iterable values,Context context) 31 throws IOException,InterruptedException{ 32 33 for(ExamineWritable value : values){ 34 Put put=new Put(Bytes.toBytes(value.Id.toString())); 35 put.addColumn(Bytes.toBytes("family1"), Bytes.toBytes("id"), Bytes.toBytes(value.Id.toString())); 36 put.addColumn(Bytes.toBytes("family1"), Bytes.toBytes("name"), Bytes.toBytes(value.Name.toString())); 37 put.addColumn(Bytes.toBytes("family1"), Bytes.toBytes("age"), Bytes.toBytes(value.Age.toString())); 38 put.addColumn(Bytes.toBytes("family1"), Bytes.toBytes("gender"), Bytes.toBytes(value.Gender.toString())); 39 put.addColumn(Bytes.toBytes("family1"), Bytes.toBytes("area"), Bytes.toBytes(value.Area.toString())); 40 put.addColumn(Bytes.toBytes("family1"), Bytes.toBytes("approved"), Bytes.toBytes(value.Approved.toString())); 41 context.write(new ImmutableBytesWritable(Bytes.toBytes(value.Id.toString())), put); 42 } 43 } 44 } 45 46 public int run(String[] arg0) throws Exception { 47 // TODO 自动生成的方法存根 48 // TODO Auto-generated method stub 49 //生成表Examine,列簇family1 50 HBaseUtils.createTable("Examine", "family1"); 51 //建立任务Job 52 Job job=Job.getInstance(getConf()); 53 job.setJarByClass(MrToHBaseExample.class); 54 //初始化HBase,把MyReducer输入数据保存到Examine表 55 TableMapReduceUtil.initTableReducerJob("Examine", MyReducer.class, job); 56 //注册Key/Value类型为Text 57 job.setOutputKeyClass(ImmutableBytesWritable.class); 58 job.setOutputValueClass(Put.class); 59 //若Map的转出Key/Value不相同是需要分别注册 60 job.setMapOutputKeyClass(NullWritable.class); 61 job.setMapOutputValueClass(ExamineWritable.class); 62 //注册Mapper及Reducer处理类 63 job.setMapperClass(MyMapper.class); 64 job.setReducerClass(MyReducer.class); 65 //输入输出数据格式化类型为TextInputFormat/TableOutputFormat 66 job.setInputFormatClass(TextInputFormat.class); 67 job.setOutputFormatClass(TableOutputFormat.class); 68 //获取命令参数 69 String[] args=new GenericOptionsParser(getConf(),arg0).getRemainingArgs(); 70 FileInputFormat.setInputPaths(job,new Path(args[0])); 71 boolean status=job.waitForCompletion(true); 72 if(status) 73 return 0; 74 else 75 return 1; 76 } 77 78 public static void main(String[] args) throws Exception{ 79 Configuration conf=new Configuration(); 80 ToolRunner.run(new MrToHBaseExample(), args); 81 } 82 } 83 84 public class HBaseUtils { 85 public static Configuration config; 86 public static Connection connection; 87 88 static{ 89 config=HBaseConfiguration.create(); 90 91 try { 92 connection=ConnectionFactory.createConnection(config); 93 } catch (IOException e) { 94 // TODO 自动生成的 catch 块 95 e.printStackTrace(); 96 } 97 } 98 99 //新建表 100 public static boolean createTable(String tableName, String columnFamily) 101 throws Exception { 102 HBaseAdmin admin = (HBaseAdmin) connection.getAdmin(); 103 104 if (admin.tableExists(tableName)) { 105 System.out.println(tableName + " exists!"); 106 return false; 107 } else { 108 //建立列簇 109 String[] columnFamilyArray; 110 if(columnFamily.contains(",")) 111 columnFamilyArray = columnFamily.split(","); 112 else{ 113 columnFamilyArray=new String[1]; 114 columnFamilyArray[0]=columnFamily; 115 } 116 HColumnDescriptor[] hColumnDescriptor = new HColumnDescriptor[columnFamilyArray.length]; 117 for (int i = 0; i < hColumnDescriptor.length; i++) { 118 hColumnDescriptor[i] = new HColumnDescriptor(columnFamilyArray[i]); 119 } 120 //建立表对象 121 HTableDescriptor familyDesc = new HTableDescriptor(TableName.valueOf(tableName)); 122 for (HColumnDescriptor columnDescriptor : hColumnDescriptor) { 123 familyDesc.addFamily(columnDescriptor); 124 } 125 HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf(tableName), familyDesc); 126 //新建表 127 admin.createTable(tableDesc); 128 admin.close(); 129 return true; 130 } 131 } 132 }
只需要输入执行命令
hadoop jar 【Jar名称】 【Main类全名称】-D 【参数名=参数值】 【InputPath】
即可得到以下运行结果( 若要获取广东省范围内的文件,参数为 Area=GuangDong)
5.3.2 通过TableMapper把HBase中某个表的数据汇总到 MR
当系统需要把符合条件的数据从HBase汇总到MR时,可利用TableMapper工具类,先建立Scan对象,加入筛选条件,然后利用 static void initTableMapperJob(String table, Scan scan,Class extends TableMapper> mapper, Class> outputKeyClass,Class> outputValueClass, Job job) 方法绑定TableMapper,Scan,Job等关系,系统就会把 table 中符合筛选条件的数据发送Mapper 进行处理。
下面例子就是从HBase的Examine表中把Area等于GuangDong的数据发送到MR进行保存
1 public class MrToHBaseExample extends Configured implements Tool{ 2 3 public static class MyMapper extends TableMapper{ 4 5 public void map(ImmutableBytesWritable writable,Result result,Context context) 6 throws IOException,InterruptedException{ 7 ExamineWritable examine=new ExamineWritable(); 8 for(Cell cell:result.listCells()){ 9 String value=Bytes.toString(CellUtil.cloneValue(cell)); 10 //获取行数据 11 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("id")) 12 examine.Id=new Text(value); 13 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("name")) 14 examine.Name=new Text(value); 15 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("age")) 16 examine.Age=new IntWritable(new Integer(value)); 17 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("gender")) 18 examine.Gender=new Text(value); 19 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("area")) 20 examine.Area=new Text(value); 21 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("approved")) 22 examine.Approved=new IntWritable(new Integer(value)); 23 } 24 25 context.write(new Text(Bytes.toString(writable.get())),examine); 26 } 27 } 28 29 public static class MyReducer extends Reducer { 30 public void reduce(Text key,Iterable values,Context context) 31 throws IOException,InterruptedException{ 32 String data=null; 33 for(ExamineWritable value : values){ 34 data=value.Id.toString()+","+value.Name.toString()+"," 35 +value.Age.toString()+","+value.Gender.toString()+"," 36 +value.Area.toString()+","+value.Approved.toString(); 37 context.write(NullWritable.get(),new Text(data)); 38 } 39 } 40 } 41 42 public int run(String[] arg0) throws Exception { 43 // TODO 自动生成的方法存根 44 // TODO Auto-generated method stub 45 //建立任务Job 46 Job job=Job.getInstance(getConf()); 47 job.setJarByClass(MrToHBaseExample.class); 48 //初始化TableMapper,把Examine表中符合条件的数据发送到Mapper 49 Scan scan=new Scan(); 50 scan.addFamily(Bytes.toBytes("family1")); 51 Filter filter=new SingleColumnValueFilter(Bytes.toBytes("family1"),Bytes.toBytes("Area"),CompareOp.EQUAL,Bytes.toBytes("GuangDong")); 52 scan.setFilter(filter); 53 TableMapReduceUtil.initTableMapperJob("Examine",scan,MyMapper.class,Text.class,ExamineWritable.class,job); 54 //注册Key/Value类型为Text 55 job.setOutputKeyClass(NullWritable.class); 56 job.setOutputValueClass(Text.class); 57 //若Map的转出Key/Value不相同是需要分别注册 58 job.setMapOutputKeyClass(Text.class); 59 job.setMapOutputValueClass(ExamineWritable.class); 60 //注册Mapper及Reducer处理类 61 job.setMapperClass(MyMapper.class); 62 job.setReducerClass(MyReducer.class); 63 //输入输出数据格式化类型为TextInputFormat/TableOutputFormat 64 job.setInputFormatClass(TableInputFormat.class); 65 job.setOutputFormatClass(TextOutputFormat.class); 66 //获取命令参数 67 String[] args=new GenericOptionsParser(getConf(),arg0).getRemainingArgs(); 68 FileOutputFormat.setOutputPath(job,new Path(args[0])); 69 boolean status=job.waitForCompletion(true); 70 if(status) 71 return 0; 72 else 73 return 1; 74 } 75 76 public static void main(String[] args) throws Exception{ 77 Configuration conf=new Configuration(); 78 ToolRunner.run(new MrToHBaseExample(), args); 79 } 80 }
5.3.2 通过TableMapper把HBase中多个表的数据汇总到 MR
若输入的数据源来源于HBase的不同表格,TableMapReduceUtil 类还提供static void initTableMapperJob(List
假如在HBase中Examine表格保存了审核结果表,当中包含了申请编号id,申请人姓名 name,年龄 age,性别 gender,所在地 area,审核结果 approved等信息
Assess 表格保存了审核流程记录表,当中包含申请编号id,申请时间 filingDate,审核时间approvedDate,审核人 assessor,审核商品 goods等信息
系统会通过TableMapReduceUtil 类所提供的 static void initTableMapperJob(List
由于本文目的主要是讲述 HBase 对表数据的处理方式,在此对排序SortComparator/分组GroupComparator不作细说,如果对 MR 的数据排序组合操作有兴趣的朋友可以参考本人的另一篇文章
Hadoop 综合揭秘——MapReduce 基础编程(介绍 Combine、Partitioner、WritableComparable、WritableComparator 使用方式)
里面会有更详细的介绍
1 public class ExamineDetailKey implements WritableComparable{ 2 //当TableType为0时是审核结果表数据,为1是审核流程记录表数据 3 public static final IntWritable IsExamine=new IntWritable(0); 4 public static final IntWritable IsAssess=new IntWritable(1); 5 public IntWritable TableType=new IntWritable(); 6 public Text Id=new Text(); 7 8 @Override 9 public void readFields(DataInput in) throws IOException { 10 // TODO 自动生成的方法存根 11 this.TableType.readFields(in); 12 this.Id.readFields(in); 13 } 14 15 @Override 16 public void write(DataOutput out) throws IOException { 17 // TODO 自动生成的方法存根 18 this.TableType.write(out); 19 this.Id.write(out); 20 } 21 22 @Override 23 public int compareTo(ExamineDetailKey o) { 24 // TODO 自动生成的方法存根 25 if(this.Id.equals(o.Id)) 26 return this.TableType.compareTo(o.TableType); 27 else 28 return this.Id.compareTo(o.Id); 29 } 30 31 @Override 32 public boolean equals(Object o){ 33 if(!(o instanceof ExamineDetailKey)) 34 return false; 35 36 ExamineDetailKey writable=(ExamineDetailKey)o; 37 if(this.Id.equals(writable.Id)&&this.TableType.equals(writable.TableType)) 38 return true; 39 else 40 return false; 41 } 42 43 @Override 44 public int hashCode(){ 45 return (this.Id.toString()+this.TableType.toString()).hashCode(); 46 } 47 } 48 49 public class ExamineDetailValue implements Writable{ 50 public Text Id=new Text(); 51 public Text Name=new Text(); 52 public IntWritable Age=new IntWritable(); 53 public Text Gender=new Text(); 54 public Text Area=new Text(); 55 public IntWritable Approved=new IntWritable(); 56 public Text Assessor=new Text(); 57 public Text ApprovedDate=new Text(); 58 public Text FilingDate=new Text(); 59 public Text Goods=new Text(); 60 61 @Override 62 public void readFields(DataInput in) throws IOException { 63 // TODO 自动生成的方法存根 64 this.Id.readFields(in); 65 this.Name.readFields(in); 66 this.Age.readFields(in); 67 this.Gender.readFields(in); 68 this.Area.readFields(in); 69 this.Approved.readFields(in); 70 this.Assessor.readFields(in); 71 this.ApprovedDate.readFields(in); 72 this.FilingDate.readFields(in); 73 this.Goods.readFields(in); 74 } 75 76 @Override 77 public void write(DataOutput out) throws IOException { 78 // TODO 自动生成的方法存根 79 this.Id.write(out); 80 this.Name.write(out); 81 this.Age.write(out); 82 this.Gender.write(out); 83 this.Area.write(out); 84 this.Approved.write(out); 85 this.Assessor.write(out); 86 this.ApprovedDate.write(out); 87 this.FilingDate.write(out); 88 this.Goods.write(out); 89 } 90 } 91 92 public class ExamineDetailSortComparator extends WritableComparator { 93 public ExamineDetailSortComparator(){ 94 super(ExamineDetailKey.class,true); 95 } 96 } 97 98 public class ExamineDetailGroupComparator extends WritableComparator { 99 public ExamineDetailGroupComparator(){ 100 super(ExamineDetailKey.class,true); 101 } 102 103 @Override 104 public int compare(WritableComparable a,WritableComparable b){ 105 ExamineDetailKey key1=(ExamineDetailKey) a; 106 ExamineDetailKey key2=(ExamineDetailKey) b; 107 return key1.Id.compareTo(key2.Id); 108 } 109 } 110 111 public class MrToHBaseExample extends Configured implements Tool{ 112 113 public static class MyMapper extends TableMapper { 114 private String tablename; 115 116 @Override 117 public void setup(Context context) throws IOException,InterruptedException{ 118 TableSplit split=(TableSplit)context.getInputSplit(); 119 tablename=new String(split.getTableName()); 120 } 121 122 public void map(ImmutableBytesWritable writable,Result result,Context context) 123 throws IOException,InterruptedException{ 124 ExamineDetailKey key=new ExamineDetailKey(); 125 ExamineDetailValue value=new ExamineDetailValue(); 126 127 for(Cell cell:result.listCells()){ 128 String data=Bytes.toString(CellUtil.cloneValue(cell)); 129 if(tablename.equals("Examine")){ 130 //设置表类型辨识符TableType 131 key.TableType=ExamineDetailKey.IsExamine; 132 //获取行数据 133 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("id")){ 134 key.Id=new Text(data); 135 value.Id=new Text(data); 136 } 137 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("name")) 138 value.Name=new Text(data); 139 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("age")) 140 value.Age=new IntWritable(new Integer(data)); 141 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("gender")) 142 value.Gender=new Text(data); 143 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("area")) 144 value.Area=new Text(data); 145 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("approved")) 146 value.Approved=new IntWritable(new Integer(data)); 147 }else if(tablename.equals("Assess")){ 148 //设置表类型辨识符TableType 149 key.TableType=ExamineDetailKey.IsAssess; 150 //获取行数据 151 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("id")){ 152 key.Id=new Text(data); 153 value.Id=new Text(data); 154 } 155 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("assessor")) 156 value.Assessor=new Text(data); 157 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("approvedDate")) 158 value.ApprovedDate=new Text(data); 159 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("filingDate")) 160 value.FilingDate=new Text(data); 161 if((Bytes.toString(CellUtil.cloneQualifier(cell))).equals("goods")) 162 value.Goods=new Text(data); 163 } 164 } 165 166 context.write(key,value); 167 } 168 } 169 170 public static class MyReducer extends Reducer { 171 public void reduce(ExamineDetailKey key,Iterable values,Context context) 172 throws IOException,InterruptedException{ 173 String data=key.Id.toString(); 174 175 for(ExamineDetailValue value : values){ 176 if(key.TableType.equals(ExamineDetailKey.IsExamine)){ 177 data+=","+value.Name.toString()+","+value.Age.toString()+"," 178 +value.Gender.toString()+","+value.Area.toString()+"," 179 +value.Approved.toString(); 180 }else if(key.TableType.equals(ExamineDetailKey.IsAssess)){ 181 data+=","+value.Assessor.toString()+"," 182 +value.ApprovedDate.toString()+","+value.FilingDate.toString()+"," 183 +value.Goods.toString(); 184 context.write(NullWritable.get(),new Text(data)); 185 } 186 } 187 } 188 } 189 190 public int run(String[] arg0) throws Exception { 191 // TODO 自动生成的方法存根 192 // TODO Auto-generated method stub 193 //生成表Examine,列簇family1 194 //HBaseUtils.createTable("Examine", "family1"); 195 //建立任务Job 196 Job job=Job.getInstance(getConf()); 197 job.setJarByClass(MrToHBaseExample.class); 198 //初始化TableMapper,把Examine表中的数据与Assess中的数据发送到Mapper 199 Scan scan1=new Scan(); 200 scan1.addFamily(Bytes.toBytes("family1")); 201 scan1.setAttribute(Scan.SCAN_ATTRIBUTES_TABLE_NAME, "Examine".getBytes()); 202 Scan scan2=new Scan(); 203 scan2.addFamily(Bytes.toBytes("family1")); 204 scan2.setAttribute(Scan.SCAN_ATTRIBUTES_TABLE_NAME, "Assess".getBytes()); 205 List list=new ArrayList (); 206 list.add(scan1); 207 list.add(scan2); 208 TableMapReduceUtil.initTableMapperJob(list,MyMapper.class,ExamineDetailKey.class,ExamineDetailValue.class,job); 209 //注册Key/Value类型为Text 210 job.setOutputKeyClass(NullWritable.class); 211 job.setOutputValueClass(Text.class); 212 //TableMapReduceUtil.initTableMapperJob方法中已经绑定Mapper输出类型,下面方法可忽略 213 //job.setMapOutputKeyClass(ExamineDetailKey.class); 214 //job.setMapOutputValueClass(ExamineDetailValue.class); 215 //注册Mapper及Reducer处理类 216 job.setMapperClass(MyMapper.class); 217 job.setReducerClass(MyReducer.class); 218 //设置SortComparator和GroupComarartor 219 job.setSortComparatorClass(ExamineDetailSortComparator.class); 220 job.setGroupingComparatorClass(ExamineDetailGroupComparator.class); 221 //输入输出数据格式化类型为TextInputFormat/TableOutputFormat 222 job.setInputFormatClass(MultiTableInputFormat.class); 223 job.setOutputFormatClass(TextOutputFormat.class); 224 //伪分布式情况下不设置时默认为1 225 //job.setNumReduceTasks(1); 226 //注册自定义Partitional类 227 //job.setPartitionerClass(MyPatitional.class); 228 //获取命令参数 229 String[] args=new GenericOptionsParser(getConf(),arg0).getRemainingArgs(); 230 FileOutputFormat.setOutputPath(job,new Path(args[0])); 231 boolean status=job.waitForCompletion(true); 232 if(status) 233 return 0; 234 else 235 return 1; 236 } 237 238 public static void main(String[] args) throws Exception{ 239 Configuration conf=new Configuration(); 240 ToolRunner.run(new MrToHBaseExample(), args); 241 } 242 }
运行结果
返回目录
本章小结
本文的主要目的是介绍 HBase 的运行原理,介绍 HBase 的常用 Java API 开发实例,讲解 HBase 与 MR 之间的关系。在现今大数据年代,了解NoSQL的开发可以说是技术人员入门的必修课程,希望本文对各位的工作学习有所帮助。
由于本人时间紧迫,文章中有所缺漏的地方敬请点评。
对 JAVA 开发有兴趣的朋友欢迎加入QQ群:174850571 共同探讨!
对 .NET 开发有兴趣的朋友欢迎加入QQ群:230564952 共同探讨 !
Hadoop 综合揭秘
HBase 的原理与应用
MapReduce 基础编程(介绍 Combine、Partitioner、WritableComparable、WritableComparator 使用方式)
作者:风尘浪子
https://www.cnblogs.com/leslies2/p/9530378.html
原创作品,转载时请注明作者及出处