Hbase初级到入门,只要您能读完,没收获你找我,好礼相送

Posted 职场spark哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hbase初级到入门,只要您能读完,没收获你找我,好礼相送相关的知识,希望对你有一定的参考价值。

      这篇关于hbase的文章是我通过多个公共账户和行业网站以及微博寻找到,对于初学者或者有点基础的可以跳着看一些我着重标红的,对于基本理解和面试都有很大的帮助。尤其后面的性能优化第十部分,不理解也得背下来。伴随着工作经验你可以慢慢回头再看,慢慢品味。

一、简介

1.列式存储数据库

以列为单位聚合数据,然后将列值顺序的存入磁盘,而传统数据库时为行式数据库,连续地存储整行

列式数据库:对于特定的查询,不需要所有的值,在分析型数据库最常见

1)经常会有人问 非关系型数据库比关系型数据库强在哪里??

我觉得一个可以从高并发说起:如何解决数据库的高并发

1.增加用于读取的从服务器,将读写分离

2.增加缓存,如Memcached,但无法保证数据的一致性

3.将数据分区到多个数据库中,运维成本高。分区主要描述了逻辑上水平划分数据的方案,特点是将数据分文件或分服务器存储,而不是连续存储。数据的分区是在固定范围内实施的:在传入数据之前,必须提前划分好数据的存储范围

恰好hbase 基于分布式存储到hdfs特性满足了高并发这个特点。


2.表、行、列和单元格

最基本的单位是列,一列或多列形成一行,并由唯一的行健(row key)来确定存储,一个表有若干行,其中每列可能有多个版本,在每个单元格中存储了不同的值

一行由若干列组成,若干列又构成了一个列族,一个列族的所有列存储在同一个底层文件中,这个存储文件叫做HFile,列族需要在表创建的时候就定义好,常见的引用列的格式为family:qualifer,列族的数量有限制,但是列的数量没有限制,列值也没有长度和类型的限制。这个特点一定要记住。

在Hbase中空值是没有任何消耗的,它们不占用任何的存储空间

每列或每个单元格的值都具有时间戳,默认由系统指定,也可以由用户显示设置

BigTable和Hbase的典型使用场景是webtable,存储从互联网中抓取的网页

行数据的存储操作都是原子的,可以读写任意数目的列,目前还不支持跨行事务和跨表事务

 

3.自动分区

Hbase中扩展和负载均衡的基本单元称为region,region本质上是以行健排序的连续存储的区间

 

4.存储API

API提供了建表、删表、增加列族和删除列族的操作,同时还提供了修改表和列族元数据的功能

scanAPI提供了高效遍历某个范围的行的功能

 

5.实现 (实现一定要记住)

每次更新数据时,都会先将数据记录在提交日志上(commit log)中,在Hbase中这叫预写日志(write-ahead log,WAL),然后才将这些数据写入内存中的memstore中,到超过给定的最大值值,就写到磁盘的HFile文件,HFile是不可被改变的

合并HFile机制

minor合并:将多个小文件(默认最小3个,最大10个)重写到数据较少的大文件中,每个HFile都是经过归类的,所以合并的速度很快,只受磁盘IO的影响

major合并:将一个region中一个列族的若干个HFile重写到一个新的HFile中,major合并能扫描所有的键值对,顺序重写全部的数据

Hbase初级到入门,只要您能读完,没收获你找我,好礼相送


二、客户端API:基础知识

1.概述

Hbase主要的客户端接口是有org.apache.hadoop.hbase.client包中的HTable提供的HTable类提供的,通过这个类用户可以向Hbase存储和检索数据

所有修改数据的操作都保证了行级别的原子性

创建HTable实例是有代价的,每个实例都需要扫描.META.表,以检查该表是否存在,是否可用,以及一些其他操作,因此推荐用户在每个线程(在开始时)只创建一次HTable实例,而后在客户端应用的生存期内复用这个对象,如果用户需要使用多个HTable实例,应考虑使用HTablePool类

Hable(Configuration conf,"tableName")

 

2.CRUD操作

2.CRUD操作

2.1 put方法:向Hbase存储数据

1)调用HTable的put方法格式:

void put(Put put)或void put(List<put> puts)

Put对象的构造方法如下

 

创建Put实例之后,就可以使用add方法

 

可以使用如下方法检查是否存在特定的单元格,而不需要遍历整个集合

 

通过客户端访问配置文件

当你调用任何一个静态create()方法时,代码会尝试使用当前的java classpath来载入两个配置文件hbase-default.xml和hbase-site.xml

 

Hbase的一个特殊功能是能为一个单元格存储多个版本的数据,这个版本使用一个时间戳,必须确保所有服务器的时间都是正确的,并且相互之间是同步的,默认情况下Hbase会保留3个版本的数据

若需要频繁的修改某些行,用户有必要创建一个RowLock实例来防止其他用户访问这些行

 

2)KeyValue类

数据和坐标都是以java的byte[]形式存储的,使用这种类型的目的是允许存储任意类型的数据

 

3)客户端的写缓冲区

每一个put操作实际上都是一个RPC操作,它将客户端数据传送到服务器后返回,这只适合小数据量的操作,Hbase的API配备了一个客户端的写缓冲区(write buffer),缓冲区负责收集put操作,然后调用RPC操作一次性将put送往服务器,全局交换机制控制着该缓冲区是否在使用,其方法如下

void setAutoFlush(boolean autoFlush)

默认情况下,客户端缓冲区是禁用的,可以通过将autoflush设置为false来激活缓冲区,通过setBufferSize(long size)来配置客户端写缓冲区的大小,默认大小是2M,为每一个用户创建的HTable实例都设置缓冲器大小十分麻烦,可以在hbase-site.xml中添加一个较大的预设值,配置如下

<property>

<name>hbase.client.write.value</name>

<value>20971520</value>

</property>

 

当需要强制把数据写到服务器端时,用flushCommit()函数

隐式刷写:在用户调用put()或setBufferSize()时触发,这两个方法都会将目前占用的缓冲区大小和用户配置的缓冲区大小进行比较,此外调用HTable的close()也会无条件地隐式刷写

用户可以通过访问ArrayList<Put> getWriteBuffer()来访问客户端写缓冲区的内容

如果用户只存储大单元格,那么客户端缓冲区的作用就不大了

4)Put列表

