Java整合Hbase

序言

在HBase架构中的数据层面:

  1. HMaster负责管理类的操作(例如表、命令空间的创建删除),
  2. HRegionServer负责表的读写(即CRUD)

对应地在客户端API中有两个类:

  1. Admin类对象职能类似于HMaster
  2. HTable的职能类似于HRegionServer------应该放置到HTablePool中,不用从connection中获取。提升效率

通常这两个类的对象在一个应用程序中只创建一次,并且它们是由Connection对象获取的,Connection对象需要通过配置文件加载。所以以上对象一般为静态的,并且获取创建的过程在静态块中执行。

Hbase的所有操作都是追加插入操作(故此hbase有历史版本的概念存在)。它可以往数据里面insert,也可以update一些数据,但update的实际上也是insert,只是插入一个新的时间戳的一行。Delete数据,也是insert,只是insert一行带有delete标记的一行。

spring-boot-starter-hbase

使用springboot的封装工具来使用Hbase,觉得没啥说的。

org.apache.hbase:hbase-client

使用原生的客户端连接Hbase。

HBaseConfiguration

包名 : org.apache.hadoop.hbase.HBaseConfiguration
作用:对HBase进行配置。
使用方法演示样例:

HBaseConfiguration hconfig = new HBaseConfiguration();
hconfig.set("hbase.zookeeper.property.clientPort","2181");

HBaseAdmin

HBaseAdmin类是Admin的子类,通常通过Admin实例即可完成对Hbase管理类的操作

包名 : org.apache.hadoop.hbase.client.HBaseAdmin
作用:提供了一个接口来管理HBase数据库的表信息


它提供的方法包括创建表,删除表列出表项。使表有效或无效,以及加入或删除表列族成员等。
使用方法演示样例:

HBaseAdmin admin = new HBaseAdmin(config);
admin.disableTable("tablename")




//获得所有命名空间
NamespaceDescriptor[] listNamespaceDescriptors()
//指定命名空间名获取ns
NamespaceDescriptor getNamespaceDescriptor(final String name)
//删除命名空间
deleteNamespace(final String name)
//列出指定命名空间下的所有表
TableName[] listTableNamesByNamespace(final String name)
 
//列出所有表
HTableDescriptor[] listTables() throws IOException

创建表的同时可是初始化Region这样子在大批量插入的时候就不会触发大面积的Region的split,以提升插入效率.同时也可以避免后期的分裂时间cuiyaonan2000@163.com

//只根据表描述符来创建表
void createTable(HTableDescriptor desc)

//numRegions是要初始化的Region数量 ,startKey 是开始的key,endkey是结束的key 然后hbase自动拆分
void createTable(HTableDescriptor desc, byte[] startKey,byte[] endKey, int numRegions)


//这个是手动录入每一个region的分区.
//比如{Bytes.toBytes("A") ,Bytes.toBytes("G"),Bytes.toBytes("Z"")} 会生成4个region.即keynum+1个region
//且区段是
//      空 到 A
//      A  到 G
//      G  到 Z
//      Z  到 空
void createTable(HTableDescriptor desc, byte[][] splitKeys)
 

//这个是后台异步执行,跟completefuturetask一样哦
void createTableAsync(HTableDescriptor desc, byte[][] splitKeys)

Region有关方法

合并region

//合并两个Region
void mergeRegions(final byte[] nameOfRegionA, final byte[] nameOfRegionB,
      final boolean forcible) throws IOException

最后一个参数指定是否强制合并,默认为false,默认会合并两个相邻的Region,若设置为True可合并两个不相邻的Region

关于Region名可以在web端查看(在table详情页中),最后两个句号之间的数据即为Region名

移动region

void move(final byte[] encodedRegionName, final byte[] destServerName)
      throws IOException

第二个参数指定了目的地服务器名(这个在登录页就可以看到),servername可通过web端查看

切分Region---自动的

void splitRegion(final byte[] regionName) throws IOException
//设置切分点行键
void split(final TableName tableName, final byte[] splitPoint)
    throws IOException

同样还可以切表---自动的

void split(final TableName tableName) throws IOException
 
void split(final TableName tableName, final byte[] splitPoint)
    throws IOException

负载均衡--即自动切分所有的region

