Hbase基础及代码实现

来源:互联网 发布:贝奥武夫结局知乎 编辑:程序博客网 时间:2024/06/16 09:28

HBase简介

HBase的发展史

2006年底由PowerSet的Chad Walters和Jim Kellerman 发起,2008年成为Apache Hadoop的一个子项目。现已作为产品在多家企业被使用,如:

1.      WorldLingo

2.      Streamy.com

3.      OpenPlaces

4.      Yahoo!

5.      Adobe

6.      淘宝

7.      Facebook

8.      Twitter

9.      Trend Micro

Hbase是什么

HBase是一种构建在HDFS之上的分布式、面向列的存储系统。在需要实时读写、随机访问超大规模数据集时,可以使用HBase。

尽管已经有许多数据存储和访问的策略和实现方法,但事实上大多数解决方案,特别是一些关系类型的,在构建时并没有考虑超大规模和分布式的特点。许多商家通过复制和分区的方法来扩充数据库使其突破单个节点的界限,但这些功能通常都是事后增加的,安装和维护都和复杂。同时,也会影响RDBMS的特定功能,例如联接、复杂的查询、触发器、视图和外键约束这些操作在大型的RDBMS上的代价相当高,甚至根本无法实现。

HBase从另一个角度处理伸缩性问题。它通过线性方式从下到上增加节点来进行扩展。HBase不是关系型数据库,也不支持SQL,但是它有自己的特长,这是RDBMS不能处理的,HBase巧妙地将大而稀疏的表放在商用的服务器集群上。

HBase是Google Bigtable 的开源实现,与Google Bigtable 利用GFS作为其文件存储系统类似, HBase 利用Hadoop HDFS 作为其文件存储系统;Google 运行MapReduce 来处理Bigtable中的海量数据, HBase 同样利用Hadoop MapReduce来处理HBase中的海量数据;Google Bigtable 利用Chubby作为协同服务, HBase 利用Zookeeper作为对应。

HBase的特点

10.   大:一个表可以有上亿行,上百万列。

11.   面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。

12.   稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。

13.   无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可以有截然不同的列。

14.   数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时的时间戳。

15.   数据类型单一:HBase中的数据都是字符串,没有类型。

HBase的高并发和实时处理数据

Hadoop是一个高容错、高延时的分布式文件系统和高并发的批处理系统,不适用于提供实时计算;HBase是可以提供实时计算的分布式数据库,数据被保存在HDFS分布式文件系统上,由HDFS保证期高容错性,但是再生产环境中,HBase是如何基于hadoop提供实时性呢? HBase上的数据是以StoreFile(HFile)二进制流的形式存储在HDFS上block块儿中;但是HDFS并不知道的hbase存的是什么,它只把存储文件是为二进制文件,也就是说,hbase的存储数据对于HDFS文件系统是透明的。下面是HBase文件在HDFS上的存储示意图。

HBase HRegion servers集群中的所有的region的数据在服务器启动时都是被打开的,并且在内冲初始化一些memstore,相应的这就在一定程度上加快系统响应;而Hadoop中的block中的数据文件默认是关闭的,只有在需要的时候才打开,处理完数据后就关闭,这在一定程度上就增加了响应时间。

从根本上说,HBase能提供实时计算服务主要原因是由其架构和底层的数据结构决定的,即由LSM-Tree+ HTable(region分区) + Cache决定——客户端可以直接定位到要查数据所在的HRegion server服务器,然后直接在服务器的一个region上查找要匹配的数据,并且这些数据部分是经过cache缓存的。具体查询流程如下图所示:

具体数据访问流程如下:

16.   Client会通过内部缓存的相关的-ROOT-中的信息和.META.中的信息直接连接与请求数据匹配的HRegion server;

17.   然后直接定位到该服务器上与客户请求对应的Region,客户请求首先会查询该Region在内存中的缓存——Memstore(Memstore是一个按key排序的树形结构的缓冲区);

18.   如果在Memstore中查到结果则直接将结果返回给Client;

19.   在Memstore中没有查到匹配的数据,接下来会读已持久化的StoreFile文件中的数据。前面的章节已经讲过,StoreFile也是按 key排序的树形结构的文件——并且是特别为范围查询或block查询优化过的,;另外HBase读取磁盘文件是按其基本I/O单元(即 HBase Block)读数据的。

具体就是过程就是:

如果在BlockCache中能查到要造的数据则这届返回结果,否则就读去相应的StoreFile文件中读取一block的数据,如果还没有读到要查的 数据,就将该数据block放到HRegion Server的blockcache中,然后接着读下一block块儿的数据,一直到这样循环的block数据直到找到要请求的数据并返回结果;如果将该 Region中的数据都没有查到要找的数据,最后接直接返回null,表示没有找的匹配的数据。当然blockcache会在其大小大于一的阀值(heapsize *hfile.block.cache.size * 0.85)后启动基于LRU算法的淘汰机制,将最老最不常用的block删除。

HBase数据模型

HBase以表的形式存储数据。表由行和列组成。列划分为若干个列族(row family),如下图所示。

HBase的逻辑数据模型:

 

HBase的物理数据模型(实际存储的数据模型):

逻辑数据模型中空白cell在物理上是不存储的,因为根本没有必要存储,因此若一个请求为要获取t8时间的contents:html,他的结果就是空。相似的,若请求为获取t9时间的anchor:my.look.ca,结果也是空。但是,如果不指明时间,将会返回最新时间的行,每个最新的都会返回

Row Key

与 NoSQL数据库一样,Row Key 是用来检索记录的主键。访问 HBase table 中的行,只有三种方式:

1)通过单个 Row Key访问。

2)通过 Row Key的 range 全表扫描。

3)Row Key可以使任意字符串(最大长度是64KB,实际应用中长度一般为 10 ~ 100bytes),在HBase 内部,Row Key 保存为字节数组。

在存储时,数据按照* Row Key的字典序(byte order)排序存储*。设计 Key 时,要充分排序存储这个特性,将经常一起读取的行存储到一起(位置相关性)。

注意字典序对 int排序的结果是 1,10,100,11,12,13,14,15,16,17,18,19,20,21,…, 9,91,92,93,94,95,96,97,98,99。要保存整形的自然序,Row Key 必须用 0 进行左填充。

行的一次读写是原子操作(不论一次读写多少列)。这个设计决策能够使用户很容易理解程序在对同一个行进行并发更新操作时的行为。

列族

HBase表中的每个列都归属于某个列族。列族是表的Schema 的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀,例如courses:history、courses:math 都属于 courses 这个列族。

访问控制、磁盘和内存的使用统计都是在列族层面进行的。在实际应用中,列族上的控制权限能帮助我们管理不同类型的应用,例如,允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、 

一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。

时间戳

HBase中通过 Row 和 Columns 确定的一个存储单元称为 Cell。每个 Cell 都保存着同一份数据的多个版本。 版本通过时间戳来索引,时间戳的类型是 64 位整型。时间戳可以由HBase(在数据写入时自动)赋值, 

此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显示赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 Cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。

为了避免数据存在过多版本造成的管理(包括存储和索引)负担,HBase提供了两种数据版本回收方式。 一是保存数据的最后 n 个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。

Cell

Cell是由 {row key,column(=< family> + < label>),version}唯一确定的单元。Cell 中的数据是没有类型的,全部是字节码形式存储。

HBase物理存储

Table在行的方向上分割为多个HRegion,每个HRegion分散在不同的RegionServer中。

每个HRegion由多个Store构成,每个Store由一个memStore和0或多个StoreFile组成,每个Store保存一个Columns Family

HBase系统架构

HBase架构图如下:

从HBase的架构图上可以看出,HBase中的组件包括Client、Zookeeper、HMaster、HRegionServer、HRegion、Store、MemStore、StoreFile、HFile、HLog等,接下来介绍他们的作用。

HBase中的每张表都通过行键按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,这个过程由HRegionServer管理,而HRegion的分配由HMaster管理。

Client

包含访问HBase的接口,并维护cache来加快对HBase的访问。

Zookeeper的作用

HBase依赖Zookeeper,默认情况下HBase管理Zookeeper实例(启动或关闭Zookeeper),Master与RegionServers启动时会向Zookeeper注册。