调用void put(List<put> puts)时,客户端先把所有的Put实例插入到本地写缓冲区中,然后隐式的调用flushCache(),如果其中有失败的Put实例(有些检查是在客户端完成的,如确认Put实例的内容是否为null或是否指定了列),那么后面的Put实例不会被添加到缓冲区,也不会触发刷写命令,当使用基于列表的Put调用时,用户需要特别注意:用户无法控制服务器执行put的顺序,如果要保证写入的顺序,需要小心地使用这个操作。

 

5)原子性操作compare-and-set

有一种特别的put调用,其能保证自身操作的原子性:checkAndPut(row,family,qualifier,value,Put put),这种有原子性的操作经常被用于账户结余,状态转换或数据处理等场景

有一种特别的检查通过checkAndPut()调用来完成,即只有在另外一个值不存在的情况下,才执行这个修改,要执行这种操作只需要将参数value设置为null

Hbase提供的compare-and-set操作,只能检查和修改同一行数据

 

2.2 get方法:客户端获取已存储数据的方法

Result get(Get get);

1)单行Get

构造Get实例:

Get(rowkey)

Get(rowkey,RowLock rowLock)

可以通过多种标准筛选目标数据:

Get addFamily(family):只能取得一个指定的列族

Get addColumn(family,qualifier):获取指定的列

Get setTimeRange(long minStamp,long masStamp)、

Get setTimeStamp(long timeStamp)

Get setMaxVersions(int maxVersions)

 

2)result类

get()返回的结果包含所有匹配的单元格数据,被封装成一个Result类

常用方法:

byte[] getValue(family,qualifier):取得特定单元格的值

byte[] value():返回第一列对应的最新单元格的值

byte[] getRow():返回行健

int size():检查是否有对应的记录

boolean isEmpty():检查是否有对应的记录

KeyValue[] raw()

List<KeyValue> list():用户可以方便的迭代数据

List<KeyValue> getColumn(family,qualifier)

KeyValue getColumnLatest(family,qualifier)

boolean containsColumn(family,qualifier)

NavigableMap<byte[],NavigableMap[],NavigableMap<Long,byte[]>>> getMap():把请求返回的内容都装入一个Map类实例中,可以遍历所有结果

 

3)get列表

Result[] get(List<Get> gets);

boolean exists(Get get):查看存储的数据是否存在

getRowOrBefore(row,family):

 

2.3 删除方法

1)单行删除

void delete(Delete delete)

Delete实例构造方法

Delete(rowkey)

Delete(rowkey,long timestamp,RowLock rowLock)

常用方法

Delete deleteFamily(family[,long timestamp])

Delete deleteColumns(family,qualifier[,long timestamp]):没指定timestamp,则删除所有版本

Delete deleteColumn(family,qualifier[,long timestamp]):没指定timestamp,则删除最新版本

 

2)Delete的列表

void delete(List<Delete> deletes)

 

3)原子性操作compare-and-delete

boolean checkAndDelete(row,qualifier,value,Delete delete)

 

2.4批处理操作

事实上许多基于列表的操作都是基于batch()方法是实现的。

方法:

void batch(List<Row> actions,Object[] results)

Object[] batch(List<Row> actions)

注:不可以将针对同一行数据的Put和Delete操作放在同一个批处理请求中

Row是Put、Get、Delete类的父类

当用户使用batch()功能时,Put实例不会被客户端写入缓冲区中缓冲,batch()请求是同步的,会把操作直接发送给服务器

两种方法相同点:

get、put、delete操作都支持,如果执行时出现问题,客户端将抛出异常并报告问题,都不适用客户端的写缓冲区

不同点:

void batch(acitons,results):能访问成功操作的结果,同时也可以获取远程失败的异常

Object[] batch(actions):只返回客户端异常,不能访问程序执行中的部分结果

注:在检查结果之前,所有的批处理操作都已经执行了:即使用户收到异常,其他的操作也都已经执行了

批量处理可以感知暂时性错误(如NoServingRegionException)会多次重试这个操作,用户可以通过调整hbase.client.retries.number配置项(默认为10)来增加或减少重试次数

 

2.5 行锁

region服务器提供一个行锁的特性,保证了只有一个客户端能获取一行的数据相应的锁,同时对该行进行修改。用户应该尽可能的避免使用行锁,如果必须使用,那么一定要节约占用锁的时间

Get不需要锁:服务器应用了一个多版本的并发控制机制来保证行级读操作,只有当一个变动被应用到整个行之后,客户端才能读出这个改动,当改动进行中时,所有的客户端读取到的数据都是将是所有列以前的版本

 

2.6 扫描

类似于数据库中的游标

ResultScanner getScanner(Scan scan)

ResultScanner getScanner(family)

ResultScanner getScanner(family,qualifier)

scan类的构造器

Scan()

Scan(startRow,Filter filter)

Scan(startRow)

Scan(startRow,stopRow)

注:如果用户只需要数据的子集,那么限制扫面的范围就能发挥Hbase的优势,如果不读取整个列族,那么整个列族的文件都不会读取,这就是列族存储架构的优势

1)ResultScanner类

扫描操作不会通过一次RPC请求返回所有匹配的行,而是以"行"为单位进行返回

Result next()

Result[] next(int nbRows)

void close()

要确保尽早释放扫描器实例,当使用完ResultScanner之后应调用它的close()方法,释放所有由扫描器控制的资源

就像行级锁一样,扫描器也使用同样的租约超时限制,保护其不被时效的客户端阻塞太久,通过如下配置

<property>

<name>hbase.regionserver.lease.period</name>

<value>120000</value>

</property>

2)缓存与批量处理

如果一次RPC请求可以返回多行数据,这样使用扫描器更有意义,可以由扫描器缓存实现,默认情况下,这个缓存是关闭的。可以从两个层面打开它

在表的层面,这个表的所有扫描器实例的缓存都会生效,用户使用一下的Htable方法设置表级的扫描器缓存:

void setScannerCaching(int scannerCaching),

int getScannerCaching():默认是1

在扫描器层面,这样只会影响当前的扫描实例,用户使用一下的Scan类的方法设置扫描器级的扫描器缓存:

void setCaching(int scannerCaching)

int getCaching():默认是1

此外用户可以修改整个集群的配置

<property>

<name>hbase.client.scanner.caching</name>

<value>10</value>

</property>