//传入true开启负载均衡
boolean balancer(boolean force) throws IOException
//进行负载均衡
 boolean balancer() throws IOException

表和Region的合并(compact)

这里的合并指的是minor compact和major compact,是底层HFile层面的合并

void compact(final TableName tableName) throws IOException
void compact(final TableName tableName, final byte[] columnFamily)
 
void compactRegion(final byte[] regionName) throws IOException
void compactRegion(final byte[] regionName, final byte[] columnFamily) throws IOException
 
void majorCompact(TableName tableName) throws IOException
void majorCompactRegion(final byte[] regionName) throws IOException
 
//...

HTableDescriptor

包名: org.apache.hadoop.hbase.HTableDescriptor
作用:包括了表的名字及其相应表的列族。
使用方法演示样例:

HTableDescriptor htd = new HTableDescriptor(table);
htd.addFamily(new HcolumnDescriptor("family"));

HColumnDescriptor

包名: org.apache.hadoop.hbase.HColumnDescriptor
作用:维护着关于列族的信息,比如版本。压缩设置等。


它通常在创建表或者为表加入列族的时候使用。
列族被创建后不能直接改动。仅仅能通过删除,然后又一次创建的方式。


列族被删除的时候,列族里面的数据也会同一时候被删除。
使用方法演示样例:

HTableDescriptor htd = new HTableDescriptor(tablename);
HColumnDescriptor col = new HColumnDescriptor("content:");
htd.addFamily(col);

HTable

包名: org.apache.hadoop.hbase.client.HTable
作用:能够用来和HBase表直接通信。此方法对于更新操作来说是非线程安全的。
使用方法演示样例:

HTable table = new HTable(conf, Bytes.toBytes(tablename));
ResultScanner scanner = table.getScanner(family);

HTablePool------------用于替换HTable,提升资源效率,相当于表的连接池cuiyaonan2000

包名: org.apache.hadoop.hbase.client.HTablePool
作用:能够解决HTable存在的线程不安全问题同一时候通过维护固定数量的HTable对象,能够在程序执行期间复用这些HTable资源对象
说明:
1. HTablePool能够自己主动创建HTable对象,并且对客户端来说使用上是全然透明的。能够避免多线程间数据并发改动问题
2. HTablePool中的HTable对象之间是公用Configuration连接的,能够能够降低网络开销。

HTablePool的使用非常easy:每次进行操作前。通过HTablePool的getTable方法取得一个HTable对象,然后进行put/get/scan/delete等操作,最后通过HTablePool的putTable方法将HTable对象放回到HTablePool中。

/**
 * A simple pool of HTable instances.
 * 
 * Each HTablePool acts as a pool for all tables. To use, instantiate an
 * HTablePool and use {@link #getTable(String)} to get an HTable from the pool.
 *
 * This method is not needed anymore, clients should call HTableInterface.close() 
 * rather than returning the tables to the pool
 *
 * Once you are done with it, close your instance of {@link HTableInterface}
 * by calling {@link HTableInterface#close()} rather than returning the tables
 * to the pool with (deprecated) {@link #putTable(HTableInterface)}.
 *
 * <p>
 * A pool can be created with a <i>maxSize</i> which defines the most HTable
 * references that will ever be retained for each table. Otherwise the default
 * is {@link Integer#MAX_VALUE}.
 *
 * <p>
 * Pool will manage its own connections to the cluster. See
 * {@link HConnectionManager}.
 * @deprecated as of 0.98.1. See {@link HConnection#getTable(String)}.
 */
@InterfaceAudience.Private
@Deprecated
public class HTablePool implements Closeable {
}

Put

包名: org.apache.hadoop.hbase.client.Put
作用:用来对单个行执行加入操作。
使用方法演示样例:

//Put类的构造函数
Put(byte[] row)
Put(byte[], long ts)



//put类提供的方法
//每次add都会添加特定的列,若再添加时间戳即形成了一个单元格
//若在addColumn时不指定时间戳,则会使用来自构造函数的时间戳;若构造时也没有声明,则时间戳将由HRegionServer来设定
Put addColumn(byte [] family, byte [] qualifier, byte [] value)
Put addColumn(byte [] family, byte [] qualifier, long ts, byte [] value)
Put add(Cell kv)