Zookeeper的作用:

保证任何时候,集群中只有一个master

存储所有Region的寻址入口

实时监控Region server的上线和下线信息。并实时通知给master

存储HBase的schema和table元数据

HMaster的作用

20.   为Region server分配region

21.   负责Region server的负载均衡

22.   发现失效的Region server并重新分配其上的region。

23.   HDFS上的垃圾文件回收。

24.   处理schema更新请求。

HRegionServer的作用

25.   维护master分配给他的region,处理对这些region的io请求。

26.   负责切分正在运行过程中变的过大的region

注意:client访问hbase上的数据时不需要master的参与,因为数据寻址访问zookeeper和region server,而数据读写访问region server。master仅仅维护table和region的元数据信息,而table的元数据信息保存在zookeeper上,因此master负载很低。

HRegion

table在行的方向上分隔为多个Region。Region是HBase中分布式存储和负载均衡的最小单元,即不同的region可以分别在不同的Region Server上,但同一个Region是不会拆分到多个server上。

Region按大小分隔,每个表一般是只有一个region。随着数据不断插入表,region不断增大,当region的某个列族达到一个阈值(默认256M)时就会分成两个新的region。

每个region由以下信息标识:

27.   < 表名,startRowkey,创建时间>

28.   由目录表(-ROOT-和.META.)记录该region的endRowkey

Region被分配给哪个Region Server是完全动态的,所以需要机制来定位Region具体在哪个region server。 

下面我们来看看Region是如何被定位的。

HRegion定位

1、29.   通过zk里的文件/hbase/rs得到-ROOT-表的位置。-ROOT-表只有一个region。

30.   通过-ROOT-表查找.META.表的第一个表中相应的region的位置。其实-ROOT-表是.META.表的第一个region;.META.表中的每一个region 

在-ROOT-表中都是一行记录。

31.   通过.META.表找到所要的用户表region的位置。用户表中的每个region在.META.表中都是一行记录。

-ROOT-表永远不会被分隔为多个region,保证了最多需要三次跳转,就能定位到任意的region。client会将查询的位置 信息保存缓存起来,缓存不会主动失效,因此如果client上的缓存全部失效,则需要进行6次网络来回,才能定位到正确的region,其中三次用来发现 缓存失效,另外三次用来获取位置信息。

提示:

-ROOT-表:表包含.META.表所在的region列表,该表只有一个Region;Zookeeper中记录了-ROOT-表的location

.META.表:表包含所有的用户空间region列表,以及Region Server的服务器地址

Store

每一个region由一个或多个store组成,至少是一个store,hbase会把一起访问的数据放在一个store里面,即为每个 ColumnFamily建一个store,如果有几个ColumnFamily,也就有几个Store。一个Store由一个memStore和0或者 多个StoreFile组成。HBase以store的大小来判断是否需要切分region

MemStore

memStore是放在内存里的。保存修改的数据即keyValues。当memStore的大小达到一个阀值(默认64MB)时,memStore会被flush到文 件,即生成一个快照。目前hbase 会有一个线程来负责memStore的flush操作。

StoreFile

memStore内存中的数据写到文件后就是StoreFile,StoreFile底层是以HFile的格式保存。

HLog

HLog(WAL log):WAL意为write ahead log,用来做灾难恢复使用,HLog记录数据的所有变更,一旦region server 宕机,就可以从log中进行恢复。

HLog文件就是一个普通的HadoopSequence File, Sequence File的value是key时HLogKey对象,其中记录了写入数据的归属信息,除了table和region名字外,还同时包括sequence number和timestamp,timestamp是写入时间,sequence number的起始值为0,或者是最近一次存入文件系统中的sequence number。 Sequence File的value是HBase的KeyValue对象,即对应HFile中的KeyValue。

LogFlusher

前面提到,数据以KeyValue形式到达HRegionServer,将写入WAL之后,写入一个SequenceFile。看过去没问题,但是因为数 据流在写入文件系统时,经常会缓存以提高性能。这样,有些本以为在日志文件中的数据实际在内存中。 

这里,我们提供了一个LogFlusher的类。它调用 HLog.optionalSync(),后者根据 hbase.regionserver.optionallogflushinterval (默认是10秒),定期调用Hlog.sync()。另外,HLog.doWrite()也会根据 

hbase.regionserver.flushlogentries (默认100秒)定期调用Hlog.sync()。Sync() 本身调用HLog.Writer.sync(),它由SequenceFileLogWriter实现。

LogRoller

Log的大小通过$HBASE_HOME/conf/hbase-site.xml的 hbase.regionserver.logroll.period 限制,默认是一个小时。所以每60分钟,会打开一个新的log文件。久而久之,会有一大堆的文件需要维护。首先,LogRoller调用 HLog.rollWriter(),定时滚动日志,之后,利用HLog.cleanOldLogs()可以清除旧的日志。它首先取得存储文件中的最大的sequence number,之后检查是否存在一个log所有的条目的“sequence number”均低于这个值,如果存在,将删除这个log。每个region server维护一个HLog,而不是每一个region一个,这样不同region(来自不同的table)的日志会混在一起,这样做的目的是不断追加 单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高table的写性能。带来麻烦的时,如果一个region server下线,为了恢复其上的region,需要将region server上的log进行拆分,然后分发到其他region server上进行恢复。

HBase工作流程

目前为止,相信你已经了解了HBase组件的使用和作用,但你可能还不太清楚上面提及的这些HBase组件间是如何运作的,下面我们来看看HBase的工作流程。

Client

首先当一个请求产生时,HBase Client使用RPC(远程过程调用)机制与HMaster和HRegionServer进行通信,对于管理类操作,Client与HMaster进行RPC;对于数据读写操作,Client与HRegionServer进行RPC。

Zookeeper

HBase Client使用RPC(远程过程调用)机制与HMaster和HRegionServer进行通信,但如何寻址呢?由于Zookeeper中存储了-ROOT-表的地址和HMaster的地址,所以需要先到Zookeeper上进行寻址。

HRegionServer也会把自己以Ephemeral方式注册到Zookeeper中,使HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的单点故障。

HMaster

当用户需要进行Table和Region的管理工作时,就需要和HMaster进行通信。HBase中可以启动多个HMaster,通过Zookeeper的MasterEletion机制保证总有一个Master运行。

32.   管理用户对Table的增删改查操作

33.   管理HRegionServer的负载均衡,调整Region的分布

34.   在Region Split后,负责新Region的分配

35.   在HRegionServer停机后,负责失效HRegionServer上的Regions迁移

HRegionServer

当用户需要对数据进行读写操作时,需要访问HRegionServer。HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族创建一个Store实例,每个Store都会有一个 MemStore和0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile, HFile就是实际的存储文件。因此,一个HRegion有多少个列族就有多少个Store。 一个HRegionServer会有多个HRegion和一个HLog。

当HStore存储是HBase的核心了,其中由两部分组成:MemStore和StoreFiles。MemStore是Sorted Memory Buffer,用户 

写入数据首先会放在MemStore,当MemStore满了以后会Flush成一个 StoreFile(实际存储在HDHS上的是HFile),当StoreFile文件数量增长到一定阀值,就会触发Compact合并操作,并将多个StoreFile合并成一个StoreFile,合并过程中会进行版本合并和数据删除,因此可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的 读写操作只要进入内存中就可以立即返回,保证了HBase I/O的高性能。

下面以一个具体数据Put的流程,让我们加深对HBase工作流程的认识。

HBase Put流程

下面是put流程的时序图:

客户端:

36.   客户端发起Put写请求,讲put写入writeBuffer,如果是批量提交,写满缓存后自动提交

37.   根据rowkey将put吩咐给不同regionserver

服务端:

38.   RegionServer将put按rowkey分给不同的region

39.   Region首先把数据写入wal

40.   wal写入成功后,把数据写入memstore

41.   Memstore写完后,检查memstore大小是否达到flush阀值

42.   如果达到flush阀值,将memstore写入HDFS,生成HFile文件

HBase Compact &&Split