这样所有scan的实例的扫描器缓存就都被设置为10了

注:当数据量非常大的行,可能超过客户端进程的内存容量,解决方法:使用Scan类的批量操作

setBatch(int batch)

int getBatch()

缓存是面向行一级的操作,而批量是面向列一级的操作。

2.8 各种特性

1)Htable使用方法

void close():使用完一个HTable实例后,需要调用一次close(),它会刷写所有客户端缓冲的写操作

HTableDescriptor getTableDescripor():每个表都使用一个HTableDescriptor实例类来定义表结构

Map<HRegion,HServerAddress> getRegionInfo():获取某一行数据的具体位置信息

 

三、客户端API:高级特性

1.过滤器

Get和Scan两个类都支持过滤器,过滤器的基本接口叫做Filter,用户还可以通过继承Filter类实现自己的需求

 

1.1 比较运算符

 

1.2 比较器

 

1.3 Hbase提供的第一类:比较过滤器

用户创建实例时需要一个比较运算符和一个比较器实例

CompareFilter(CompareOp op,WriteableByteArrayComparable valueComparator)

· 行过滤器(父类为比较过滤器)RowFilter

· 列族过滤器(父类为比较过滤器)FamilyFilter

· 列名过滤器(父类为比较过滤器)QualifierFilter

· 值过滤器(父类为比较过滤器)ValueFilter

· 参考列过滤器(父类为比较过滤器)DependentColumnFilter:可以把它理解为一个ValueFilter和一个时间戳过滤器的组合

 

 

1.4 Hbase提供的第二类:专用过滤器

· 单列值过滤器:SingleColumnValueFilter,用一列的值决定是否一行数据被过滤

· SingleColumnValueFilter(family,qualifier,CompareOp op,value)

· 单列值排除过滤器:SingleColumnValueExcludeFilter

· 前缀过滤器:PrefixFilte(byte[] prefix):返回所有与前缀匹配的行

· 分页过滤器:PageFilter(int pageSize):用户可以使用这个过滤器对结果进行分页。当用户创建分页过滤器实例时,需要执行pageSize参数,注:物理上分离的服务器中并行执行过滤器操作时,需要注意,在不同的region服务器上并行执行的过滤器操作不能共享它们现在的状态和边界,因此每个过滤器都会在完成扫描前获取pageCount行的结果,这种情况可能使分页过滤器可能失效

· 行健过滤器:KeyOnlyFilter:只返回结果中的键

· FirstKeyOnlyFilter:访问一行中第一列,这种过滤器通常用在行数统计的应用场景中

· InclusiveStopFilter(byte[] endRow):扫描操作中的开始行被包含在结果中,但终止行被排除在外,使用这个过滤器时,用户可以将结束行包含在结果中

· 时间戳过滤器:TimestampFilter(List<Long> timestamps):当用户需要在扫描结果中对版本进行细粒度的控制时,可以使用该过滤器

· 列计数过滤器:ColumnCountGetFilter(int limit,int offset):限制每行最多取回多少列,不太适合扫描操作,适合get()方法

· 列分页过滤器:ColumnPaginationFilter(int limit,int offset):可以对一行的所有列进行分页,它将跳过所有偏移量小于offset的列,并包含之后所有偏移量在limit之前(包含limit)的列

· 列前缀过滤器:ColumnPrefixFilter(byte[] prefix)

· 随机行过滤器:RandomRowFilter(float chance)change在0到1之间

 

1.5 附加过滤器(应用在其他过滤器上)

跳转过滤器:SkipFilter(Filter filter):当被包装的过滤器遇到一个需要过滤的KeyValue时,用户可以扩展并过滤整行数据

全匹配过滤器:WhileMatchFilter

 

1.6 FilterList

需要多个过滤器共同限制返回到客户端的结果

FilterList(List<Filter> rowFilters)

FilterList(Operator op)

FilterList(List<Filter> rowFilters,Operator op)

参数rowFilters以列表的形式组合过滤器,参数operator决定了组合它们的结果,默认是为MUST_PASS_ALL,使用ArrayList可以保证过滤器的执行顺序与它们添加到列表中的顺序一致

 

1.7 自定义过滤器

用户可以实现Filter接口或者直接继承FilterBase类

 

 

 

用户自定义过滤器部署

编写代码-->打成jar包-->把文件发送到每个region服务器上-->修改hbase-env.sh文件如下

export HBASE_CLASSPATH="/hbase-book/target.aa.jar"-->重启hbase

 

2 计数器

2.1 简介

许多收集统计信息的应用有点击流和在线广告意见,这些需要被收集到日志文件中用户后续的分析,用户可以使用计数器做实时统计,从而放弃延迟较高的批处理操作

虽然用户可以一次更新多个计数器,但是它们必须属于同一行,更新多个计数器需要通过独立的API调用,即多个RPC请求

初始化计数器:用户不用初始化计数器,当用户第一次使用计数器时,计数器自动设置为0,即当用户创建一个新列时,计数器的值是0

在shell中操作计数器使用incr '<table>','<row>','<column>',[increment-value]与get_counter '<table>','<row>','<column>'

客户端API操作计数器

1)单计数器

只能操作一个计数器:用户需要自己设定列,方法由HTable提供的

long incrementColumnValue(row,family,qualifier,long amount)

long incrementColumnValue(row,family,qualifier,long amount,boolean writeToWAL)

2)多计数器

Result increment(Increment increment)

需要创建一个Increment实例,构造方法如下

Increment()

Increment(row)

Increment(row,RowLock rowLock)

向其中加入实际的计数器:addColumn(family,qualifier,long maxStamp)

 

3.协处理器

协处理器允许用户在region服务器上运行自己的代码,也就是允许用户执行region级的操作,并可以使用如触发器类似的功能

另一类适合使用协处理器的场景就是权限控制

协处理器框架提供了一些类,用户可以通过继承这些类扩展自己的功能,主要有两类,即observer和endpoint

observer:回调函数(也被称为钩子函数,hook)在一些特定时间发生时。这些事件包括用户产生的事件和服务器内部自动产生的事件

RegionObserver:用户可以用这种处理器处理数据修改事件,它们与表的region联系紧密

MasterObserver:可以被用作管理或DDL类型的操作,这些是集群级事件

WALObserver:提供控制WAL的钩子函数

 