//用例
HTable table = (HTable) conn.getTable(TableName.valueOf(tableName));
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(family), Bytes.toBytes(qualifier), value);
table.put(put);
table.close();

table一次提交多个put

table提供的方法如下:

void put(List<Put> puts) throws IOException

当Put列表中有部分失败,例如部分Put指定了不存在的列族,由于客户端并不知道HBase中表的结构,所以对列族的检查会在服务器端完成。正常Put的数据会添加成功,并且客户端不会抛出异常失败的Put实例会被保存在本地写缓冲区内,下一次刷写时会重试。客户端会检查Put实例内容是否为空或是否制定了列在这种情况下客户端会抛出异常

当提交了Put列表时,用户无法控制服务器端执行Put实例的顺序。若要尽量保证写入的顺序,可以减少每一批Put的数量并显式地刷写。

table的原子性的put方法

调用table的checkAndPut会对指定单元格数据与参数value对比,若相同则视为检查通过,并执行指定Put。

使用场景为:当读取数据的同时还需要处理数据,想把一个处理好的数据写回到HBase并确保没有其他客户端做了相同的事------------相当于先去数据库中查询下该数据是否做了修改,如果没有做修改则执行put。cuiyaonan2000@163.com

boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
    byte[] value, Put put) throws IOException