当StoreFile文件数量增长到一定阀值,就会触发Compact合并操作,并将多个StoreFile合并成一个StoreFile,当这个StoreFile大小超过一定阀值后,会触发Split操作,同时把当前Region Split成2个Region,这是旧的Region会下线,新Split出的2个Region会被HMaster分配到相应的HregionServer上,使得原先1个Region的压力得以分散到2个Region上。

如下图四个Storefile文件(从memstore文件经过flush而得到,默认64M的storefile文件)经过Compact合并成一个大的256M storefile文件,当设定的Region阀值为128M时,就会Split为两个128M的Storefile文件,然后HMaster再把这两个storefile文件分配到不停地Regionserver上。

HFile

HBase中所有的数据文件都存储在Hadoop HDFS上,主要包括两种文件类型:

Hfile:HBase中KeyValue数据的存储格式,HFile是Hadoop的 二进制格式文件,实际上StoreFile就是对Hfile做了轻量级包装,即StoreFile底层就是HFile

HLog File:HBase中WAL(write ahead log)的存储格式,物理上是Hadoop的Sequence File

HFile的存储格式如下:

HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。

Trailer中有指针指向其他数据块的起始点,FileInfo记录了文件的一些meta信息。 Data Block是hbaseio的基本单元,为了提高效率,HRegionServer中有基于LRU的block cache机制。 

每个Data块的大小可以在创建一个Table的时候通过参数指定(默认块大小64KB),大号的Block有利于顺序Scan,小号的 Block利于随机查询。 

每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成,Magic内容就是一些随机数字,目的是防止数 据损坏,结构如下。

上图可知,开始是两个固定长度的数值,分别表示key的长度和alue的长度。紧接着是Key,开始是固定长度的数值,表示RowKey的长度,紧接着是RowKey,然后是固定长度的数值,表示Family的长度,然后是Family,接着是Qualifier,然后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)。Value部分没有那么复杂的结构,就是纯粹的二进制数据。

HBase的三维有序(即字典顺序)存储

Hfile是HBase中KeyValue数据的存储格式。从上面的 HBase物理数据模型中可以看出,HBase是面向列表(簇)的存储。每个Cell由 {row key,column(=< family> + <label>),version} 唯一确定的单元,他们组合在一起就是一个KeyValue。根据上述描述,这个KeyValue中的key就是{row key,column(=<family> + < label>),version} ,而value就是cell中的值。

HBase的三维有序存储中的三维是指:rowkey(行主键),column key(columnFamily+< label>),timestamp(时间戳或者版本号)三部分组成的三维有序存储。

rowkey

rowkey是行的主键,它是以字典顺序排序的。所以rowkey的设计是至关重要的,关系到你应用层的查询效率。我们在根据rowkey范围查询的时候,我们一般是知道startRowkey,如果我们通过scan只传startRowKey : d开头的,那么查询的是所有比d大的都查了,而我们只需要d开头的数据,那就要通过endRowKey来限制。我们可以通过设定endRowKey为:d 开头,后面的根据你的rowkey组合来设定,一般是加比startKey大一位。

column key

column key是第二维,数据按rowkey字典排序后,如果rowkey相同,则是根据column key来排序的,也是按字典排序。 

我们在设计table的时候要学会利用这一点。比如我们的收件箱。我们有时候需要按主题排序,那我们就可以把主题这设置为我们的column key,即设计为columnFamily+主题.,这样的设计。

timestamp

timestamp时间戳,是第三维,这是个按降序排序的,即最新的数据排在最前面。这个就没有什么说的了。网上其他的博客也提到比较多。

HLog Replay

根据以上的叙述,我们已经了解了关于HStore的基本原理,但我们还必须要了解一下HLog的功能,因为上述的HStore在系统正常工作的前提下是没问题的,但是在分布式 系统环境中,无法避免系统出错或者宕机,因为一旦HRegionServer意外退出,MemStore中的内存数据将会丢失,这就需要引入HLog。每个HRegionServer中都有一个HLog对象,HLog是一个实现WriteAhead Log的类,在每一次用户操作写入MemStore的同时,也会写一份数据到HLog文件中,HLog文件定期(当文件已持久化到StoreFile中的数据)会滚出新的,并且删除旧的文件。当HRegionServer意外终止 后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的Hlog文件,将其中不同Region的Log数据进行拆分,分别放到相应Region的目录下,然后再将失效的Region重新分配,领取到这些Region的Regionserver在LoadRegion的过程中,会发现历史HLog需要处理,因此ReplayHLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。

HLog存储格式

WAL(WriteAhead Log):RegionServer在处理插入和删除过程中,用来记录操作内容的日志,只有日志写入成功,才会通知客户端操作成功。

上图中是HLog文件的结构,其实HLog文件就是一个普通的Hadoop Sequence File,Sequence File的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和Region名字外,同时还包括sequence number和timestamp,timestamp是”写入时间”,sequencenumber 的起始值为0,或者是最近一次存入文件系统中的sequencenumber。

HLog Sequence File的Value是HBase的KeyValue对象昂,即对应HFile中的KeyValue。

HBase的高可用

当出现上图三种情况的高可用策略:

43.   HDFS机架识别策略:当数据文件损坏时,会找相同机架上备份的数据文件,如果相同机架上的数据文件也损坏会找不同机架备份数据文件。

44.   HBase的Region快速恢复:当节点损坏时,节点上的丢失region,会在其他节点上均匀快速恢复。

45.   Master节点的HA机制:Master为一主多备。当Master主节点宕机后,剩下的备节点通过选举,产生主节点。

HBase性能和优化

影响HBase性能的因素

上图中,从HDFS以下都属于HBase的支撑系统。

从构成集群机器的底层硬件到把硬件和操作系统(尤其是文件系统),JVM,HDFS连接起来的网络之间的所有部件都会影响到HBase的性能。HBase系统的状态也会影响到HBase的系统。例如,在集群中执行合并的时候或者Memstore刷写的时候与什么都没有做的时候相比,其性能表现是不同的。应用系统的性能还取决于它和HBase的交互方式,所以模式设计和其他环节一样起到了必不可少的作用。

在评判HBase性能时,所有这些因素都有影响;在优化集群时,需要查看所有这些因素。

优化HBase支撑系统

(1)硬件选择

总的原则是,根据业务情况和集群规模大小选择合理的硬件。

(2)网络配置

基于当前阶段硬件的典型分布式系统都会受到网络的限制,HBase也不例外。在节点和机架顶置交换机之间建议采用10Gb以太网交换机。千万不要过于满额配置地使用网络,否则在高负载时,会影响HBase应用系统的性能。

(3)操作系统

一般情况下,只要使用Hadoop和HBase,操作系统通常选择Linux。可以选择Red Hat Enterprise Linux,CentOS,也可以选择成功部署过的其他操作系统。

(4)本地文件系统

本地Linux文件系统在HBase集群体系里起到了重要作用,并且严重影响到HBase的性能。虽然EXT4是推荐使用的本地文件系统,但没有大规模使用,相反EXT3和XFS已经在生产系统里得到成功使用,建议使用EXT3和XFS作为本地文件系统。

(5)HDFS

根据业务访问特点优化

根据业务访问特点,将Hbase的工作负载大致分为以下四类:

(1)随机读密集型

对于随机读密集型工作负载,高效利用缓存和更好地索引会给HBase系统带来更高的性能。

(2)顺序读密集型

对于顺序读密集型工作负载,读缓存不会带来太多好处;除非顺序读的规模很小并且限定在一个特定的行键范围内,否则很可能使用缓存会比不使用缓存需要更频繁地访问硬盘。

(3)写密集型

写密集型工作负载的优化方法需要有别于读密集型负载。缓存不再起到重要作用。写操作总是进入MemStore,然后被刷写生成新的Hfile,以后再被合并。获得更好写性能的办法是不要太频繁刷写、合并或者拆分,因为在这段时间里IO压力上升,系统会变慢。

(4)混合型

对于完全混合型工作负载,优化方法会变得复杂些。优化时,需要混合调整多个参数来得到一个最优的组合。

其它角度来优化HBase性能

(1)JVM垃圾回收优化

(2)本地memstore分配缓存优化

(3)Region拆分优化

(4**)Region合并优化**

(5)Region预先加载优化

(6)负载均衡优化