endpoint:通过添加一些远程调用来动态扩展RPC协议,可以把它理解为与RDBMS中类似的存储过程。endpoint可以与observer的实现组合起来直接作用于服务器的状态

 

Coprocesspor类

所有协处理器的类都必须实现这个接口,它定义了协处理器的基本预定。提供了两个被应用于框架的枚举类Priority(SYSTEM,USER)和State

待续!!!!

 

4.HtablePool

为Hbase集群提供一个客户端连接池,用户可以通过以下任意一个构造器来创建池:

HTablePool()

HtablePool(Configuration conf,int maxSize)

HtablePool(Configuration conf,int maxSize,HTableInterfaceFactory fac)

HTableInterfaceFactory:用户可以创建自定义的工厂类,例如,为Htable实例使用特定的配置,或可以让实例完成一些初始化操作。如果用户想自己扩展HtableInterfaceFactory,则必须实现两个方法createHTableInterface(conf,tableName)和releaseHtableInterface

使用表实例池方式:getTable(String tableName);getTable(byte[] tableName);void putTable(HtableInterface table)

 

5.连接管理

每个HTable实例都需要建立和远程主机的连接,这些连接在内部使用HConnection类表示,被HConnectionManager类管理并共享。用户没必要和这两个类打交道,只需要创建一个Configuration实例,然后利用客户端API使用这些类。

Hbase内部使用键值映射来存储连接,使用Configuration实例作为键值映射的键。

HtablePool类,所有连接池中的Htable实例都自动共用一个提供的Configuration实例,共享它们的连接。

共享连接的缺点在于释放,如果用户不显示关闭连接,它将一直存在,直到客户端退出,建议用户不在需要使用Htable时主动调用其close(),释放资源,以下是显式清理连接方法

deleteConnection(Configuration conf,boolean stop);

deleteAllConnection(boolean stop)

 

四、客户端API:管理功能

1.模式定义

1)表:使用表的主要原因是控制表中的所有列以达到共享表内的某些特性的目的

表描述符的构造函数:

HTableDescriptor();

HTableDescriptor(String name);

HTableDescriptor(byte[] name);

HTableDescriptor(HTableDescriptor desc);

 

任意不相交的系统间的远程通信都使用到了Hadoop RPC框架,需要远程方法中参数都实现Writeable接口,进而能够序列化对象并远程传输

 

2)表属性

表名一定不能以".","-"开头,表名只能包含拉丁字母或数字、下划线、".","-"

 

列族:

void addFamily(HColumnDescriptor family)

boolean hasFamily(byte[] c)

HColumnDescriptor[] getColumnFamilies()

HColumnDescriptor getFamily(byte[] column)

 

文件大小限制:

long getMaxFileSize()

void setMaxFileSize(long maxFileSize)

如果一个列族的存储单元已使用的存储空间超过了大小限制,region将发生拆分,maxFileSize默认值是256M

这个参数只是大致的预期值,而在某些特殊条件下,文件大小可能超过这个预期值(一行数据不能跨region存储)

 

 

只读:默认所有的表都可写,对于特殊的表来说,只读参数有特殊的用途。调用setReadOnly(boolean readOnly)设置

 

memstore刷写大小:写操作会写入到写缓冲区,人后按照合适的条件顺序写入到一个新的存储文件(磁盘)中,可以通过setMemStoreFlushSize(long memstoreFlushSize),这个memstoreFlushSize的默认值是64MB

 

延迟日志刷写:

Hbase有两种将WAL保存到磁盘的方式,一种是延迟日志刷写,另一种不是,通过setDeferredLogFlush(boolean isDeferredLogFlush),isDeferredLogFlush的默认值为false

 

3)列族

列族定义了所有列的共享信息,并且可以通过客户端创建任意数量的列。定位到某个具体的列需要列族名和列名合并在一起,以:分隔,如family:qualifier

构造方法:

 

列族名:getName()或getNameAsString()

不能被重命名,通常的做法是新建一个列族,然后使用API从旧列族中复制数据到新列族

列族名不能以"."开头,也不能包含":"、""或ISO控制符

 

最大版本数:所有列族都限定了每个值能够保留的最大版本数,Hbase会移除超过最大版本数的数据,通过setMaxVersions(int maxVersion)设置,这个maxVersion默认为3.

 

压缩:

Hbase支持插件式的压缩算法,允许用户选择最合适的压缩算法

 

块大小:

在Hbase中,所有的存储文件都被划分为若干个小存储块,这些小存储块在get或scan操作时会加载到内存中,通过setBlockSize(int size)设置,默认大小是64KB

注:HFile的不同于HDFS中的块,HDFS提到的块是用于拆分大文件以提供分布式存储,且便于MapReduce框架进行并行计算的存储单元;而HBase中的块主要用于高效加载和缓冲数据

 

缓存块:

Hbase顺序的读取一个数据块到内存缓存中,这个参数默认是为true,每次读取的块都会缓存到内存中,但是如果顺序读取某个特定的列族,最好将这个属性设置为false,通过setBlockCacheEnabled(boolean blockCacheEnabled)方法设置

 

生存期TTL:

Hbase支持版本数据保存的时间,TTL设置了一个基于时间戳的临界值,内部管理会自动检查TTL的值是否达到上限,在major合并过程中时间戳被判定为超过TTL的数据会被删掉,可以通过setTimeToLive(int timestamp)方法设置,TTL的参数为秒,默认值是Integer.MAX_VALUE,理解为永久保留

 

在内存中:setInMemory(boolean inMemory)

将inMemory设置为true只是一种承诺,或者说高优先级。在正常数据读取过程中,块数据被加载到缓冲区中并长期驻留在内存中,除非堆压力过大,这个时候才会强制从内存中卸载这部分数据

 

布隆过滤器:

能够减少特定访问模式下的查询时间,由于这种模式增加了内存和存储的负担,这个模式默认为关闭状态

 

复制范围:Hbase提供跨集群同步的功能,本地集群的数据更新可以及时同步到其他集群,复制范围参数默认为0,即这个功能处于关闭状态。可以通过setScope(int scope)设置,1表示开启实时同步

 

2.HBaseAdmin

HBaseAdmin提供了创建表、列族、检查表是否存在,修改表结构和列族结构、以及删除表功能

构造方法:HbaseAdmin(Configuration conf)

 

1)表操作

建表:

void createTable(HTableDescriptor desc)

