一、Hbase介绍
二、Hbase的Region
三、Hbase的读写流程
四、Hbase主键设计
五、Hbase预分区
六、Hbase Java API
Hbase是分布式、面向列的开源数据库(其实准确的说是面向列族)。HDFS为Hbase提供可靠的底层数据存储服务,MapReduce为Hbase提供高性能的计算能力,Zookeeper为Hbase提供稳定服务和Failover机制,因此我们说Hbase是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。
2.1、海量存储
Hbase适合存储PB级别的海量数据,在PB级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正式因为Hbase良好的扩展性,才为海量数据的存储提供了便利。
2.2、列式存储
这里的列式存储其实说的是列族存储,Hbase是根据列族来存储数据的。列族下面可以有非常多的列,列族在创建表的时候就必须指定。为了加深对Hbase列族的理解,下面是一个简单的关系型数据库的表和Hbase数据库的表:
RDBMS的表:
Hbase的表:
下图是针对Hbase和关系型数据库的基本的一个比较:
2.3、极易扩展
Hbase的扩展性主要体现在两个方面,一个是基于上层处理能力(RegionServer)的扩展,一个是基于存储的扩展(HDFS)。
通过横向添加RegionSever的机器,进行水平扩展,提升Hbase上层的处理能力,提升Hbsae服务更多Region的能力。
2.4、稀疏
稀疏主要是针对Hbase列的灵活性,在列族中,你可以指定任意多的列,在列数据为null的情况下,是不会占用存储空间的。
在我学习Hbase的时候有几个概念需要重点理解一下,列出4个基础概念如下图所示:
3.1、Column Family的概念
Column Family又叫列族,Hbase通过列族划分数据的存储,列族下面可以包含任意多的列,实现灵活的数据存取。
Hbase表的创建的时候就必须指定列族。就像关系型数据库创建的时候必须指定具体的列是一样的。
Hbase的列族不是越多越好,官方推荐的是列族最好小于或者等于3。我们使用的场景一般是1个列族。
3.2、Rowkey的概念
Rowkey的概念和mysql中的主键是完全一样的,Hbase使用Rowkey来唯一的区分某一行的数据。
由于Hbase只支持3中查询方式:
因此,Rowkey对Hbase的性能影响非常大,Rowkey的设计就显得尤为的重要。设计的时候要兼顾基于Rowkey的单行查询也要键入Rowkey的范围扫描。
3.3、Region的概念
Region的概念和关系型数据库的分区或者分片差不多。
Hbase会将一个大表的数据基于Rowkey的不同范围分配到不通的Region中,每个Region负责一定范围的数据访问和存储。这样即使是一张巨大的表,由于被切割到不通的region,访问起来的时延也很低。
3.4、TimeStamp的概念
TimeStamp对Hbase来说至关重要,因为它是实现Hbase多版本的关键。在Hbase中使用不同的timestame来标识相同rowkey行对应的不同版本的数据。
在写入数据的时候,如果用户没有指定对应的timestamp,Hbase会自动添加一个timestamp,timestamp和服务器时间保持一致。
在Hbase中,相同rowkey的数据按照timestamp倒序排列。默认查询的是最新的版本,用户可同指定timestamp的值来读取旧版本的数据。
Hbase的架构图如下图所示:
从图中可以看出Hbase是由Client、Zookeeper、Master、HRegionServer、HDFS等几个组建组成,下面来介绍一下几个组建的相关功能:
4.1、Client
Client包含了访问Hbase的接口,另外Client还维护了对应的cache来加速Hbase的访问,比如cache的.META.元数据的信息。
4.2、Zookeeper
Hbase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口以及集群配置的维护等工作。具体工作如下:
通过Zoopkeeper来保证集群中只有1个master在运行,如果master异常,会通过竞争机制产生新的master提供服务
通过Zoopkeeper来监控RegionServer的状态,当RegionSevrer有异常的时候,通过回调的形式通知Master RegionServer上下限的信息
通过Zoopkeeper存储元数据的统一入口地址
4.3、Hmaster
master节点的主要职责如下:
为RegionServer分配Region
维护整个集群的负载均衡
维护集群的元数据信息
发现失效的Region,并将失效的Region分配到正常的RegionServer上
当RegionSever失效的时候,协调对应Hlog的拆分
4.4、HregionServer
HregionServer直接对接用户的读写请求,是真正的“干活”的节点。它的功能概括如下:
管理master为其分配的Region
处理来自客户端的读写请求
负责和底层HDFS的交互,存储数据到HDFS
负责Region变大以后的拆分
负责Storefile的合并工作
4.5、HDFS
HDFS为Hbase提供最终的底层数据存储服务,同时为Hbase提供高可用(Hlog存储在HDFS)的支持,具体功能概括如下:
提供元数据和表数据的底层分布式存储服务
数据多副本,保证的高可靠和高可用性
Hbase是一个通过廉价PC机器集群来存储海量数据的分布式数据库解决方案。它比较适合的场景概括如下:
有几个典型的场景特别适合使用Hbase来存储:
海量订单流水数据(长久保存)
交易记录
数据库历史数据
前面已经介绍了Region类似于数据库的分片和分区的概念,每个Region负责一小部分Rowkey范围的数据的读写和维护,Region包含了对应的起始行到结束行的所有信息。master将对应的region分配给不同的RergionServer,由RegionSever来提供Region的读写服务和相关的管理工作。这部分主要介绍Region实例以及Rgeion的寻找路径:
上图模拟了一个Hbase的表是如何拆分成region,以及分配到不同的RegionServer中去。上面是1个Userinfo表,里面有7条记录,其中rowkey为0001到0002的记录被分配到了Region1上,Rowkey为0003到0004的记录被分配到了Region2上,而rowkey为0005、0006和0007的记录则被分配到了Region3上。region1和region2被master分配给了RegionServer1(RS1),Region3被master配分给了RegionServer2(RS2)
备注:这里只是为了更容易的说明拆分的规则,其实真实的场景并不会几条记录拆分到不通的Region上,而是到一定的数据量才会拆分,具体的在Region的拆分那部分再具体的介绍。
既然读写都在RegionServer上发生,每个RegionSever为一定数量的region服务,那么client要对某一行数据做读写的时候如何能知道具体要去访问哪个RegionServer呢?那就是接下来我们要讨论的问题
2.1、老的Region寻址方式
在Hbase 0.96版本以前,Hbase有两个特殊的表,分别是-ROOT-表和.META.表,其中-ROOT-的位置存储在ZooKeeper中,-ROOT-本身存储了 .META. Table的RegionInfo信息,并且-ROOT-不会分裂,只有一个region。而.META.表可以被切分成多个region。读取的流程如下图所示:
第1步:client请求ZK获得-ROOT-所在的RegionServer地址
第2步:client请求-ROOT-所在的RS地址,获取.META.表的地址,client会将-ROOT-的相关信息cache下来,以便下一次快速访问
第3步:client请求 .META.表的RS地址,获取访问数据所在RegionServer的地址,client会将.META.的相关信息cache下来,以便下一次快速访问
第4步:client请求访问数据所在RegionServer的地址,获取对应的数据
从上面的路径我们可以看出,用户需要3次请求才能直到用户Table真正的位置,这在一定程序带来了性能的下降。在0.96之前使用3层设计的主要原因是考虑到元数据可能需要很大。但是真正集群运行,元数据的大小其实很容易计算出来。在BigTable的论文中,每行METADATA数据存储大小为1KB左右,如果按照一个Region为128M的计算,3层设计可以支持的Region个数为2^34个,采用2层设计可以支持2^17(131072)。那么2层设计的情况下一个 集群可以存储4P的数据。这仅仅是一个Region只有128M的情况下。如果是10G呢? 因此,通过计算,其实2层设计就可以满足集群的需求。因此在0.96版本以后就去掉了-ROOT-表了。
2.2、新的Region寻址方式
如上面的计算,2层结构其实完全能满足业务的需求,因此0.96版本以后将-ROOT-表去掉了。如下图所示:
访问路径变成了3步:
第1步:Client请求ZK获取.META.所在的RegionServer的地址。
第2步:Client请求.META.所在的RegionServer获取访问数据所在的RegionServer地址,client会将.META.的相关信息cache下来,以便下一次快速访问。
第3步:Client请求数据所在的RegionServer,获取所需要的数据。
总结去掉-ROOT-的原因有如下2点:
其一:提高性能
其二:2层结构已经足以满足集群的需求
这里还有一个问题需要说明,那就是Client会缓存.META.的数据,用来加快访问,既然有缓存,那它什么时候更新?如果.META.更新了,比如Region1不在RerverServer2上了,被转移到了RerverServer3上。client的缓存没有更新会有什么情况?
其实,Client的元数据缓存不更新,当.META.的数据发生更新。如上面的例子,由于Region1的位置发生了变化,Client再次根据缓存去访问的时候,会出现错误,当出现异常达到重试次数后就会去.META.所在的RegionServer获取最新的数据,如果.META.所在的RegionServer也变了,Client就会去ZK上获取.META.所在的RegionServer的最新地址。
Hbase的写逻辑涉及到写内存、写log、刷盘等操作。
Hbase的写入流程如下图所示:
从上图可以看出氛围3步骤:
第1步:Client获取数据写入的Region所在的RegionServer
第2步:请求写Hlog
第3步:请求写MemStore
只有当写Hlog和写MemStore都成功了才算请求写入完成。MemStore后续会逐渐刷到HDFS中。
备注:Hlog存储在HDFS,当RegionServer出现异常,需要使用Hlog来恢复数据。
步骤1:client访问Zookeeper,查找-ROOT-表,获取.META.表信息。
步骤2:从.META.表查找,获取存放目标数据的HRegion信息,从而找到对应的HRegionServer。
步骤3:通过HRegionServer获取需要查找的数据。
步骤4:HRegionserver的内存分为MemStore和BlockCache两部分,MemStore主要用于写数据,BlockCache主要用于读数据。读请求先到MemStore中查数据,查不到就到BlockCache中查,再查不到就会到StoreFile上读,并把读的结果放入BlockCache。
为了提高Hbase的写入性能,当写请求写入MemStore后,不会立即刷盘。而是会等到一定的时候进行刷盘的操作。具体是哪些场景会触发刷盘的操作呢?总结成如下的几个场景:
3.1、全局内存控制
这个全局的参数是控制内存整体的使用情况,当所有memstore占整个heap的最大比例的时候,会触发刷盘的操作。这个参数是hbase.regionserver.global.memstore.upperLimit,默认为整个heap内存的40%。但这并不意味着全局内存触发的刷盘操作会将所有的MemStore都进行输盘,而是通过另外一个参数hbase.regionserver.global.memstore.lowerLimit来控制,默认是整个heap内存的35%。当flush到所有memstore占整个heap内存的比率为35%的时候,就停止刷盘。这么做主要是为了减少刷盘对业务带来的影响,实现平滑系统负载的目的。
3.2、MemStore达到上限
当MemStore的大小达到hbase.hregion.memstore.flush.size大小的时候会触发刷盘,默认128M大小
3.3、RegionServer的Hlog数量达到上限
前面说到Hlog为了保证Hbase数据的一致性,那么如果Hlog太多的话,会导致故障恢复的时间太长,因此Hbase会对Hlog的最大个数做限制。当达到Hlog的最大个数的时候,会强制刷盘。这个参数是hase.regionserver.max.logs,默认是32个。
3.4、手工触发
可以通过hbase shell或者java api手工触发flush的操作。
3.5、关闭RegionServer触发
在正常关闭RegionServer会触发刷盘的操作,全部数据刷盘后就不需要再使用Hlog恢复数据。
3.6、Region使用HLOG恢复完数据后触发
当RegionServer出现故障的时候,其上面的Region会迁移到其他正常的RegionServer上,在恢复完Region的数据后,会触发刷盘,当刷盘完成后才会提供给业务访问。
4.1、Hlog简介
Hlog是Hbase实现WAL(Write ahead log)方式产生的日志信息,内部是一个简单的顺序日志。每个RegionServer对应1个Hlog(备注:1.x版本的可以开启MultiWAL功能,允许多个Hlog),所有对于该RegionServer的写入都被记录到Hlog中。Hlog实现的功能就是我们前面讲到的保证数据安全。当RegionServer出现问题的时候,能跟进Hlog来做数据恢复。此外为了保证恢复的效率,Hbase会限制最大保存的Hlog数量,如果达到Hlog的最大个数(hase.regionserver.max.logs参数控制)的时候,就会触发强制刷盘操作。对于已经刷盘的数据,其对应的Hlog会有一个过期的概念,Hlog过期后,会被监控线程移动到 .oldlogs,然后会被自动删除掉。
Hbase是如何判断Hlog过期的呢?要找到这个答案,我们就必须了解Hlog的详细结构。
4.2、Hlog结构
下图是Hlog的详细结构(图片来源 http://hbasefly.com/ ):
从上图我们可以看出都个Region共享一个Hlog文件,单个Region在Hlog中是按照时间顺序存储的,但是多个Region可能并不是完全按照时间顺序。
每个Hlog最小单元由Hlogkey和WALEdit两部分组成。Hlogky由sequenceid、timestamp、cluster ids、regionname以及tablename等组成,WALEdit是由一系列的KeyValue组成,对一行上所有列(即所有KeyValue)的更新操作,都包含在同一个WALEdit对象中,这主要是为了实现写入一行多个列时的原子性。
注意,图中有个sequenceid。sequenceid是一个store级别的自增序列号,这个非常重要,region的数据恢复和Hlog过期清除都要依赖它。下面就来简单描述一下sequenceid的相关逻辑。
4.3、Hlog的生命周期
Hlog从产生到最后删除需要经历如下几个过程:
产生
所有涉及到数据的变更都会先写Hlog,除非是你关闭了Hlog
滚动
Hlog的大小通过参数hbase.regionserver.logroll.period控制,默认是1个小时,时间达到hbase.regionserver.logroll.period 设置的时间,Hbase会创建一个新的Hlog文件。这就实现了Hlog滚动的目的。Hbase通过hbase.regionserver.maxlogs参数控制Hlog的个数。滚动的目的,为了控制单个Hlog文件过大的情况,方便后续的过期和删除。
过期
前面我们有讲到sequenceid这个东东,Hlog的过期依赖于对sequenceid的判断。Hbase会将Hlog的sequenceid和Hfile最大的sequenceid(刷新到的最新位置)进行比较,如果该Hlog文件中的sequenceid比刷新的最新位置的sequenceid都要小,那么这个Hlog就过期了,过期了以后,对应Hlog会被移动到.oldlogs目录。
这里有个问题,为什么要将过期的Hlog移动到.oldlogs目录,而不是直接删除呢?
答案是因为Hbase还有一个主从同步的功能,这个依赖Hlog来同步Hbase的变更,有一种情况不能删除Hlog,那就是Hlog虽然过期,但是对应的Hlog并没有同步完成,因此比较好的做好是移动到别的目录。再增加对应的检查和保留时间。
HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度可以对HBase中的数据进行快速定位。
HBase中rowkey可以唯一标识一行记录,在HBase查询的时候,有以下几种方式:
1>通过get方式,指定rowkey获取唯一一条记录
2>通过scan方式,设置startRow和stopRow参数进行范围匹配
3>全表扫描,即直接扫描整张表中所有行记录
rowkey是一个二进制的,可以是任意字符串,最大长度 64kb ,实际应用中一般为10-100bytes,以byte[] 形式保存,一般设计成定长。
建议越短越好,不要超过16个字节,原因如下:
1>数据的持久化文件HFile中是按照KeyValue存储的,如果rowkey过长,比如超过100字节,1000w行数据,光rowkey就要占用100*1000w=10亿个字节,将近1G数据,这样会极大影响HFile的存储效率;
2>MemStore将缓存部分数据到内存,如果rowkey字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。
3>目前操作系统都是64位系统,内存8字节对齐,控制在16个字节,8字节的整数倍利用了操作系统的最佳特性。
如果rowkey按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。
一些常见的避免热点的方法以及它们的优缺点:
这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。
哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据
第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。
反转rowkey的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题
其他一些建议
尽量减少行和列的大小在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。如果你的rowkey和列名很大,甚至可以和具体的值相比较,那么你将会遇到一些有趣的问题。HBase storefiles中的索引(有助于随机访问)最终占据了HBase分配的大量内存,因为具体的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey和列名的大小。压缩也有助于更大的索引。
列族尽可能越短越好,最好是一个字符
冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好
背景:HBase默认建表时有一个region,这个region的rowkey是没有边界的,即没有startkey和endkey,在数据写入时,所有数据都会写入这个默认的region,随着数据量的不断 增加,此region已经不能承受不断增长的数据量,会进行split,分成2个region。在此过程中,会产生两个问题:1.数据往一个region上写,会有写热点问题。2.region split会消耗宝贵的集群I/O资源。基于此我们可以控制在建表的时候,创建多个空region,并确定每个region的起始和终止rowky,这样只要我们的rowkey设计能均匀的命中各个region,就不会存在写热点问题。自然split的几率也会大大降低。当然随着数据量的不断增长,该split的还是要进行split。像这样预先创建hbase表分区的方式,称之为预分区,下面给出一种预分区的实现方式:
首先看没有进行预分区的表,startkey和endkey为空。
要进行预分区,首先要明确rowkey的取值范围或构成逻辑,以我的rowkey组成为例:两位随机数+时间戳+客户号,两位随机数的范围从00-99,于是我划分了10个region来存储数据,每个region对应的rowkey范围如下:
-10,10-20,20-30,30-40,40-50,50-60,60-70,70-80,80-90,90-
在使用HBase API建表的时候,需要产生splitkeys二维数组,这个数组存储的rowkey的边界值。下面是java 代码实现:
需要注意的是,在上面的代码中用treeset对rowkey进行排序,必须要对rowkey排序,否则在调用admin.createTable(tableDescriptor,splitKeys)的时候会出错。创建表的代码如下:
在hbase shell中输入命令san 'hbase:meta'查看建表结果:
从上图可看出10个region均匀的分布在了3台regionserver上(集群就3台机器regionserver),达到预期效果。还可以在hbase的web UI界面中更加直观的查看建表的预分区信息。
再看看写数据是否均匀的命中各个region,是否能够做到对写请求的负载均衡
一、几个主要 Hbase API 类和数据模型之间的对应关系:
1、 HBaseAdmin
关系: org.apache.hadoop.hbase.client.HBaseAdmin
作用:提供了一个接口来管理 HBase 数据库的表信息。它提供的方法包括:创建表,删 除表,列出表项,使表有效或无效,以及添加或删除表列族成员等。
2、 HBaseConfiguration
关系: org.apache.hadoop.hbase.HBaseConfiguration
作用:对 HBase 进行配置
3、 HTableDescriptor
关系: org.apache.hadoop.hbase.HTableDescriptor
作用:包含了表的名字极其对应表的列族
4、 HColumnDescriptor
关系: org.apache.hadoop.hbase.HColumnDescriptor
作用:维护着关于列族的信息,例如版本号,压缩设置等。它通常在创建表或者为表添 加列族的时候使用。列族被创建后不能直接修改,只能通过删除然后重新创建的方式。
列族被删除的时候,列族里面的数据也会同时被删除。
5、 HTable
关系: org.apache.hadoop.hbase.client.HTable
作用:可以用来和 HBase 表直接通信。此方法对于更新操作来说是非线程安全的。
6、 Put
关系: org.apache.hadoop.hbase.client.Put
作用:用来对单个行执行添加操作
7、 Get
关系: org.apache.hadoop.hbase.client.Get
作用:用来获取单个行的相关信息
8、 Result
关系: org.apache.hadoop.hbase.client.Result
作用:存储 Get 或者 Scan 操作后获取表的单行值。使用此类提供的方法可以直接获取值 或者各种 Map 结构( key-value 对)
二、具体增删改查 代码具体实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
package
HbaseDome;
import
java.util.List;
import
org.apache.hadoop.conf.Configuration;
import
org.apache.hadoop.hbase.Cell;
import
org.apache.hadoop.hbase.HBaseConfiguration;
import
org.apache.hadoop.hbase.HColumnDescriptor;
import
org.apache.hadoop.hbase.HTableDescriptor;
import
org.apache.hadoop.hbase.KeyValue;
import
org.apache.hadoop.hbase.TableName;
import
org.apache.hadoop.hbase.client.Delete;
import
org.apache.hadoop.hbase.client.Get;
import
org.apache.hadoop.hbase.client.HBaseAdmin;
import
org.apache.hadoop.hbase.client.HTable;
import
org.apache.hadoop.hbase.client.Put;
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.protobuf.generated.ZooKeeperProtos.Table;
import
org.apache.hadoop.hbase.util.Bytes;
public
class
Hbasedome
implements
HBaseDemoInterface{
static
Configuration conf =
null
;
private
static
final
String ZKconnect=
"192.168.123.212:2181,192.168.123.213:2181,192.168.123.214:2181"
;
static
{
conf=HBaseConfiguration.create();
conf.set(
"hbase.zookeeper.quorum"
, ZKconnect);
}
// static String tableName="student";
// static String[] family={"lie01","lie02"};
public
static
void
main(String[] args) {
Hbasedome a =
new
Hbasedome();
String tableName=
"student11"
;
String[] family={
"lie01"
,
"lie02"
};
try
{
HTableDescriptor htds =
new
HTableDescriptor(tableName);
for
(
int
z=
0
;z
HColumnDescriptor h=
new
HColumnDescriptor(family[z]);
htds.addFamily(h);
}
// a.descTable("table03");
// a.createTable(tableName, htds);
// a.descTable("table03");
// a.getAllTables();
// a.createTable(tableName,family);
// a.getResult("table03", "usr001");
// a.dropTable("user1");
// a.getAllTables();
// a.putData("table03", "usr005", "liezu01", "name", "liu");
// a.getResult("table03", "usr001");
// a.getResultScann("table03");
// a.getResultScann("table03","");
Result result = a.getResult(
"table03"
,
"usr001"
);
System.out.println(result.toString());
List
for
(
int
i =
0
; i < cells.size(); i++) {
Cell cell = cells.get(i);
System.out.println(cell.toString());
// printCell(cell);
}
// List
// for (int i = 0; i < list.size(); i++) {
// KeyValue kv = list.get(i);
// printKeyValye(kv);
// }
}
catch
(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public
static
void
printKeyValye(KeyValue kv) {
System.out.println(Bytes.toString(kv.getRow()) +
"\t"
+ Bytes.toString(kv.getFamily()) +
"\t"
+ Bytes.toString(kv.getQualifier()) +
"\t"
+ Bytes.toString(kv.getValue()) +
"\t"
+ kv.getTimestamp());
}
public
static
void
printCell(Cell cell) {
System.out.println(Bytes.toString(cell.getRow()) +
"\t"
+ Bytes.toString(cell.getFamily()) +
"\t"
+ Bytes.toString(cell.getQualifier()) +
"\t"
+ Bytes.toString(cell.getValue()) +
"\t"
+ cell.getTimestamp());
}
//创建表
@Override
public
void
createTable(String tableName, String[] family)
throws
Exception {
HBaseAdmin admin=
new
HBaseAdmin(conf);
HTableDescriptor desc =
new
HTableDescriptor(tableName);
for
(
int
i=
0
;i
desc.addFamily(
new
HColumnDescriptor(family[i]));
System.out.println(
"11111111111"
+family[i]);
}
if
(admin.tableExists(tableName)){
System.out.println(
"表已经存在,别瞎输行吗"
);
// System.exit(0);
}
else
{
admin.createTable(desc);
System.out.println(
"表创建成功"
);
}
}
//创建表
@Override
public
void
createTable(String tableName, HTableDescriptor htds)
throws
Exception {
HBaseAdmin admin=
new
HBaseAdmin(conf);
boolean
tableExists1 = admin.tableExists(Bytes.toBytes(tableName));
System.out.println(tableExists1 ?
"表已存在"
:
"表不存在"
);
admin.createTable(htds);
boolean
tableExists = admin.tableExists(Bytes.toBytes(tableName));
System.out.println(tableExists ?
"创建表成功"
:
"创建失败"
);
}
@Override
public
void
descTable(String tableName)
throws
Exception {
HBaseAdmin admin=
new
HBaseAdmin(conf);
HTable table=
new
HTable(conf, tableName);
HTableDescriptor desc =table.getTableDescriptor();
HColumnDescriptor[] columnFamilies = desc.getColumnFamilies();
for
(HColumnDescriptor t:columnFamilies){
System.out.println(Bytes.toString(t.getName()));
}
}
这种方式是替换该表tableName的所有列簇
@Override
public
void
modifyTable(String tableName)
throws
Exception {
HBaseAdmin admin=
new
HBaseAdmin(conf);
HTableDescriptor htd =
new
HTableDescriptor(TableName.valueOf(tableName));
htd.addFamily(
new
HColumnDescriptor(Bytes.toBytes(
"cf3"
)));
htd.addFamily(
new
HColumnDescriptor(Bytes.toBytes(
"cf2"
)));
admin.modifyTable(tableName, htd);
// 删除该表tableName当中的特定的列簇
// admin.deleteColumn(tableName, "cf3");
System.out.println(
"修改成功"
);
}
@Override
public
void
getAllTables()
throws
Exception {
HBaseAdmin admin =
new
HBaseAdmin(conf);
String[] tableNames = admin.getTableNames();
for
(
int
i=
0
;i
System.out.println(tableNames[i]);
}
}
//更新数据 插入数据
@Override
public
void
putData(String tableName, String rowKey, String familyName, String columnName, String value)
throws
Exception {
HTable htable=
new
HTable(conf, Bytes.toBytes(tableName));
Put put=
new
Put(Bytes.toBytes(rowKey));
put.add(Bytes.toBytes(familyName), Bytes.toBytes(columnName), Bytes.toBytes(value));
htable.put(put);
}
//为表添加数据
@Override
public
void
addData(String tableName, String rowKey, String[] column1, String[] value1, String[] column2,
String[] value2)
throws
Exception {
Put put=
new
Put(Bytes.toBytes(rowKey));
HTable htable=
new
HTable(conf, Bytes.toBytes(tableName));
HColumnDescriptor[] columnFamilies = htable.getTableDescriptor().getColumnFamilies();
for
(
int
i=
0
;i<=columnFamilies.length;i++){
String nameAsString = columnFamilies[i].getNameAsString();
if
(nameAsString.equals(
"lie01"
)){
for
(
int
j=
0
;j
put.add(Bytes.toBytes(nameAsString), Bytes.toBytes(column1[j]),Bytes.toBytes(value1[j]));
}
}
if
(nameAsString.equals(
"lie02"
)){
for
(
int
j=
0
;j
put.add(Bytes.toBytes(nameAsString), Bytes.toBytes(column2[j]),Bytes.toBytes(value2[j]));
}
}
}
htable.put(put);
System.out.println(
"addData ok!"
);
}
//根据rowkey 查询
@Override
public
Result getResult(String tableName, String rowKey)
throws
Exception {
Get get=
new
Get(Bytes.toBytes(rowKey));
HTable htable=
new
HTable(conf, Bytes.toBytes(tableName));
Result result=htable.get(get);
// for(KeyValue k:result.list()){
// System.out.println(Bytes.toString(k.getFamily()));
// System.out.println(Bytes.toString(k.getQualifier()));
// System.out.println(Bytes.toString(k.getValue()));
// System.out.println(k.getTimestamp());
// }
return
result;
}
//查询指定的某列
@Override
public
Result getResult(String tableName, String rowKey, String familyName, String columnName)
throws
Exception {
Get get=
new
Get(Bytes.toBytes(rowKey));
HTable htable=
new
HTable(conf, Bytes.toBytes(tableName));
get.addColumn(Bytes.toBytes(familyName),Bytes.toBytes(columnName));
Result result=htable.get(get);
for
(KeyValue k:result.list()){
System.out.println(Bytes.toString(k.getFamily()));
System.out.println(Bytes.toString(k.getQualifier()));
System.out.println(Bytes.toString(k.getValue()));
System.out.println(k.getTimestamp());
}
return
result;
}
//遍历查询表
@Override
public
ResultScanner getResultScann(String tableName)
throws
Exception {
Scan scan=
new
Scan();
ResultScanner rs =
null
;
HTable htable=
new
HTable(conf, tableName);
try
{
rs=htable.getScanner(scan);
for
(Result r: rs){
for
(KeyValue kv:r.list()){
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
System.out.println(kv.getTimestamp());
}
}
}
finally
{
rs.close();
}
return
rs;
}
@Override
public
ResultScanner getResultScann(String tableName, Scan scan)
throws
Exception {
ResultScanner rs =
null
;
HTable htable=
new
HTable(conf, tableName);
try
{
rs=htable.getScanner(scan);
for
(Result r: rs){
for
(KeyValue kv:r.list()){
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
System.out.println(kv.getTimestamp());
}
}
}
finally
{
rs.close();
}
return
rs;
}
//查询表中的某一列
@Override
public
Result getResultByColumn(String tableName, String rowKey, String familyName, String columnName)
throws
Exception {
HTable htable=
new
HTable(conf, tableName);
Get get=
new
Get(Bytes.toBytes(rowKey));
get.addColumn(Bytes.toBytes(familyName),Bytes.toBytes(columnName));
Result result=htable.get(get);
for
(KeyValue kv: result.list()){
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
System.out.println(kv.getTimestamp());
}
return
result;
}
//查询某列数据的某个版本
@Override
public
Result getResultByVersion(String tableName, String rowKey, String familyName, String columnName,
int
versions)
throws
Exception {
HTable htable=
new
HTable(conf, tableName);
Get get =
new
Get(Bytes.toBytes(rowKey));
get.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(columnName));
get.setMaxVersions(versions);
Result result=htable.get(get);
for
(KeyValue kv: result.list()){
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
System.out.println(kv.getTimestamp());
}
return
result;
}
//删除指定某列
@Override
public
void
deleteColumn(String tableName, String rowKey, String falilyName, String columnName)
throws
Exception {
HTable htable=
new
HTable(conf, tableName);
// Delete delete1=new Delete(Bytes.toBytes(rowKey));
Delete de =
new
Delete(Bytes.toBytes(rowKey));
de.deleteColumn(Bytes.toBytes(falilyName), Bytes.toBytes(columnName));
htable.delete(de);
}
//删除指定的某个rowkey
@Override
public
void
deleteColumn(String tableName, String rowKey)
throws
Exception {
HTable htable=
new
HTable(conf, tableName);
Delete de =
new
Delete(Bytes.toBytes(rowKey));
htable.delete(de);
}
//让该表失效
@Override
public
void
disableTable(String tableName)
throws
Exception {
HBaseAdmin admin=
new
HBaseAdmin(conf);
admin.disableTable(tableName);
}
//删除表
@Override
public
void
dropTable(String tableName)
throws
Exception {
HBaseAdmin admin=
new
HBaseAdmin(conf);
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
|
package
com.ghgj.hbase.test1610;
import
java.io.IOException;
import
java.util.ArrayList;
import
java.util.Iterator;
import
java.util.List;
import
org.apache.hadoop.conf.Configuration;
import
org.apache.hadoop.hbase.Cell;
import
org.apache.hadoop.hbase.HBaseConfiguration;
import
org.apache.hadoop.hbase.HColumnDescriptor;
import
org.apache.hadoop.hbase.HTableDescriptor;
import
org.apache.hadoop.hbase.KeyValue;
import
org.apache.hadoop.hbase.TableName;
import
org.apache.hadoop.hbase.client.Get;
import
org.apache.hadoop.hbase.client.HBaseAdmin;
import
org.apache.hadoop.hbase.client.HTable;
import
org.apache.hadoop.hbase.client.Put;
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;
public
class
HBaseAPIDemo1610
implements
HBaseDemoInterface {
private
static
final
String ROWKEY =
"p001"
;
private
static
final
String ROWKEY2 =
"p002"
;
private
static
final
String FAMILY1 =
"cf1"
;
private
static
final
String FAMILY2 =
"cf2"
;
private
static
final
String KEY =
"name"
;
private
static
final
String VALUE =
"huangbo"
;
private
static
final
String TABLE_NAME =
"person"
;
private
static
final
String[] COLUMN_FAMILY =
new
String[] { FAMILY1, FAMILY2 };
static
Configuration conf =
null
;
static
HBaseAdmin admin =
null
;
static
HTable table =
null
;
static
{
try
{
conf = HBaseConfiguration.create();
conf.set(
"hbase.zookeeper.quorum"
,
"hadoop03:2181,hadoop04:2181,hadoop05:2181"
);
admin =
new
HBaseAdmin(conf);
table =
new
HTable(conf, TABLE_NAME);
}
catch
(IOException e) {
// e.printStackTrace();
System.out.println(
"报错"
);
}
}
public
static
void
main(String[] args)
throws
Exception {
HBaseAPIDemo1610 hbase =
new
HBaseAPIDemo1610();
// 测试创建表
hbase.createTable(TABLE_NAME, COLUMN_FAMILY);
// 测试创建表
HTableDescriptor htd =
new
HTableDescriptor(TableName.valueOf(TABLE_NAME));
for
(
int
i =
0
; i < COLUMN_FAMILY.length; i++) {
HColumnDescriptor cf1 =
new
HColumnDescriptor(COLUMN_FAMILY[i]);
htd.addFamily(cf1);
}
hbase.createTable(TABLE_NAME, htd);
// 查看表属性
hbase.descTable(TABLE_NAME);
// 查询所有的表
hbase.getAllTables();
// 测试修改表
hbase.modifyTable(TABLE_NAME);
// 插入数据
hbase.putData(TABLE_NAME, ROWKEY, FAMILY1, KEY, VALUE);
// 测试插入一堆数据
String[] column1 =
new
String[] {
"name1"
,
"age"
,
"province"
};
String[] value1 =
new
String[] {
"huangbo"
,
"33"
,
"xinjiang"
};
String[] column2 =
new
String[] {
"gender"
};
String[] value2 =
new
String[] {
"male"
};
hbase.addData(TABLE_NAME, ROWKEY2, column1, value1, column2, value2);
// 通过rowkey查询数据
Result result = hbase.getResult(TABLE_NAME, ROWKEY2);
System.out.println(result.toString());
List
for
(
int
i =
0
; i < list.size(); i++) {
KeyValue kv = list.get(i);
printKeyValye(kv);
}
// 通过rowkey, family, province查询数据
Result result1 = hbase.getResult(TABLE_NAME, ROWKEY2, FAMILY1,
"province"
);
List
for
(
int
i =
0
; i < cells.size(); i++) {
Cell cell = cells.get(i);
printCell(cell);
}
// 扫描全表数据
ResultScanner resultScann = hbase.getResultScann(TABLE_NAME);
printResultScanner(resultScann);
/*Iterator
while(iterator.hasNext()){
Result next = iterator.next();
}*/
// 通过scan扫描全表数据,scan中可以加入一些过滤条件
Scan scan =
new
Scan();
scan.setStartRow(Bytes.toBytes(
"user"
));
scan.setStopRow(Bytes.toBytes(
"zk002"
));
scan.setTimeRange(1488252774189l, 1488252774191l);
ResultScanner resultScann1 = hbase.getResultScann(TABLE_NAME, scan);
printResultScanner(resultScann1);
// 两种方式查询最大版本数的hbase数据
Result resultByVersion = hbase.getResultByVersion(TABLE_NAME, ROWKEY, FAMILY1,
"name"
,
3
);
printResult(resultByVersion);
System.out.println(
"-------------------"
);
ResultScanner rs = hbase.getResultByVersion(ROWKEY, FAMILY1,
"name"
,
3
);
printResultScanner(rs);
// 删除表
hbase.dropTable(TABLE_NAME);
}
public
static
void
printResultScanner(ResultScanner resultScann) {
for
(Result result : resultScann) {
printResult(result);
}
}
public
static
void
printResult(Result result) {
List
for
(
int
i =
0
; i < cells.size(); i++) {
Cell cell = cells.get(i);
printCell(cell);
}
}
public
static
void
printCell(Cell cell) {
System.out.println(Bytes.toString(cell.getRow()) +
"\t"
+ Bytes.toString(cell.getFamily()) +
"\t"
+ Bytes.toString(cell.getQualifier()) +
"\t"
+ Bytes.toString(cell.getValue()) +
"\t"
+ cell.getTimestamp());
}
public
static
void
printKeyValye(KeyValue kv) {
System.out.println(Bytes.toString(kv.getRow()) +
"\t"
+ Bytes.toString(kv.getFamily()) +
"\t"
+ Bytes.toString(kv.getQualifier()) +
"\t"
+ Bytes.toString(kv.getValue()) +
"\t"
+ kv.getTimestamp());
}
// create 'tablename','cf1','cf2'
@Override
public
void
createTable(String tableName, String[] family)
throws
Exception {
HTableDescriptor htd =
new
HTableDescriptor(TableName.valueOf(tableName));
for
(
int
i =
0
; i < family.length; i++) {
HColumnDescriptor cf1 =
new
HColumnDescriptor(family[i]);
htd.addFamily(cf1);
}
admin.createTable(htd);
boolean
tableExists = admin.tableExists(Bytes.toBytes(tableName));
System.out.println(tableExists ?
"创建表成功"
:
"创建失败"
);
}
@Override
public
void
createTable(String tableName, HTableDescriptor htd)
throws
Exception {
admin.createTable(htd);
boolean
tableExists = admin.tableExists(Bytes.toBytes(tableName));
System.out.println(tableExists ?
"创建表成功"
:
"创建失败"
);
}
// desc 'person'
@Override
public
void
descTable(String tableName)
throws
Exception {
HTableDescriptor tableDescriptor = table.getTableDescriptor();
HColumnDescriptor[] columnFamilies = tableDescriptor.getColumnFamilies();
for
(HColumnDescriptor hcd : columnFamilies) {
// System.out.println(hcd.toString()+"\t");
System.out.println(Bytes.toString(hcd.getName()));
}
}
@Override
public
void
modifyTable(String tableName)
throws
Exception {
// 这种方式是替换该表tableName的所有列簇
HTableDescriptor htd =
new
HTableDescriptor(TableName.valueOf(tableName));
htd.addFamily(
new
HColumnDescriptor(Bytes.toBytes(
"cf3"
)));
htd.addFamily(
new
HColumnDescriptor(Bytes.toBytes(
"cf2"
)));
admin.modifyTable(tableName, htd);
// 删除该表tableName当中的特定的列簇
// admin.deleteColumn(tableName, "cf3");
System.out.println(
"修改成功"
);
}
// list
@Override
public
void
getAllT
|