(7)启用压缩

(8)GZIP、snappy、lzo,推荐snappy,压缩比稍差于lzo;但是压缩速度高于lzo,并且与lzo有差不多的 解压缩速度。

(9)进行预分区,从而避免自动split,提高hbase响应速度

(10)避免出现region热点现象,启动按照table级别进行balance

HBase常见的调优参数

 

 

 

对于建表,和RDBMS类似,HBase也有namespace的概念,可以指定表空间创建表,也可以直接创建表,进入default表空间。

对于数据操作,HBase支持四类主要的数据操作,分别是:

Put:增加一行,修改一行;

Delete:删除一行,删除指定列族,删除指定column的多个版本,删除指定column的制定版本等;

Get:获取指定行的所有信息,获取指定行和指定列族的所有colunm,获取指定column,获取指定column的几个版本,获取指定column的指定版本等;

Scan:获取所有行,获取指定行键范围的行,获取从某行开始的几行,获取满足过滤条件的行等。

这四个类都是org.apache.hadoop.hbase.client的子类,可以到官网API去查看详细信息,本文仅总结常用方法,力争让读者用20%的时间掌握80%的常用功能。

目录

1.命名空间Namespace

2.创建表

3.删除表

4.修改表

5.新增、更新数据Put

6.删除数据Delete

7.获取单行Get

8.获取多行Scan

1. 命名空间Namespace

在关系数据库系统中,命名空间namespace指的是一个表的逻辑分组,同一组中的表有类似的用途。命名空间的概念为即将到来的多租户特性打下基础:

l 配额管理(Quota Management(HBASE-8410)):限制一个namespace可以使用的资源,资源包括region和table等;

l 命名空间安全管理(Namespace SecurityAdministration (HBASE-9206)):提供了另一个层面的多租户安全管理;

l Region服务器组(Region server groups (HBASE-6721)):一个命名空间或一张表,可以被固定到一组regionservers上,从而保证了数据隔离性。

1.1.命名空间管理

命名空间可以被创建、移除、修改。

表和命名空间的隶属关系在在创建表时决定,通过以下格式指定:

<namespace>:<table>

 

Example:hbase shell中创建命名空间、创建命名空间中的表、移除命名空间、修改命名空间

#Create a namespacecreate_namespace 'my_ns'            

#create my_table in my_nsnamespace create 'my_ns:my_table', 'fam'          

#drop namespacedrop_namespace 'my_ns'          

#alter namespacealter_namespace 'my_ns', {METHOD =>'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}        

1.2.预定义的命名空间

有两个系统内置的预定义命名空间:

hbase:系统命名空间,用于包含hbase的内部表

default所有未指定命名空间的表都自动进入该命名空间

Example:指定命名空间和默认命名空间

#namespace=foo and tablequalifier=bar create 'foo:bar', 'fam' #namespace=default and table qualifier=bar create 'bar', 'fam'

2.创建表

废话不多说,直接上样板代码,代码后再说明注意事项和知识点:

 

       Configuration conf = HBaseConfiguration.create();

        HBaseAdmin admin = new HBaseAdmin(conf);

        //create namespace named "my_ns"

        admin.createNamespace(NamespaceDescriptor.create("my_ns").build());

        

        //create tableDesc, with namespace name "my_ns" and table name "mytable"

        HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf("my_ns:mytable"));

        tableDesc.setDurability(Durability.SYNC_WAL);

 

        //add a column family "mycf"

        HColumnDescriptor hcd = new HColumnDescriptor("mycf");

        tableDesc.addFamily(hcd);

        admin.createTable(tableDesc);

        admin.close();  

 

关键知识点:

46.   必须将HBase集群的hbase-site.xml文件添加进工程的classpath中,或者通过Configuration对象设置相关属性,否则程序获取不到集群相关信息,也就无法找到集群,运行程序时会报错;

47.   HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf("my_ns:mytable"))代码是描述表mytable,并将mytable放到了my_ns命名空间中,前提是该命名空间已存在,如果指定的是不存在命名空间,则会报错org.apache.hadoop.hbase.NamespaceNotFoundException

48.   命名空间一般在建模阶段通过命令行创建,在java代码中通过admin.createNamespace(NamespaceDescriptor.create("my_ns").build())创建的机会不多;

49.   创建HBaseAdmin对象时就已经建立了客户端程序与HBase集群的connection,所以在程序执行完成后,务必通过admin.close()关闭connection;

50.   可以通过HTableDescriptor对象设置表的特性,比如:通过tableDesc.setMaxFileSize(512)设置一个region中的store文件的最大size,当一个region中的最大store文件达到这个size时,region就开始分裂;通过tableDesc.setMemStoreFlushSize(512)置region内存中的memstore的最大值设,当memstore达到这个值时,开始往磁盘中刷数据。更多特性请自行查阅官网API;

51.   可以通过HColumnDescriptor对象设置列族的特性,比如:通过hcd.setTimeToLive(5184000)设置数据保存的最长时间;通过hcd.setInMemory(true)设置数据保存在内存中以提高响应速度;通过 hcd.setMaxVersions(10)设置数据保存的最大版本数;通过hcd.setMinVersions(5)设置数据保存的最小版本数(配合TimeToLive使用)。更多特性请自行查阅官网API;

52.   数据的版本数只能通过HColumnDescriptor对象设置,不能通过HTableDescriptor对象设置;

53.   由于HBase的数据是先写入内存,数据累计达到内存阀值时才往磁盘中flush数据,所以,如果在数据还没有flush进硬盘时,regionserver down掉了,内存中的数据将丢失。要想解决这个场景的问题就需要用到WAL(Write-Ahead-Log),tableDesc.setDurability(Durability.SYNC_WAL)就是设置写WAL日志的级别,示例中设置的是同步写WAL,该方式安全性较高,但无疑会一定程度影响性能,请根据具体场景选择使用;

54.   setDurability(Durability d)方法可以在相关的三个对象中使用,分别是:HTableDescriptor,Delete,Put(其中Delete和Put的该方法都是继承自父类org.apache.hadoop.hbase.client.Mutation)。分别针对表、插入操作、删除操作设定WAL日志写入级别。需要注意的是,Delete和Put并不会继承Table的Durability级别(已实测验证)。Durability是一个枚举变量,可选值参见4.2节。如果不通过该方法指定WAL日志级别,则为默认USE_DEFAULT级别。

3.删除表

删除表没创建表那么多学问,直接上代码:

       Configuration conf = HBaseConfiguration.create();

        HBaseAdmin admin = new HBaseAdmin(conf);

        String tablename = "my_ns:mytable";

        if(admin.tableExists(tablename)) {

            try {

                admin.disableTable(tablename);

                admin.deleteTable(tablename);

            } catch (Exception e) {

                // TODO: handle exception

                e.printStackTrace();

            }

        }

        admin.close();  

说明:删除表前必须先disable表。

4.修改表

4.1.实例代码

(1)删除列族、新增列族

修改之前,四个列族:

hbase(main):014:0>describe 'rd_ns:itable'

DESCRIPTION                                                                                                   ENABLED

 'rd_ns:itable',{NAME => 'info', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW',REPLICATION_SCOPE => '0', V true

 ERSIONS=> '10', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL =>'2147483647', KEEP_DELETED_CELLS => 'false',

 BLOCKSIZE=> '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME =>'newcf', DATA_BLOCK_ENCODING => 'NONE

 ',BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE',VERSIONS => '10', TTL => '2147483647',

 MIN_VERSIONS=> '0', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', IN_MEMORY=> 'false', BLOCKCACHE => 'tr

 ue'},{NAME => 'note', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW',REPLICATION_SCOPE => '0', VERSIONS =>

  '10',COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647',KEEP_DELETED_CELLS => 'false', BLOCKSIZE

 =>'65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME =>'sysinfo', DATA_BLOCK_ENCODING => 'NONE', BLOOM

 FILTER=> 'ROW', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE', VERSIONS=> '10', TTL => '2147483647', MIN_VERS

 IONS=> '0', KEEP_DELETED_CELLS => 'true', BLOCKSIZE => '65536', IN_MEMORY=> 'false', BLOCKCACHE => 'true'}