void createTable(HTableDescriptor desc,byte[] startKey,byte[] endKey,int numRegions)

void createTable(HTableDescriptor desc,byte[][] splitKeys)

void createTableAsync(HTableDescriptor desc,byte[][] splitKeys)

HTable类中的方法getStartEndKey()来获取所有region的边界

 

获取所有表的列表,以及判断是否存在

boolean tableExists(String tableName)

boolean tableExists(byte[] tableName)

HTableDescriptor[] listTables()

HTableDescriptor getTableDescriptor(byte[] tableName)

 

删除表:

void deleteTable(String tableName)

void deleteTable(byte[] tableName)

 

在删除表之前需要将表禁用,region服务器会先将内存中近期内还未提交的已修改的数据刷写到磁盘,然后关闭所有region,并更新这样表的元数据,将所有region标记为下线状态。

 

 

2)集群管理

允许用户查看集群当前的状态:

static void checkHBaseAvailable(Configuration)

ClusterStatus getClusterStatus()

void closeRegion(String regionname,String hostAndport)

注:所有可用表的region都应该是在线状态

void majorCompact(String tableNameOrRegionName)

 

五、可用的客户端

1.REST、Thrift,Avro介绍

它们都支持大量的语言,Protocol Buffer与Thrift和Avro最大的不同是它没有自己的RPC堆,而它生成的RPC定义需要被后来其他的RPC库使用,Hbase提供了REST、Thrift、Avro的辅助服务,它们可以实现成专门的网关服务,这些服务运行在共享或专用的机器上。Thrift和Avro都有各自的RPC实现,所以网关服务仅是在它们的基础上进行了封装,至于REST,HBase则采用了自己的实现,并提供了访问存储数据的路径。

每个请求使用一个服务而非建立一个连接的优势在于用户可以复用连接来获得最优性能。

2.交互

1)使用原生Java:直接使用HTable并通过原生的RPC调用与HBase服务器进行交互。

2)REST:

操作:基于REST服务的客户端是在能够与HBase通信之前需要先启动REST网关服务,使用hbase-daemon.sh start rest命令。REST服务提供了HBase表提供的所有操作,REST服务器返回的值都经过了base64Binary编码。

支持的格式:通过使用HTTP Content-Type和Accept头,调用者可以自动选择发送和接收信息的数据格式

Plain(text/plain):

XML(text/xml):默认的存储和查询格式是XML

JSON(application/json):curl -H "Accept:application/json" http://<servername>:testtable/%01%02%03/colfam1:col1

 

REST的java客户端:

REST服务器同样具有全面的java客户端API,位于org.apache.hadoop.hbase.rest.client包中,其中核心类为RemoteHTable和RemoteAdmin

 

3)Thrift

在使用Thrift之前需要安装Thrift,并且需要启动HBase提供的Thrift Server

启动Thrift命令:hbase-daemon.sh start thrift

 

4)Avro

在使用Thrift之前需要安装Avro,并且需要启动HBase提供的Avro Server

启动Avro命令:hbase-daemon.sh start Avro

 

3.批量处理客户端

1)Mapreduce:两种方式,原生java API或Clojure

2)Hive

Hive与Hbase之前的版本需要匹配,细微的RPC变化可能影响交互

3)pig

4)Cascading

 

4.shell

1)命令

引用名:命令行要求在使用表名和列名时需要通过单引号或双引号对其进行引用。

引用值:命令行支持二进制、八进制、十六进制的输入和输出,用户在引用时必须使用双引号

使用逗号分隔参数

Ruby散列属性:

一些命令中需要设置键值对属性,使用Ruby散列按照以下方式来完成

{'key1'=>'val1','key2'=>'val2',...}

 

DDL命令

 

alter 使用modifyTable()修改现有表结构

 

DML命令:

 

 

工具命令:

 

assgn 分配一个region到一台region服务器中

 

复制命令:

 

 

脚本:

可以使用Nagios或其他监控工具发送脚本

用户还以使用管道的形式运行命令

 

5.基于Web UI

1)master的web默认端口是60010,region服务默认端口是60030

 

六、与Mapreduce集成

1.类

InputFormat

InputFormat负责:拆分输入数据,同时返回一个RecordReader实例,定义了键值对象的类,并提供了next()遍历数据

就HBase而言,它提供了一组专用的实现,叫TableInputFormatBase,该实现的子类是TableInputFormat

 

mapper

HBase提供了一个TableMapper类,将键的类型强制转换为ImmutableBytesWritable类,同时将改制的类型强制转换为Result类型,TableMapper类没有实现具体的功能,它只是添加了键值对的签名

Reducer

HBase提供了一个TableReducer类

OutputFormat

Hbase提供了一个TableOutputFormat

 

 

2.支撑类

Mapreduce的支撑类与TableMapReduceUtil类一同协作在HBase上执行Mapreduce任务,它有一个静态方法能配置作业,病并使作业可以使用Hbase作为数据源或目标

 

3.在HBase上的Mapreduce

当运行Mapreduce作业所需库中的文件不是绑定在Hadoop或Mapreduce框架中时,用户就必须确保这些库文在作业之前已经可用,用户一般两个选择:在所有的任务节点上准备静态的库或直接提供作业所需的所有库

1)静态配置:

将jar文件复制到所有节点的常用路径中

将这些jar文件的完整路径写入hadoop-env.sh配置文件中,按照如右方式编辑HADOOP-CLASSPATH变量:HADOOP-CLASSPATH="<extra-entries>":HADOOP-CLASSPATH

重启所有的任务的NodeManager使变更生效

2)动态配置:Hadoop有一个特殊的功能,它可以读取操作目录中/lib目录下所包含的所有库的jar文件,用户可以使用此功能生成所谓的胖jar文件,胖jar文件使用maven的Maven Assembly插件

 

七、架构

1.数据查找和传输

1)B+树:它的叶节点项目链接并且有序,能够通过主键对记录进行高效的插入、查找以及删除,它表能为一个动态、多层并由上下界的索引,同时要注意维护每一段所包含的键数目

 

 

2)LSM树(log-structured merge-tree):输入数据首先存储在日志文件,这些文件内的数据完全有序,当有日志文件被修改时,对应的更新会被保存在内存中加速查询,修改数据文件的操作通过滚动合并完成。

 

 

2.存储

1)概览

 