客户端的写缓冲区(批量puts 失败的时候,失败的会到这里来

每一次put操作实际上就是一次rpc操作,这样的工作原理只适合小数据量的操作。减少rpc调用的关键是限制往返时间(LAN中1次rpc大约花1ms)和控制消息大小。所以API配备了一个客户端的写缓冲区,缓冲区负责收集put操作,然后调用rpc操作一次性将put发送至服务器。-------------目前2.0的版本及以上都去掉了如下的方法cuiyaonan2000@163.com

//默认情况下客户端缓冲区是禁用的,可以通过将自动刷写设置为false来激活缓冲区
table.setAutoFlush(false)

//当需要强制将数据刷写到服务器上时调用
table.flushCommits()

//通常强制刷写是不必要的,一旦超出了缓冲区指定的大小限制,客户端会隐式地调用刷写方法,可以通过以下命令设置缓冲区大小
table.setWriteBufferSize(2097152)

Get

包名: org.apache.hadoop.hbase.client.Get
作用:用来获取单个行的相关信息。

使用方法演示样例:

//get的构造方法
Get(byte[] row)



//get对象提供的方法
//指定列
Get addColumn(byte [] family, byte [] qualifier)
//指定列族
Get addFamily(byte [] family)
//指定时间戳及更旧的
Get setTimeStamp(long timestamp)
//Get请求默认返回版本数为1,不带参数的setMaxVersions设置最大版本号位int最大值
Get Get setMaxVersions()
Get setMaxVersions(int maxVersions) throws IOException



//用例
Get get = new Get(Bytes.toBytes(rowKey));
HTable table = (HTable) conn.getTable(TableName.valueOf(tableName));
//这里的allversion表示是否不需要包历史版本也查询出来,否则只查询最新版本
Result result = table.get(allVersion ? get.readAllVersions() : get);
return result.listCells();

table一次提交多个Get

table提供的方法如下:

Result[] get(List<Get> gets) throws IOException

注意此时get()方法要么返回一个与发送Get列表大小一致的Result数组要么抛出异常

有错误的Get时与put列表不同当有一个Get错误时会导致整个get()操作终止。关于批量操作中的局部错误,可以使用batch()方法更精细地处理

table检查get中的数据是否存在

exist()方法会引发regionserver服务器端的查询数据操作,通过这种方式而不是get()后检查可以避免网络数据传输的开

boolean exist(Get get) throws IOException

table获取get指定的前一条记录,或者查找最后一条记录

此方法中的行键要么与指定的一致,要么是指定行键之前的一行。

通常可以使用此方法在不知道行数的情况下获取表中最后一行的数据,调用getRowOrBefore()时传入像row-999999999这样的row参数,来获取表中最后一行row-n

Result getRowOrBefore(byte[] row, byte[] family) throws IOException

Delete

顾名思义:用于删除hbase中的记录

另外针对delete对象的方法cuiyaonan2000@163.com:

  1. 不指定时间戳服务器会强制检索服务器端最近的时间戳这比指定时间戳的删除要慢
  2. 若尝试删除未设置时间戳的单元格什么都不会发生
//delete的构造函数
Delete(byte [] row)
Delete(byte [] row, long timestamp)



//delete对象的方法
//不指定时间戳会删除整个列族;指定会删除改时间戳及更旧版本
Delete addFamily(final byte [] family)
Delete addFamily(final byte [] family, final long timestamp)
 
//不指定时间戳会删除指定列的所有版本;指定会删除该时间戳及比这个时间戳更旧的版本
Delete addColumns(final byte [] family, final byte [] qualifier)
Delete addColumns(final byte [] family, final byte [] qualifier, final long timestamp)
 
//不指定时间戳只删除指定列的最新版本,保留旧版本;指定会只指定列的指定版本
Delete addColumn(final byte [] family, final byte [] qualifier)
Delete addColumn(byte [] family, byte [] qualifier, long timestamp)
 
Delete setTimestamp(long timestamp



//示例
HTable table = (HTable) conn.getTable(TableName.valueOf(tableName));
		Delete delete = new Delete(Bytes.toBytes(rowKey));
		table.delete(delete);

table一次提交多个delete

用户不能设置删除操作在服务器端的执行顺序API会重新排列它们并将同一regionserver的操作集中到一个rpc中以提升性能

有错误的Delete时:与put类似,不能终止整个delete(),列信息正确的则被成功删除,错误删除的处理跟put的操作类似

void delete(List<Delete> deletes) throws IOException

table的原子性的delete方法

当指定单元格的值与参数value相同时,才会执行指定的Delete,------即乐观锁的螺旋操作cuiyaonan2000@163.com

boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
    byte[] value, Delete delete) throws IOException

Result

包名: org.apache.hadoop.hbase.client.Result
作用:存储Get或者Scan操作后获取表的单行值。

使用此类提供的方法能够直接获取值或者各种Map结构( key-value对)。

//Result提供的方法如下所示
//获取指定单元格的值
byte[] getValue(byte [] family, byte [] qualifier)
//获取第一列数据的最新版本
byte [] value()
//获取行键
byte [] getRow()
//获取单元格数量
int size()
//获取单元格列表
List<Cell> listCells()


//示例
//获取一行数据中的所有单元格并打印基本信息
	 public static void get_row_cells(String tName, String rk){
		Get get = new Get(Bytes.toBytes(rk));
		try {
			TableName tbn = TableName.valueOf(tName);
			table =  conn.getTable(tbn);
			Result res = table.get(get);
			List<Cell> cl = res.listCells();
			for(Cell c : cl){
				//调用Cell工具类的cloneXXX方法获取信息,注意Cell.getXXX()已过时
				String row = Bytes.toString(CellUtil.cloneRow(c));
				String cf = Bytes.toString(CellUtil.cloneFamily(c));
				String qua = Bytes.toString(CellUtil.cloneQualifier(c));
				String val = Bytes.toString(CellUtil.cloneValue(c));
				System.out.println("row:"+row+",cf:"+cf+",qua:"+qua+",val:"+val);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

table的batch批量操作

//参数说明
//Row类是Put、Get、Delete的父类。参数actions的列表中可以存放3种同类型的子类,将不同的操作融为一个rpc调用
//hmaster关于效率的解决也是有着自己处理方式


batch(final List<? extends Row> actions, final Object[] results)

注意点:

  • actions和results大小相同,一个操作返回一个result
  • 不可以针对同一行的Put和Delete操作放在同一个batch()请求中

  • 调用batch()时Put实例不会写入客户端的写缓冲区中,batch()请求是同步的

Scan

扫描与get()方法十分类似,可以看成是多行的get()结果(通过指定扫描器Scan,即指定扫描规则)到客户端的一个结果扫描器(ResultScanner)中

因为HBase中数据(HFile)是按照列族存储的,如果扫描不读取某个列族,那么整个列族文件就不会比读取,这便是列式存储的优势

//SCAN构造函数
//扫描全表
Scan()
//指定开始扫描的行键,至表未
Scan(byte[] startRow)
//指定扫描开始和结束行键,包前不包后
Scan(byte[] startRow, byte[] stopRow)



//scan对象的方法
Scan addColumn(byte [] family, byte [] qualifier)
Scan addFamily(byte [] family)
//设置过滤器
Scan setFilter(Filter filter)

ResultScanner

包名: org.apache.hadoop.hbase.client.ResultScanner
作用:存储Get或者Scan操作后获取表的单行值。---这里理解为scan的返回结果就好了

通过Table类实例调用getScanner()获取结果扫描器

//table返回resultscanner的结果
ResultScanner getScanner(Scan scan) throws IOException

//同样是table方法
//以下方法隐式地创建了Scan实例---同理也是table对象的方法
ResultScanner getScanner(byte[] family) throws IOException
ResultScanner getScanner(byte[] family, byte[] qualifier) throws IOException


//如下的是resultscanner对象的方法
Result next() throws IOException
//返回nbRows行的Result
Result [] next(int nbRows) throws IOException
void close()

关于scan的缓存设置

在旧版本中默认情况下ResultScanner每一次调用next()都会为每行数据生成一个单独的rpc调用(这效率岂不是很低么),next(int nbRows)中也不过是循环调用next()方法。API提供了扫描器缓存和批量处理来避免生成过多的rpc,总的来说扫描器缓存是面向行的批量处理是面向列的

扫描器缓存----针对行

可以在两个层面设置扫描器缓存打开它:(因为scan面向的是行,批处理面向的是列,所以如下的缓存都是设置的行数

  1. 在表层面设置会所该表的所有Scan实例生效,------------即table层设置
  2. 也可以在扫描层面设置,只会影响当前的Scan实例。且扫描器层面的优先级更高-------------即scan对象中设置
//Table层面,参数为一次rpc扫描的行数
void setScannerCaching(int scannerCaching)
 
//Scan层面
void setCaching(int caching)

不过在较新的版本中,扫描器缓存默认是打开的,且caching为2^31-1

配置文件相关的配置如下:

<property>
    <name>hbase.client.scanner.caching</name>
    <value>2147483647</value>
</property>

扫描器的批量设置----针对每一行的列

在扫描器层面设置

void setBatch(int batch)

因为每一行中的列指的是单元格,可以把批量看成是单元格层面的处理;若设置的batch大于总列数,则每次取回所有列。另外每次扫描完成后会发送1个rpc确认完成

比如有一个10行20例的表,即一共200列(单元格):--------------------此为如下表格的基础条件,每次的数据获取都需要增加一次rpc,比如一行批量获取10个,则一次rpc获取不完,需要第二次rpc才能将一行数据全都获取完

cachingbatchResult个数rpc次数
11200201
20012002
2102011
5100103
520103
1010203

在Hadoop生态系统中,Hive是一个用于数据仓库和数据分析的开源工具,而HBase是一个开源的、分布式的、非关系型数据库。在某些情况下,因为版本不兼容或者依赖关系错误,可能会发生Hive整合HBase时出现IncompatibleClassChangeError(不兼容的类变更错误)。 IncompatibleClassChangeError是Java虚拟机(JVM)在运行期间抛出的错误,它表示在编译时使用的类与运行时使用的类发生了不兼容的变化。当Hive试图整合HBase时,如果Hive代码使用了HBase中的类,但实际运行时使用的HBase库与Hive编译时使用的库不兼容,就会出现这个错误。 解决这个问题的步骤如下: 1. 检查HBase和Hive的版本是否兼容。在整合Hive和HBase之前,确保使用的HBase版本与Hive版本兼容,并遵循它们之间的兼容性要求。 2. 检查依赖关系。在使用Hive整合HBase时,确保在Hive配置文件(hive-site.xml)中正确地设置了HBase相关的依赖。这包括指定HBase的主机名、端口号和表名等。 3. 检查类路径。确保在Hive运行期间正确配置了HBase的类路径,以便可以找到所需的HBase类。这可以通过检查Hive和HBase的环境变量设置或者Hive的启动脚本来完成。 4. 更新Hive和HBase的库。如果以上步骤都没有解决问题,可能需要升级Hive和HBase的库版本,以确保它们之间的兼容性。 综上所述,Hive整合HBase时出现IncompatibleClassChangeError错误可能是由于版本不兼容或者依赖关系错误导致的。通过检查版本兼容性、依赖关系、类路径和库更新等措施,可以解决这个问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cuiyaonan2000

给包烟抽吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值