1 row(s) in0.0450 seconds

 

修改表,删除三个列族,新增一个列族,代码如下:

       Configuration conf = HBaseConfiguration.create();

        HBaseAdmin admin = new HBaseAdmin(conf);

        String tablename = "rd_ns:itable";

        if(admin.tableExists(tablename)) {

            try {

                admin.disableTable(tablename);

                //get the TableDescriptor of target table

                HTableDescriptor newtd = admin.getTableDescriptor(Bytes.toBytes("rd_ns:itable"));

                

                //remove 3 useless column families

                newtd.removeFamily(Bytes.toBytes("note"));

                newtd.removeFamily(Bytes.toBytes("newcf"));

                newtd.removeFamily(Bytes.toBytes("sysinfo"));

                

                //create HColumnDescriptor for new column family

                HColumnDescriptor newhcd = new HColumnDescriptor("action_log");

                newhcd.setMaxVersions(10);

                newhcd.setKeepDeletedCells(true);

                

                //add the new column family(HColumnDescriptor) to HTableDescriptor

                newtd.addFamily(newhcd);

                

                //modify target table struture

                admin.modifyTable(Bytes.toBytes("rd_ns:itable"),newtd);

                

                admin.enableTable(tablename);

            } catch (Exception e) {

                // TODO: handle exception

                e.printStackTrace();

            }

        }

        admin.close();  

 

 

修改之后:

hbase(main):015:0>describe 'rd_ns:itable'

DESCRIPTION                                                                                                   ENABLED

 'rd_ns:itable',{NAME => 'action_log', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER =>'ROW', REPLICATION_SCOPE =>  true

 '0',COMPRESSION => 'NONE', VERSIONS => '10', TTL => '2147483647',MIN_VERSIONS => '0', KEEP_DELETED_CELLS => 'tr

 ue',BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'},{NAME => 'info', DATA_BLOCK_ENCODING => '

 NONE',BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '10',COMPRESSION => 'NONE', MIN_VERSIONS => '

 0',TTL => '2147483647', KEEP_DELETED_CELLS => 'false', BLOCKSIZE =>'65536', IN_MEMORY => 'false', BLOCKCACHE =>

 'true'}

1 row(s) in0.0400 seconds

逻辑很简单:

55.   通过admin.getTableDescriptor(Bytes.toBytes("rd_ns:itable"))取得目标表的描述对象,应该就是取得指向该对象的指针了;

56.   修改目标表描述对象;

57.   通过admin.modifyTable(Bytes.toBytes("rd_ns:itable"),newtd)将修改后的描述对象应用到目标表。

 

(2)修改现有列族的属性(setMaxVersions)

       Configuration conf = HBaseConfiguration.create();

        HBaseAdmin admin = new HBaseAdmin(conf);

        String tablename = "rd_ns:itable";

        if(admin.tableExists(tablename)) {

            try {

                admin.disableTable(tablename);

 

                //get the TableDescriptor of target table

                HTableDescriptor htd = admin.getTableDescriptor(Bytes.toBytes("rd_ns:itable"));

                HColumnDescriptor infocf = htd.getFamily(Bytes.toBytes("info"));

                infocf.setMaxVersions(100);

 

                //modify target table struture

                admin.modifyTable(Bytes.toBytes("rd_ns:itable"),htd);

                admin.enableTable(tablename);

            } catch (Exception e) {

                // TODO: handle exception

                e.printStackTrace();

            }

        }

        admin.close();  

 

5.新增、更新数据Put

5.1.常用构造函数:

(1)指定行键

publicPut(byte[] row)

参数:row 行键

 

(2)指定行键和时间戳

publicPut(byte[] row, long ts)

参数:row 行键,ts 时间戳

 

(3)从目标字符串中提取子串,作为行键

Put(byte[]rowArray, int rowOffset, int rowLength)

 

(4)从目标字符串中提取子串,作为行键,并加上时间戳

Put(byte[]rowArray, int rowOffset, int rowLength, long ts)

 

5.2.常用方法:

(1)指定列族、限定符,添加值

add(byte[]family, byte[] qualifier, byte[] value)  

 

(2)指定列族、限定符、时间戳,添加值

add(byte[]family, byte[] qualifier, long ts, byte[] value)

 

(3)设置写WAL(Write-Ahead-Log)的级别

public voidsetDurability(Durability d)

参数是一个枚举值,可以有以下几种选择:

l ASYNC_WAL : 当数据变动时,异步写WAL日志

l SYNC_WAL : 当数据变动时,同步写WAL日志

l FSYNC_WAL : 当数据变动时,同步写WAL日志,并且,强制将数据写入磁盘

l SKIP_WAL : 不写WAL日志

l USE_DEFAULT : 使用HBase全局默认的WAL写入级别,即SYNC_WAL

 

5.3.实例代码