注:HBase主要处理两种文件:一种是WAL文件,另一种是实际的数据文件,这两种文件都是由HRegionServer管理

 

2)读取数据流程

· 联系ZooKeeper子集群(quorum),通过ZooKeeper获取含有-ROOT-表的region服务器来查找行健

· 通过含有-ROOT-的region服务器可以查询到含有.meta.表中对应region服务器名,其中包内含请求的行健信息

· 以上信息都会缓存下来,只查找一次

· 通过查询.META.服务器来获取客户端查询的行健数据所在的region服务器,HBase会存储这次查询的信息

 

HRegionServer负责打开region,并创建一个对应的HRegion实例,它会为每个表的HColumnFamily创建一个Store实例,每个Store实例包含多个StoreFile(HFile的轻量级封装)和一个MemStore,一个HRegionServer共享一个HLog实例

 

3)写路径

· 用户发送HTable.put()请求到HRegion实例来大处理

· 决定数据是否需要写到HLog类实现的预写日志中,其中WAL是标准的Hadoop SequenceFile,并且存储了HLogKey实例,这些键包含序列号和实际数据

· 一旦数据写入到WAL中,数据就会放到MemStore中,同时检查MemStore是否已经满了,如果满了就刷写到磁盘中去。书写请求由另一个HRegionServer的线程处理,生成一个新的HFile

 

4)region拆分

· 当region的存储文件大于hbase.hregion.max.filesize大小或嫘祖层面的配置的大小时,region会被一分为二

· 为新region创建两个对应的文件,每个region为原region的一半,通过在父region中创建split目录来完成。

· 关闭该region,此后该region不再接受任何请求。

· region服务器通过split目录中设立必需的文件接受来准备新的子region,包括新region的目录和参考文件。

· 把这两个新region目录移动表目录

· .META.表中父region的状态会被更新,以表示其现在拆分的节点和子节点是什么

· 两个子region准备好后,将会被同一个服务器并行打开,打开的过程包括更新.META.表,同时也会初始化为两个region并对region中的内容进行合并,合并过程中替换引用文件之前会把父region的存储文件异步重写到两个子region中,在region的.tmp目录进行

· 最终父region被清理,master被告知查分的情况,并且可以由于负载均衡而把新region移动到其他的HRegionServer上

 

5)合并

minor合并:负责重写最后生成的几个文件到一个更大的文件中,文件数量有hbase.hstore.compaction.min决定,默认值是3,minor合并处理的最大文件数默认为10,用户可以通过hbase.hstore.compaction.max来配置

major合并:把所有文件压缩成一个单独的文件,在memstore被刷写到磁盘后出发检查,或shell命令major-compact后触发,配置相关参数

hbase.hregion.majorcompaction(默认为24小时)和hbase.hregion.majorcompaction.jitter(默认为0.2)

 

6)HFile

 

注:文件长度可变,唯一固定的块是File Info和Trailer,Trailer是指向其他快递的指针,块大小是由HColumnDescriptor配置的,该配置可以在创建表的时候由用户指定,默认大小时64K,每一个块都包含一个magic头部和一定数量的序列化的KeyValue实例

 

7)WAL

WAL存储了对数据的所有更改,实现了WAL的类叫HLog类,HLog类的一个特性就是跟踪修改,通过使用序列号来实现

 

3.ZooKeeper

HBase建立的znode列表,默认为/hbase,这个znode的名称由zookeeper.znode.parent属性决定,

以下是znode的列表以及他们的作用

/hbase/hbaseid:包括clusterID,与存储在HDFS上的hbase.id文件内容相同。

/hbase/master:包含服务器名称

/hbase/replication:包含副本信息

/hbase/root-region-server:包含-ROOT-region所在的region服务器的机器名,这个经常在region定位中使用

/hbase/rs:所偶region服务器的根节点,集群使用它来跟踪服务器异常

/hbase/shutdown:集群的启动事假和关闭时间

/hbase/table:当表被禁用,信息会被添加到这个znode之下

 

4.复制

HBase复制可以作为一种灾难恢复的方法,并且可以提供HBase层的高可用性

HBase复制中最基本的机构模式是"主推送",因为每个region服务器都有自己的WAL,所以很容易保存现在正在复制的位置

 

注:参与复制的集群可以不相等,主机群会通过随机分配尽量均衡从集群的负载

 

1)常规处理

客户端发送Put、Delete、或Increment到region服务器,这些请求包含的键值对会被region服务器转化为WALEdit,同时WALEdit会被复制程序检查,并以列族为单元复制数据。修改被添加到WAL中,并把实际数据添加到Memstore。

2)没有反馈的从集群

如果从集群的region服务器没有响应rpc请求,主集群的region服务器将会睡眠并按照配置的次数重试。如果从集群的region服务器还是不可用,主机全会重新选择一台其他的机器来提交修改

3)挑选要复制的目标集群

主集群的region服务器连接从集群的ZooKeeper群组,然后扫描/hbase/rs目录来发现所有可用的并随机选择一部分服务器来复制数据(默认是10%)

 

 

八、高级用法

1.rowkey设计

1)高表与宽表

HBase中的表可以设计为高表和宽表,前者指表中列少儿行多,后者则正好相反。用户应当尽量将需要查询的维度或信息存储在行健中,应为用它筛选的效率最高

此外,HBase只能按行分片,因此高表更有优势

 

2)时间序列:当处理流式事件时,最常用的就是按时间序列组织数据,这些数据会被有序的存储在一个特定的范围内,到时系统产生读写热点,解决这个问题的方法就是想办法将数据分散到所有的region服务器上,有很多中方法,例如在行键前价格,通常情况下如下选择

· salting方式:prefix=Long.hashCode(timestamp) % <number of regionservers>

· 字段变换/提升字段:如果用户设计的行键包含多个字段则可以调整它们的位置

· 随机化:rowkey=MD5(timestamp),随机化很适合每次只读取一行数据的应用,如果用户不需要连续扫描而只需要随机读取,可以考虑这种策略

 

 

时间顺序关系

每个列族下的列可以作为辅助索引单独进行排序,主要内容在主要的列族下,索引放在另外一个列族下,为了编码创建太多的列族,可以把所有辅助索引存储在有一个单独的列族下,同时列键的最左端使用索引ID这个前缀来表示不同的顺序

 

 

2.辅助索引