(1)插入行

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

 

        Put put = new Put(Bytes.toBytes("100001"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("lion"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("address"), Bytes.toBytes("shangdi"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("30"));

       put.setDurability(Durability.SYNC_WAL);  

 

        table.put(put);

        table.close();  

 

(2)更新行

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        

        Put put = new Put(Bytes.toBytes("100001"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("lee"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("address"), Bytes.toBytes("longze"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("31"));

       put.setDurability(Durability.SYNC_WAL);  

 

        table.put(put);       

        table.close();  

注意:

58.   Put的构造函数都需要指定行键,如果是全新的行键,则新增一行;如果是已有的行键,则更新现有行。

59.   创建Put对象及put.add过程都是在构建一行的数据,创建Put对象时相当于创建了行对象,add的过程就是往目标行里添加cell,直到table.put才将数据插入表格;

60.   以上代码创建Put对象用的是构造函数1,也可用构造函数2,第二个参数是时间戳;

61.   Put还有别的构造函数,请查阅官网API。

 

(3)从目标字符串中提取子串,作为行键,构建Put

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        

        Put put = new Put(Bytes.toBytes("100001_100002"),7,6);

        put.add(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("show"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("address"), Bytes.toBytes("caofang"));

        put.add(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("30"));

        

        table.put(put);

        table.close();  

 

注意,关于:Put put = new Put(Bytes.toBytes("100001_100002"),7,6)

62.   第二个参数是偏移量,也就是行键从第一个参数的第几个字符开始截取;

63.   第三个参数是截取长度;

64.   这个代码实际是从100001_100002 中截取了100002子串作为目标行的行键。

6.删除数据Delete

      Delete类用于删除表中的一行数据,通过HTable.delete来执行该动作。

      在执行Delete操作时,HBase并不会立即删除数据,而是对需要删除的数据打上一个“墓碑”标记,直到当Storefile合并时,再清除这些被标记上“墓碑”的数据。

      如果希望删除整行,用行键来初始化一个Delete对象即可。如果希望进一步定义删除的具体内容,可以使用以下这些Delete对象的方法:

l 为了删除指定的列族,可以使用deleteFamily

l 为了删除指定列的多个版本,可以使用deleteColumns

l 为了删除指定列的指定版本,可以使用deleteColumn,这样的话就只会删除版本号(时间戳)与指定版本相同的列。如果不指定时间戳,默认只删除最新的版本

     下面详细说明构造函数和常用方法:

6.1.构造函数

(1)指定要删除的行键

Delete(byte[]row)

删除行键指定行的数据。

如果没有进一步的操作,使用该构造函数将删除行键指定的行中所有列族中所有列的所有版本

 

(2)指定要删除的行键和时间戳

Delete(byte[]row, long timestamp)

删除行键和时间戳共同确定行的数据。

如果没有进一步的操作,使用该构造函数将删除行键指定的行中,所有列族中所有列的时间戳小于等于指定时间戳的数据版本

注意:该时间戳仅仅和删除行有关,如果需要进一步指定列族或者列,你必须分别为它们指定时间戳。

 

(3)给定一个字符串,目标行键的偏移,截取的长度

Delete(byte[]rowArray, int rowOffset, int rowLength)

 

(4)

Delete(byte[]rowArray, int rowOffset, int rowLength, long ts)

 

6.2.常用方法

l Delete  deleteColumn(byte[] family, byte[]qualifier)    删除指定列的最新版本的数据。

l Delete  deleteColumns(byte[]family, byte[] qualifier)    删除指定列的所有版本的数据。

l Delete  deleteColumn(byte[] family, byte[]qualifier, long timestamp)    删除指定列的指定版本的数据。

l Delete  deleteColumns(byte[] family, byte[] qualifier,long timestamp)    删除指定列的,时间戳小于等于给定时间戳所有版本的数据。

 

l Delete  deleteFamily(byte[] family)   删除指定列族的所有列的所有版本数据。

l Delete deleteFamily(byte[] family, long timestamp)    删除指定列族的所有列中时间戳小于等于指定时间戳的所有数据。

l Delete deleteFamilyVersion(byte[] family, long timestamp)    删除指定列族中所有列的时间戳等于指定时间戳的版本数据。

 

l voidsetTimestamp(long timestamp)    为Delete对象设置时间戳。

 

6.3.实例代码

(1)删除整行的所有列族、所有行、所有版本

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        

        Delete delete = new Delete(Bytes.toBytes("000"));

        table.delete(delete);

        table.close();  

 

(2)删除指定列的最新版本

以下是删除之前的数据,注意看100003行的info:address,这是该列最新版本的数据,值是caofang1,在这之前的版本值是caofang:

hbase(main):007:0>scan 'rd_ns:leetable'

ROW                     COLUMN+CELL

 100001                 column=info:address, timestamp=1405304843114, value=longze

 100001                  column=info:age,timestamp=1405304843114, value=31

 100001                 column=info:name, timestamp=1405304843114, value=leon

 100002                 column=info:address, timestamp=1405305471343, value=caofang

 100002                  column=info:age,timestamp=1405305471343, value=30

 100002                 column=info:name, timestamp=1405305471343, value=show

 100003                 column=info:address, timestamp=1405390959464, value=caofang1

 100003                  column=info:age,timestamp=1405390959464, value=301

 100003                 column=info:name, timestamp=1405390959464, value=show1

3 row(s) in0.0270 seconds

 

执行以下代码:

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

 

        Delete delete = new Delete(Bytes.toBytes("100003"));

        delete.deleteColumn(Bytes.toBytes("info"), Bytes.toBytes("address"));

        

        table.delete(delete);

        table.close();  

 

然后查看数据,发现100003列的info:address列的值显示为前一个版本的caofang了!其余值均不变:

hbase(main):008:0>scan 'rd_ns:leetable'

ROW                     COLUMN+CELL

 100001                 column=info:address, timestamp=1405304843114, value=longze

 100001                  column=info:age,timestamp=1405304843114, value=31

 100001                 column=info:name, timestamp=1405304843114, value=leon

 100002                 column=info:address, timestamp=1405305471343, value=caofang

 100002                  column=info:age,timestamp=1405305471343, value=30

 100002                 column=info:name, timestamp=1405305471343, value=show

 100003                 column=info:address, timestamp=1405390728175, value=caofang

 100003                  column=info:age,timestamp=1405390959464, value=301

 100003                 column=info:name, timestamp=1405390959464, value=show1

3 row(s) in0.0560 seconds

 

(3)删除指定列的所有版本

接以上场景,执行以下代码:

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

 

        Delete delete = new Delete(Bytes.toBytes("100003"));

        delete.deleteColumns(Bytes.toBytes("info"), Bytes.toBytes("address"));

        

        table.delete(delete);

        table.close();  

 

然后我们会发现,100003行的整个info:address列都没了:

hbase(main):009:0>scan 'rd_ns:leetable'

ROW                     COLUMN+CELL

 100001                 column=info:address, timestamp=1405304843114, value=longze

 100001                  column=info:age,timestamp=1405304843114, value=31

 100001                 column=info:name, timestamp=1405304843114, value=leon

 100002                 column=info:address, timestamp=1405305471343, value=caofang

 100002                  column=info:age,timestamp=1405305471343, value=30

 100002                  column=info:name,timestamp=1405305471343, value=show

 100003                  column=info:age,timestamp=1405390959464, value=301

 100003                 column=info:name, timestamp=1405390959464, value=show1

3 row(s) in0.0240 seconds

 

(4)删除指定列族中所有列的时间戳等于指定时间戳的版本数据

为了演示效果,我已经向100003行的info:address列新插入一条数据

hbase(main):010:0>scan 'rd_ns:leetable'

ROW                     COLUMN+CELL

 100001                 column=info:address, timestamp=1405304843114, value=longze

 100001                  column=info:age,timestamp=1405304843114, value=31

 100001                 column=info:name, timestamp=1405304843114, value=leon

 100002                 column=info:address, timestamp=1405305471343, value=caofang

 100002                  column=info:age,timestamp=1405305471343, value=30

 100002                 column=info:name, timestamp=1405305471343, value=show

 100003                 column=info:address, timestamp=1405391883886, value=shangdi

 100003                  column=info:age,timestamp=1405390959464, value=301

 100003                 column=info:name, timestamp=1405390959464, value=show1

3 row(s) in0.0250 seconds

 

现在,我们的目的是删除info列族中,时间戳为1405390959464的所有列数据:

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        

        Delete delete = new Delete(Bytes.toBytes("100003"));

        delete.deleteFamilyVersion(Bytes.toBytes("info"), 1405390959464L);

        

        table.delete(delete);

        table.close();  

 

hbase(main):011:0>scan 'rd_ns:leetable'

ROW                     COLUMN+CELL

 100001                 column=info:address, timestamp=1405304843114, value=longze

 100001                  column=info:age,timestamp=1405304843114, value=31

 100001                 column=info:name, timestamp=1405304843114, value=leon

 100002                 column=info:address, timestamp=1405305471343, value=caofang

 100002                  column=info:age,timestamp=1405305471343, value=30

 100002                  column=info:name,timestamp=1405305471343, value=show

 100003                 column=info:address, timestamp=1405391883886, value=shangdi

 100003                  column=info:age,timestamp=1405390728175, value=30

 100003                  column=info:name,timestamp=1405390728175, value=show

3 row(s) in0.0250 seconds

 

可以看到,100003行的info列族,已经不存在时间戳为1405390959464的数据,比它更早版本的数据被查询出来,而info列族中时间戳不等于1405390959464的address列,不受该delete的影响。

7.获取单行Get

如果希望获取整行数据,用行键初始化一个Get对象就可以,如果希望进一步缩小获取的数据范围,可以使用Get对象的以下方法:

l 如果希望取得指定列族的所有列数据,使用addFamily添加所有的目标列族即可;

l 如果希望取得指定列的数据,使用addColumn添加所有的目标列即可;

l 如果希望取得目标列的指定时间戳范围的数据版本,使用setTimeRange

l 如果仅希望获取目标列的指定时间戳版本,则使用setTimestamp

l 如果希望限制每个列返回的版本数,使用setMaxVersions

l 如果希望添加过滤器,使用setFilter

下面详细描述构造函数及常用方法:

7.1.构造函数

Get的构造函数很简单,只有一个构造函数:Get(byte[] row) 参数是行键。

 

7.2.常用方法

l GetaddFamily(byte[] family) 指定希望获取的列族

l GetaddColumn(byte[] family,byte[] qualifier)  指定希望获取的列

l GetsetTimeRange(long minStamp,long maxStamp)  设置获取数据的时间戳范围

l GetsetTimeStamp(long timestamp) 设置获取数据的时间戳

l GetsetMaxVersions(intmaxVersions) 设定获取数据的版本数

l GetsetMaxVersions()  设定获取数据的所有版本

l GetsetFilter(Filter filter) 为Get对象添加过滤器,过滤器详解请参见:http://blog.csdn.net/u010967382/article/details/37653177

l voidsetCacheBlocks(booleancacheBlocks)  设置该Get获取的数据是否缓存在内存中

 

7.3.实测代码

测试表的所有数据:

hbase(main):016:0>scan 'rd_ns:leetable'

ROW                     COLUMN+CELL

 100001                 column=info:address, timestamp=1405304843114, value=longze

 100001                  column=info:age,timestamp=1405304843114, value=31

 100001                 column=info:name, timestamp=1405304843114, value=leon

 100002                 column=info:address, timestamp=1405305471343, value=caofang

 100002                  column=info:age,timestamp=1405305471343, value=30

 100002                 column=info:name, timestamp=1405305471343, value=show

 100003                 column=info:address, timestamp=1405407883218, value=qinghe

 100003                  column=info:age,timestamp=1405407883218, value=28

 100003                  column=info:name,timestamp=1405407883218, value=shichao

3 row(s) in0.0250 seconds

(1)获取行键指定行的所有列族、所有列最新版本数据

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        Get get = new Get(Bytes.toBytes("100003"));

        Result r = table.get(get);

        for (Cell cell : r.rawCells()) {

            System.out.println(

                    "Rowkey : "+Bytes.toString(r.getRow())+

                    "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                    "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))

                    );

        }

        table.close();  

代码输出:

Rowkey : 100003   Familiy:Quilifier : address   Value : qinghe

Rowkey : 100003   Familiy:Quilifier : age   Value : 28

Rowkey : 100003   Familiy:Quilifier : name   Value : shichao  

 

(2)获取行键指定行中,指定列的最新版本数据

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        Get get = new Get(Bytes.toBytes("100003"));

        get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));

        Result r = table.get(get);

        for (Cell cell : r.rawCells()) {

            System.out.println(

                    "Rowkey : "+Bytes.toString(r.getRow())+

                    "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                    "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))

                    );

        }

        table.close();  

代码输出:

Rowkey : 100003   Familiy:Quilifier : name   Value : shichao  

 

(3)获取行键指定的行中,指定时间戳的数据

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:leetable");

        Get get = new Get(Bytes.toBytes("100003"));

        get.setTimeStamp(1405407854374L);

        Result r = table.get(get);

        for (Cell cell : r.rawCells()) {

            System.out.println(

                    "Rowkey : "+Bytes.toString(r.getRow())+

                    "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                    "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))

                    );

        }

        table.close();   

 

代码输出了上面scan命令输出中没有展示的历史数据:

Rowkey : 100003   Familiy:Quilifier : address   Value : huangzhuang

Rowkey : 100003   Familiy:Quilifier : age   Value : 32

Rowkey : 100003   Familiy:Quilifier : name   Value : lily  

 

(4)获取行键指定的行中,所有版本的数据

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:itable");

        Get get = new Get(Bytes.toBytes("100003"));

        get.setMaxVersions();

        Result r = table.get(get);

        for (Cell cell : r.rawCells()) {

            System.out.println(

                    "Rowkey : "+Bytes.toString(r.getRow())+

                    "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                    "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))+

                    "   Time : "+cell.getTimestamp()

                    );

        }

       table.close();         

 

代码输出:

Rowkey : 100003   Familiy:Quilifier : address   Value : xierqi   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : address   Value : shangdi   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : address   Value : longze   Time : 1405417448414

Rowkey : 100003   Familiy:Quilifier : age   Value : 29   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : age   Value : 30   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : age   Value : 31   Time : 1405417448414

Rowkey : 100003   Familiy:Quilifier : name   Value : leon   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : name   Value : lee   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : name   Value : lion   Time : 1405417448414  

 

注意:

能输出多版本数据的前提是当前列族能保存多版本数据,列族可以保存的数据版本数通过HColumnDescriptor的setMaxVersions(Int)方法设置。

8.获取多行Scan

      Scan对象可以返回满足给定条件的多行数据。如果希望获取所有的行,直接初始化一个Scan对象即可。如果希望限制扫描的行范围,可以使用以下方法:

l 如果希望获取指定列族的所有列,可使用addFamily方法来添加所有希望获取的列族

l 如果希望获取指定列,使用addColumn方法来添加所有列

l 通过setTimeRange方法设定获取列的时间范围

l 通过setTimestamp方法指定具体的时间戳,只返回该时间戳的数据

l 通过setMaxVersions方法设定最大返回的版本数

l 通过setBatch方法设定返回数据的最大行数

l 通过setFilter方法为Scan对象添加过滤器,过滤器详解请参见:http://blog.csdn.net/u010967382/article/details/37653177

l Scan的结果数据是可以缓存在内存中的,可以通过getCaching()方法来查看当前设定的缓存条数,也可以通过setCaching(intcaching)来设定缓存在内存中的行数,缓存得越多,以后查询结果越快,同时也消耗更多内存。此外,通过setCacheBlocks方法设置是否缓存Scan的结果数据块,默认为true

l 我们可以通过setMaxResultSize(long)方法来设定Scan返回的结果行数。

 

      下面是官网文档中的一个入门示例:假设表有几行键值为"row1", "row2", "row3",还有一些行有键值 "abc1", "abc2", 和"abc3",目标是返回"row"打头的行:

HTable htable = ...      // instantiate HTable

Scan scan = new Scan();

scan.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("attr"));

scan.setStartRow( Bytes.toBytes("row"));                  // start key is inclusive

scan.setStopRow( Bytes.toBytes("row" +  (char)0)); // stop key is exclusive

ResultScanner rs = htable.getScanner(scan);

try {

  for (Result r = rs.next(); r != null; r = rs.next()) {

  // process result...

} finally {

  rs.close();  // always close theResultScanner!

}

 

8.1.常用构造函数

(1)创建扫描所有行的Scan

Scan()

 

(2)创建Scan,从指定行开始扫描

Scan(byte[] startRow)

参数:startRow行键

注意:如果指定行不存在,从下一个最近的行开始

 

(3)创建Scan,指定起止行

Scan(byte[] startRow, byte[] stopRow)

参数:startRow起始行,stopRow终止行

注意:startRow <=结果集 < stopRow

 

(4)创建Scan,指定起始行和过滤器

Scan(byte[] startRow, Filter filter) 

参数:startRow起始行,filter过滤器

注意:过滤器的功能和构造参见http://blog.csdn.net/u010967382/article/details/37653177

 

8.2.常用方法

Scan setStartRow(byte[] startRow) 设置Scan的开始行,默认结果集包含该行。如果希望结果集不包含该行,可以在行键末尾加上0。

Scan setStopRow(byte[]stopRow)  设置Scan的结束行,默认结果集不包含该行。如果希望结果集包含该行,可以在行键末尾加上0。

Scan setTimeRange(long minStamp,long maxStamp)  扫描指定时间范围的数据

Scan setTimeStamp(long timestamp) 扫描指定时间的数据

Scan addColumn(byte[] family,byte[] qualifier)  指定扫描的列

Scan addFamily(byte[] family)指定扫描的列族

Scan setFilter(Filter filter) 为Scan设置过滤器

Scan  setReversed(boolean reversed)  设置Scan的扫描顺序,默认是正向扫描(false),可以设置为逆向扫描(true)。注意:该方法0.98版本以后才可用!!

Scan setMaxVersions()  获取所有版本的数据

Scan setMaxVersions(intmaxVersions)  设置获取的最大版本数

void setCaching(int caching)  设定缓存在内存中的行数,缓存得越多,以后查询结果越快,同时也消耗更多内存

voidsetRaw(boolean raw)  激活或者禁用raw模式。如果raw模式被激活,Scan将返回所有已经被打上删除标记但尚未被真正删除的数据。该功能仅用于激活了KEEP_DELETED_ROWS的列族,即列族开启了hcd.setKeepDeletedCells(true)。Scan激活raw模式后,就不能指定任意的列,否则会报错

 

Enable/disable "raw" mode for this scan. If"raw" is enabled the scan will return all delete marker and deletedrows that have not been collected, yet. This is mostly useful for Scan on column families that have KEEP_DELETED_ROWS enabled. It is an errorto specify any column when "raw" is set.

hcd.setKeepDeletedCells(true);

 

8.3.实测代码

(1)扫描表中的所有行的最新版本数据

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:itable");

        

        Scan s = new Scan();

        ResultScanner rs = table.getScanner(s);

        for (Result r : rs) {

            for (Cell cell : r.rawCells()) {

                System.out.println(

                        "Rowkey : "+Bytes.toString(r.getRow())+

                        "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                        "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))+

                        "   Time : "+cell.getTimestamp()

                        );

            }

        }

        table.close();  

代码输出:

Rowkey : 100001   Familiy:Quilifier : address   Value : anywhere   Time : 1405417403438