辅助索引存储了新坐标和现有坐标之间的映射关系,一些为可行的解决方案

由客户端管理索引:

把责任完全转移到应用层的典型做法是把一个数据表和一个查找/映射表结合起来,每当程序写数据表的时候,它也同时更新映射表(辅助索引表)。读数据时可以直接在主表进行查询,从辅助索引表中先查找原表的行键,再在原表中读取实际数据。优点:用户可以按照需求设计映射表。缺点:Hbase不能保证跨行操作的原子性

用户可以自由设计主索引和辅助索引之间的映射关系时,必须接受的缺点是用户需要实现所有存储和查找数据必需的方法

 

带索引的事务型HBase

开源的ITHBase,扩展了HBase,最核心的扩展是增加了保证所有辅助索引,提供了一个IndexedTableDescriptor,提供了数据表的辅助索引操作支持,大多数类被添加了索引支持功能的类替换了

 

带索引的HBase

IHBase是完全在内存中维护索引,索引永远都是同步的,不需要额外的事务控制,索引的定义由IdxIndexDescriptor类完成

 

3.搜索集成

使用任意关键字来搜索数据,满足这种需要往往是集成一个完整的搜索引擎

Lucene:独立于HBase使用的Lucene或其他派生类的解决方案可以通过Mapreduce建立索引。

HBasene:选择的方法是直接在HBase内部建立搜索索引,同时为用户提供Lucene的API,它把每个文档的字段、词存储在一个单独的行,同时包含这个词的文档储存在这一行的列中

 

九、监控集群

1.监控框架

每个HBase进程都会提供一系列监控指标,这些监控指标可以被各种监控API和工具使用,包括JMX和Ganglia。每种服务器都有多组监控指标,这些监控指标按子系统分组并隶属于一种服务器

HBase使用Hadoop的监控框架,并继承了其所有类和特性,这个框架基于MetricsContext接口来处理监控数据点的生成,并使用这些数据点监控和绘图

1)可用的实现列表:

GangliaContext:用来推送监控指标到Ganglia

FileContext:将监控指标写入磁盘上一个文件中

TimeStampingFileContext:将监控指标写入磁盘上一个文件中,但是为每个监控指标添加一个时间戳前缀

CompositeContext:允许为监控指标生成不止一个上下文

NullContext:监控指标框架的关闭选项,使用这个上下文时,不生成也不聚合监控指标

NullContextWithUpdateThread:不生成任何监控指标,但是启动聚合统计线程。这种上下文在通过JMX检索监控指标时使用

 

多重监控指标使用MetricsRecored分组,来描述一个具体的子系统。HBase使用这些组分别来保存master、region机器,以及其他服务器的统计信息,每个组都有唯一的名字:<context-name>.<record-name>.<metrics-name>。上下文有内置的定时器来触发并将监控指标推送至目标

 

2)各种指标类型:

整型值:(IV)

长整型值:(LV)

速率(R):一个代表速率的浮点型值,可以是每秒操作或者消息数

字符串:存储静态的,基于文本的信息。并用来报告HBase版本信息和构建时间

时间变化整型(TVI):上下文会维护一个单调递增累加计数器。框架使用这个方法对各种消息进行技术

时间变化长整型(TVL):用于增速较快的计数器

时间变化率(TVR):需要追踪操作数或消息的数量,以及完成操作所用的时间,通常用来计算一次操作完成的平均时间

持续型时间变化率(PTVR):添加了对持续的周期性的监控指标的必要的支持。

3)master提供的监控指标

 

 

4)region服务器监控指标:

块缓存监控指标

块缓存用来保存底层HFile从HDFS读取的存储块。count(LV)监控指标反映了当前缓存中保存的块数目,size(LV)监控指标时占用java堆空间大小,free(LV)监控指标时堆空间为缓存保留的可用空间,evicted(LV)反映了命中缓存总数与请求缓存总数的关系

块缓存追踪追踪缓存命中hit(LV)和缓存失效miss(LV)的数目,以及命中率hit radio(LV),其反映了命中缓存总数与请求缓存总数的关系。所有读操作使用缓存,不管用户是否制定过将使用的块保留在缓存中。使用setCacheBlocks()仅仅影响块的保留策略

合并监控

compaction size(PTVR)和compaction time(PTVR)分别代表需要合并的存储文件总大小和操作花费时间

compaction queue size:用来监测一个region服务器有多少文件当前正在排队等待合并

 

memstore监控指标

memstore size MB(IV):服务器上所有memstore总共占用的堆大小

flush queue zie(IV):将要被刷写的region的数目

 

存储监控指标:

store files(IV):所有存储文件的数目

store file index MB(IV):所有存储文件的块索引和元数据的总和大小

 

I/O监控指标:

fs read latency(TVR):文件系统的读延迟

fs write latency:写延迟

fs sync latency:统计了预写日志记录同步到文件系统的延迟

 

RPC监控指标

 

Ganglia

组成:

Ganglia元数据守护进程(gmetad):元数据守护进程安装在一个中心节点上,作为整个集群的管理节点。元数据守护进程从一个或多个监控守护进程拉去数据来获取整个集群的状态,然后使用RDTool将这些信息存放在一个用于轮询的时间序列数据库中。

Gangliaphp:展示统计信息

 

十、性能优化

1.垃圾回收优化

垃圾回收时master通常不会产生问题,只需要添加到region服务器的启动参数中。

用户可以通过向hbase-env.sh配置文件中添加HBASE_OPTS或者HBASE_REGIONSERVER_OPTS变量来设置垃圾回收的相关选项。后者仅影响region服务器进程,也是推荐的修改方式

指定新生代的方式:-XX:MaxNewSize=128m -XX:NewSize=128m或-Xmn128m

注意:默认值对于多数region服务器面对的负载还是太小,所以她必须增大

 

建议在JRE日志中输入垃圾回收的详细信息,通过添加以下JRE选项:

-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:$HBASE-HOME/logs/gc-hbase.log

该日志不会按日期定时滚动,需要手动处理(例如:使用基于crontab的每日滚动转存任务)

 

通过不同的JRE参数来指定不同的垃圾回收实现策略,推荐的值是:

-XX:+UseParNewGC and -XX:+UseConcMarkSweepGC

UseParNewGC:停止运行java进程二期清空年轻代堆

ConcMarkSweepGC:不停止java进程的情况下异步并行的完成工作

 

2.预拆分region

管理接口中createTable()方法或shell中的create命令可以接受列表形式提供的拆分行健作为参数

 

3.负载均衡:

master有一个内置的叫做均衡器的特性,在默认的情况下,均衡器每五分钟运行一次,可以通过hbase.balancer.period属性设置

均衡器有一个可以限制自身运行时间的上限,用户可以通过hbase.balance.max.balancing属性来设置,默认为均衡器间隔周期的一半

 

4.合并region

HBase集成了一个工具能够让用户在集群没有工作时河北那个两个相邻的region,工具为org.apache.hadoop.hbase.util.Merge

 

5.客户端API:最佳实践

1)禁止自动刷写:当有大量的写入操作时,使用setAutoFlush(false)方法,确认HTable自动刷写的特性已经关闭,如果禁用了自动刷写,add(Put)操作指导写缓冲区被填满时才会被送出,可以调用HTable的flushCommits()方法显式刷写数据,调用HTable的close()也会隐式调用flushCommits()

2)使用扫面缓存:如果HBase被用作一个MapReduce作业的输入源,请最好将作为MapReduce作业输入扫描器实例的缓存用setCaching()方法设置为比默认值1大得多的值,使用默认值意味着map任务处理每条记录都请求region服务器

3)限定扫描范围

当Scan用来处理大量行时注意哪些属性被选中了,从而限制扫描范围

4)关闭ResultScanner

如果用户忘记关闭由HTable.getScanner()返回的ResultScanner实例,则可能对服务器端造成影响,一定要在try/catche的finally块中关闭ResultScanner

5)块缓存用法

Scan实例通过setCacheBlock()方法来设置使用region服务器中的块缓存,如果MapReduce作业中使用扫描,这个方法应当被设为false,对于那些频繁访问行的行,建议使用块缓存

6)关闭Put上WAL

提高吞吐量方式,使用Put的writeToWAL(false)来关闭WAL,不过一旦region服务器故障就会丢失数据

 

6.配置

1)减少Zookeeper超时

默认在region服务器和ZooKeeper集群之间的超时时间是3分钟,通过zookeeper.session.timeout属性设置,在改变值之前,确认用户服务器上JVM的垃圾回收机制是受控的

2)增加处理线程

hbase.regionserver.handler.count属性定义了响应外部用户访问数据表请求的线程数,默认值10有些偏小,将这个值设置的高也有可能产生问题,并发的写请求造成压力

3)增加堆大小

如果用户使用更好的服务器,可以给HBase分配8G内存或更大,用户可以在hbase-env.sh文件中调整HBASE_HEAPSIZE的设置,master会默认1GB的堆运行,region服务器则会按照用户单独指定的堆空间运行

4)启动数据压缩

用户应当为存储文件启动压缩,尤其推荐使用Snappy或LZO压缩

5)增加region大小

默认region大小为256M,用户可以增加其大小,但是该参数的大小要仔细评估

6)调整块缓存大小

控制堆中块缓存大小的属性是一个浮动点数类型的百分比,默认值是20%,可以通过perf.hfile.block.cache.size属性改变这个值,看看是否存在许多块被换出的情况,如果存在,则可以考虑增加块缓存的大小。用户负载大多数为读请求是另一个增加缓存大小的原因。

7)调整memstore限制

内存存储占用的堆大小用hbase.regionserver.global.memstore.upperLimit属性来配置,默认值为40%,此外hbase.regionserver.global.memstore.lowerLimit属性(默认为35%)用户控制当服务器清空memstore之后剩余的大小,当用户主要在处理读请求时,可以考虑通知减少memstore的上线限来增加块缓存的空间

8)增加阻塞时存储文件数目

这个值通过hbase.hstore.blockingStoreFiles属性来设置的,它决定了当存储文件的数据达到阈值时,更新操作将会被阻塞,当应用经常遇到大负载的突发写请求时,用户可以稍微增加这个值来应对这种情况,其默认值是7

9)增加阻塞倍率

属性hbase.hregion.memstore.block.multiplier的默认值是2,当memstore达到属性multiplier乘以flush的大小限制时会阻止进一步更新

10)减少最大日志文件的限制

设置hbase.regionserver.maxlogs属性使得用户能够控制基于磁盘的WAL文件数目,进而控制刷写频率,该参数的默认值是32

 

负载测试

Hbase有自己的性能评价工具,名为PE,使用命令:hbase org.apache.hadoop.hbase.PerformanceEvaluation

YCSB:Yahoo的云服务基准测试系统也可用于对HBase集群进行超负荷测试

 

十一、集群管理

1.运维任务

1)减少节点

使用hbase-daemon.sh stop regionserver停止region服务器

如果关闭节点时负载均衡还在运行,则在复杂均衡和master恢复下线的region服务器之间可能存在竞争,要避免这种情况,使用shell命令:balance_switch false禁用负载均衡

HBase0.90.2之后引入了一种可以让region服务器逐渐减少其负载并停止服务的方法,使用graceful_stop.sh脚本来完成

用户也可以用graceful_stop.sh脚本来重启服务器,并将之前属于它的region移回原味(用户可能选择后者以保持数据的局部性),最简单的滚动重启可以使用如下命令(确认之前已经关闭负载均衡):

for i in 'cat conf/regionservers|sort;

do ./bin/graceful_stop.sh --restart --reload --debug $i;

done $> /tmp/log.txt &

 

2.数据任务

1)导入/导出

HBase发布了一些有用的工具,其中两个支持导入和导出MapReduce作业。这些工具包含在HBase的JAR文件中,用户可以通过hadoop jar命令来获得这些工具,使用格式:hadoop jar $HBASE_HOME/HBASE-0.91.0-SNAPSHOT.jar



                   



 


以上是关于Hbase初级到入门,只要您能读完,没收获你找我,好礼相送的主要内容,如果未能解决你的问题,请参考以下文章

数据库,从B+树讲到索引优化,听不明白你找我

史上最牛的降低论文重复率的方法!!!(查重率降不下来你找我)

Hbase 之 Shell 初级

手把手教你MySQL主从复制,配置不成功你找我!

万字图文 | 聊一聊 ReentrantLock 和 AQS 那点事(看完不会你找我)

万字图文 | 聊一聊 ReentrantLock 和 AQS 那点事(看完不会你找我)