Rowkey : 100001   Familiy:Quilifier : age   Value : 24   Time : 1405417403438

Rowkey : 100001   Familiy:Quilifier : name   Value : zhangtao   Time : 1405417403438

Rowkey : 100002   Familiy:Quilifier : address   Value : shangdi   Time : 1405417426693

Rowkey : 100002   Familiy:Quilifier : age   Value : 28   Time : 1405417426693

Rowkey : 100002   Familiy:Quilifier : name   Value : shichao   Time : 1405417426693

Rowkey : 100003   Familiy:Quilifier : address   Value : xierqi   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : age   Value : 29   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : name   Value : leon   Time : 1405417500485  

 

(2扫描指定行键范围,通过末尾加0,使得结果集包含StopRow

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:itable");

        Scan s = new Scan();

        s.setStartRow(Bytes.toBytes("100001"));

        s.setStopRow(Bytes.toBytes("1000020"));

        

        ResultScanner rs = table.getScanner(s);

        for (Result r : rs) {

            for (Cell cell : r.rawCells()) {

                System.out.println(

                        "Rowkey : "+Bytes.toString(r.getRow())+

                        "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                        "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))+

                        "   Time : "+cell.getTimestamp()

                        );

            }

        }

        table.close();  

代码输出:

Rowkey : 100001   Familiy:Quilifier : address   Value : anywhere   Time : 1405417403438

Rowkey : 100001   Familiy:Quilifier : age   Value : 24   Time : 1405417403438

Rowkey : 100001   Familiy:Quilifier : name   Value : zhangtao   Time : 1405417403438

Rowkey : 100002   Familiy:Quilifier : address   Value : shangdi   Time : 1405417426693

Rowkey : 100002   Familiy:Quilifier : age   Value : 28   Time : 1405417426693

Rowkey : 100002   Familiy:Quilifier : name   Value : shichao   Time : 1405417426693  

 

(3返回所有已经被打上删除标记但尚未被真正删除的数据

本测试针对rd_ns:itable表的100003行。

如果使用get结合setMaxVersions()方法能返回所有未删除的数据,输出如下:

Rowkey : 100003   Familiy:Quilifier : address   Value : huilongguan   Time : 1405494141522

Rowkey : 100003   Familiy:Quilifier : address   Value : shangdi   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : age   Value : new29   Time : 1405494141522

Rowkey : 100003   Familiy:Quilifier : name   Value : liyang   Time : 1405494141522  

 

然而,使用Scan强大的s.setRaw(true)方法,可以获得所有已经被打上删除标记但尚未被真正删除的数据。

代码如下:

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:itable");

        Scan s = new Scan();

        s.setStartRow(Bytes.toBytes("100003"));

        s.setRaw(true);

        s.setMaxVersions();

        

        ResultScanner rs = table.getScanner(s);

        for (Result r : rs) {

            for (Cell cell : r.rawCells()) {

                System.out.println(

                        "Rowkey : "+Bytes.toString(r.getRow())+

                        "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                        "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))+

                        "   Time : "+cell.getTimestamp()

                        );

            }

        }

        table.close();  

 

输出结果如下:

Rowkey : 100003   Familiy:Quilifier : address   Value : huilongguan   Time : 1405494141522

Rowkey : 100003   Familiy:Quilifier : address   Value :    Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : address   Value : xierqi   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : address   Value : shangdi   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : address   Value :    Time : 1405417448414

Rowkey : 100003   Familiy:Quilifier : address   Value : longze   Time : 1405417448414

Rowkey : 100003   Familiy:Quilifier : age   Value : new29   Time : 1405494141522

Rowkey : 100003   Familiy:Quilifier : age   Value :    Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : age   Value :    Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : age   Value : 29   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : age   Value : 30   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : age   Value : 31   Time : 1405417448414

Rowkey : 100003   Familiy:Quilifier : name   Value : liyang   Time : 1405494141522

Rowkey : 100003   Familiy:Quilifier : name   Value :    Time : 1405493879419

Rowkey : 100003   Familiy:Quilifier : name   Value : leon   Time : 1405417500485

Rowkey : 100003   Familiy:Quilifier : name   Value : lee   Time : 1405417477465

Rowkey : 100003   Familiy:Quilifier : name   Value : lion   Time : 1405417448414

 

(4结合过滤器,获取所有age在25到30之间的行

目前的数据:

hbase(main):049:0> scan 'rd_ns:itable'

ROW                                         COLUMN+CELL

 100001                                     column=info:address, timestamp=1405417403438, value=anywhere

 100001                                     column=info:age, timestamp=1405417403438, value=24

 100001                                     column=info:name, timestamp=1405417403438, value=zhangtao

 100002                                     column=info:address, timestamp=1405417426693, value=shangdi

 100002                                     column=info:age, timestamp=1405417426693, value=28

 100002                                     column=info:name, timestamp=1405417426693, value=shichao

 100003                                     column=info:address, timestamp=1405494141522, value=huilongguan

 100003                                     column=info:age, timestamp=1405494999631, value=29

 100003                                     column=info:name, timestamp=1405494141522, value=liyang

3 row(s) in 0.0240 seconds

 

代码:

       Configuration conf = HBaseConfiguration.create();

        HTable table = new HTable(conf, "rd_ns:itable");

 

        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);  

        SingleColumnValueFilter filter1 = new SingleColumnValueFilter(

                Bytes.toBytes("info"),

                Bytes.toBytes("age"),

                CompareOp.GREATER_OR_EQUAL,

                Bytes.toBytes("25")

                );

        SingleColumnValueFilter filter2 = new SingleColumnValueFilter(

                Bytes.toBytes("info"),

                Bytes.toBytes("age"),

                CompareOp.LESS_OR_EQUAL,

                Bytes.toBytes("30")

                );

        filterList.addFilter(filter1);

        filterList.addFilter(filter2);

        

        Scan scan = new Scan();

        scan.setFilter(filterList);

        

        ResultScanner rs = table.getScanner(scan);

        for (Result r : rs) {

            for (Cell cell : r.rawCells()) {

                System.out.println(

                        "Rowkey : "+Bytes.toString(r.getRow())+

                        "   Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+

                        "   Value : "+Bytes.toString(CellUtil.cloneValue(cell))+

                        "   Time : "+cell.getTimestamp()

                        );

            }

        }

        table.close();  

 

代码输出:

Rowkey : 100002   Familiy:Quilifier : address   Value : shangdi   Time : 1405417426693

Rowkey : 100002   Familiy:Quilifier : age   Value : 28   Time : 1405417426693

Rowkey : 100002   Familiy:Quilifier : name   Value : shichao   Time : 1405417426693

Rowkey : 100003   Familiy:Quilifier : address   Value : huilongguan   Time : 1405494141522

Rowkey : 100003   Familiy:Quilifier : age   Value : 29   Time : 1405494999631

Rowkey : 100003   Familiy:Quilifier : name   Value : liyang   Time : 1405494141522

 

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 紧身裙女老师 日本紧身裙女辅教师 紧身裙女辅教师2中文字幕神马 肉丝紧身裙女教师在线 紧身裙女辅教师观看 肉丝紧身裙女教师 紧身裙女辅教师2在线 紧身裙女教师在线 波多野女教师紧身裙在线 紧身裙女教师1 紧身裙女教师波多野 穿紧身裙女教师 紧身裙女教师在线看 日本紧身裙女辅教师2 紧身裙女辅教师2中文字幕下载 紧身裙女辅教师中文字幕下载 紧身裙女教师系列在线 紧身裙女辅教师2中文字幕 紧身裙女辅教师1在线 紧身裙女教师樱井步免费播放 十唱裙钗女 紧身裙女补教师辉月 穿紧身裙女老师 紧身裙女医生在线播放 紧身裙女教师直线观看 175紧身裙女教师 紧身裙放课后补女教师 早川奈里濑女教师窄裙 中文紧身裙女教师 紧身裙女辅教师231 紧身裙女教师系列 紧身裙女教师波多 紧身裙女 紧身裙女补教师2 窄裙痴女ol在公司内 紧身裙女补教师 雪纺裙 连衣裙雪纺裙 雪纺半身裙 打底裙连衣裙 碎花裙