Hbase基本操作

转载自:bbs.byr.cn/#!article/buptAUTA/105?p=1


Hbase基本操作
  
Hbase中主要的客户端接口是HTable类,HTable提供了对数据的所有CRUD操作。需要注意的是由于创建HTabe实例比较耗时, 所以在实际使用中最好创建单例模式的HTable实例,不过如果需要多个HTable实例的话,可以考虑使用HBase的HTablePool特性(下面后讲到)。Hbase不提供直接的update操作。由于Hbase中数据存储有版本支持。所以如果需要update一条记录,一般是通过put操作,这样历史版本会在Compaction操作中被合并掉,这样就间接实现了更新。(在MemStore中有一个变量MemstoreTS,该变量是随put操作而递增的。比如首先往列A,timeStamp为T1上put一条数据data1,假设此时MemstoreTS为1;之后如果想更新这条数据,只需要往列A,timeStamp为T1上put一条数据data2,此时MemstoreTS为2,Hbase会自动会将MemstoreTS大的排在前面。MemstoreTS小的在Compaction过程中就被过滤掉了。)
  
1)put操作
  
Put操作就是讲数据插入到Hbase中。有两种模式,一种是对单行的操作(single put);还有一种是对多行的操作(List of put)。针对单行操作的方式如下:
  
  
1、创建put实例有如下构造函数:需要用户指定某行,用户也可以设定时间戳作为版本标示。此外,用户还可以加入自定义的行锁,以防其它用户或者其它线程在变更期间访问此行的数据。
  
      Put(byte[] row)
  
      Put(byte[] row, RowLock rowLock)
  
      Put(byte[] row, long ts)
  
      Put(byte[] row, long ts, RowLock rowLock)
  
在Hbase中参数的传递大多是byte数组类型。Hbase提供了许多静态方法将java类型转换成byte数组类型。如下:
  
      static byte[] toBytes(ByteBuffer bb)
  
      static byte[] toBytes(String s)
  
      static byte[] toBytes(boolean b)
  
      static byte[] toBytes(long val)
  
      static byte[] toBytes(float f)
  
      static byte[] toBytes(int val)
  
2、一旦创建好put实例后,就可以通过put类提供的方法插入数据了。插入数据的操作需要指定列族,所在列等。如下:
  
      Put add(byte[] family, byte[] qualifier, byte[] value)
  
      Put add(byte[] family, byte[] qualifier, long ts, byte[] value)
  
      Put add(KeyValue kv) throws IOException
  
3、put组装完成后,就可以通过HTable提供的void put(Put put)throws IOException完成数据的插入操作。
  
如果需要对多行进行put操作,可以组装一系列的put实例,然后调用HTable提供的void put(List puts) throws IOException来完成多行插入操作。不过需要指出的是:如果在这多个Put实例中存在一个put实例有误(比如:往一个不存在的列族中插入数据),那么该put实例会报错,但是不影响其他的put实例。跟后面的get操作有点区别。
  
此外,Hbase还提供了一个原子型的put操作:Atomic compare-and-set ,方法如下:boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,byte[] value, Put put) throws IOException。只有校验成功后才会完成put操作.
  
需要注意的是,因为每次的put操作相当于一个RPC,将数据从客户端传递到服务端并返回。如果你的应用中RPC非常频繁,比如一秒内成千上万次,可能会有隐患。解决的办法就是尽量降低RPC次数,Hbase提供了一个嵌入的客户端写缓存器(Client-side Write Buffer)。它会缓存所有的put操作,然后再一次性提交。默认情况下Client-side Write Buffer是没有激活的。用户可以在创建HTable的时候通过调用table.setAutoFlush(false)方法来激活它。并且可以通过isAutoFlush()来检查是否已经激活。默认是true,表示一旦有put操作会立即发送到服务器端。当你想将所有put操作提交到服务器端时,可以调用flushCommits()操作。它会将缓存器中所有变更提交到远程服务器。Client-side Write Buffer还会自动对buffer中的所有变更进行分组,同一个HRegionServer的分到同一个组。这样每个HRegionServer通过一个RPC传送.
  
  
2)get操作
  
Get操作就是从服务器端获取数据。跟put操作一样,get操作也分为两种模式,一种是对单行的get操作(single get),另一种是对多行进行检索操作(List of gets)。
  
1、HTable提供的get方法如下:其返回值为Result类,该类包含了列族,列,keyvalue,
  
RowKey等信息。该类提供的丰富的方法供用户获取返回的各种信息。
  
Result get(Get get) throws IOException
  
2、Get类的构造函数如下,需要用户传入指定的行及行锁等参数。
  
       Get(byte[] row)
  
       Get(byte[] row, RowLock rowLock)
  
3、 一旦创建的get实例后,用户可以调用Get类提供的如下方法来框定你需要检索的数据。如下:用户可以指定列族,列,时间戳,最大版本号等。如果不设置版本号,默认是1,表示最大的版本。
  
       Get addFamily(byte[] family)
  
       Get addColumn(byte[] family, byte[] qualifier)
  
       Get setTimeRange(long minStamp, long maxStamp) throws IOException
  
       Get setTimeStamp(long timestamp)
  
       Get setMaxVersions()
  
       Get setMaxVersions(int maxVersions) throws IOException
  
跟List of put 类似,对于多行的检索操作,HTable也提供了类似的如下方法:用户只要创建多个get实例,就可以通过如下方法获取需要的数据。不过需要注意的是:跟List of put不同的是,如果Get实例列表中只要存在一个Get实例有误(比如get一个不存在的列族的值),那么整体就会抛出一个异常.
  
       Result[] get(List gets) throws IOException
  
  
3)delete操作
  
Delete操作也类似,HTable提供了两种方法,支持单个delete实例和多个delete实例的操作。如下:
  
  
      void delete(Delete delete) throws IOException
  
      void delete(List deletes) throws IOException
  
1、相应的delete实例构造函数有:
  
      Delete(byte[] row)
  
      Delete(byte[] row, long timestamp, RowLock rowLock)
  
2、如果你需要添加一些限制条件,可以使用delete类提供的相关方法,支持指定列族,列,时间戳等。如果你指定了一个时间戳,则表示小于等于该时间戳的时间将被删除。如果指定了列和行号,但没有指定时间戳,则默认会删掉版本号最大的那个值。
  
       Delete deleteFamily(byte[] family)
  
       Delete deleteFamily(byte[] family, long timestamp)
  
       Delete deleteColumns(byte[] family, byte[] qualifier)
  
       Delete deleteColumns(byte[] family, byte[] qualifier, long timestamp)
  
       Delete deleteColumn(byte[] family, byte[] qualifier)
  
       Delete deleteColumn(byte[] family, byte[] qualifier, long timestamp)
  
       void setTimestamp(long timestamp)
  
3、当使用List of delete时,如果有一个delete实例出错,那么会抛出异常。而且delete的实例列表中只会存在那个出问题的delete实例。Delete也支持原子型的Compare-and- Delete,如下:
  
boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,byte[] value, Delete delete) throws IOException
  
  
4)Batch操作
  
Hbase还支持批量操作。其实上面所谈到的List of puts,gets,deletes都是基于Batch操作来的。不过List of puts,gets,deletes逐渐会被废弃。推荐使用Batch操作。HTable提供的batch操作方法如下:参数中Row类是Put,Delete,Get类的父类。表示用户可以同时传入put,get及delete实例操作。不过在一个batch中,最好不要同时传入针对同一行的put和delete实例。
  
  
(1) void batch(List actions, Object[] results) throws IOException, InterruptedException
  
(2)  Object[] batch(List actions) throws IOException, InterruptedException上面这两个batch方法比较类似,但有比较大的区别。第一个batch方法需要用户传递一个数组,该数组用来填充batch操作中所有成功的操作的结果集。如果没有指定这个数组,比如第二个方法。一旦batch操作中某一个实例出现问题,那么Hbase只会抛出一个异常。那些成功的操作的结果并不会返回。而第一个方法则会将那些成功的操作的结果集返回给用户。
  
此外Batch操作不支持Client-side write buffer,Batch方法是同步的,会直接将其包含的操作发往服务器。这点需要注意!
  
Batch操作返回的结果可能的结果有如下几种:
  
1、null:表示那个操作操作连接远程服务器失败。
  
2、Empty Result:put和delete操作的返回结果,表示操作成功。
  
3、Result:get操作的返回结果集
  
4、Throwable:异常结果
  
  
5)Scan操作
  
Scan操作类似于传统的RDBMS中的游标的概念。其目的跟get一样,也是检索服务器端数据。Hbase也提供了一个Scan类。由于Scans类似于迭代器,所以你需要通过getScanner()方法获取。HTable提供了如下方法:如果你看了源码就会知道,后面那两个方法其实是先创建一个scan实例,并加入传入的参数,然后再调用第一个方法。
  
       ResultScanner getScanner(Scan scan) throws IOException
  
       ResultScanner getScanner(byte[] family) throws IOException
  
       ResultScanner getScanner(byte[] family, byte[] qualifier) throws IOException
  
1、Scan类提供了多个构造函数,如下:startRow和stopRow是左闭右开的。从构造函数中可以看出,用户只需要指定rowKey的范围,或者添加相应的过滤器,Hbase能够自动检索你指定的RowKey的范围的数据。如果没有指定startRow,默认从第一行开始.
  
       Scan()
  
       Scan(byte[] startRow, Filter filter)
  
       Scan(byte[] startRow)
  
       Scan(byte[] startRow, byte[] stopRow)
  
2、当创建好Scan实例后,如果想添加更多的限制条件,可以通过调用Scan提供的如下方法:允许添加列族,列,时间戳等.
  
      Scan addFamily(byte [] family)
  
      Scan addColumn(byte[] family, byte[] qualifier)
  
      Scan setTimeRange(long minStamp, long maxStamp) throws IOException
  
      Scan setTimeStamp(long timestamp)
  
      Scan setMaxVersions()
  
      Scan setMaxVersions(int maxVersions)
  
GetScanner()方法返回的是一个ResultScanner实例。需要注意的是:如果结果集存在多行,Scans并不会一次性将所有行在一个RPC里面传送给客户端,而是基于一行一行传送。这样做主要是因为多行需要耗费大量时间。
  
ResultScanner类包装了Result类将其每行结果以迭代的方式输出,使得Scan操作类似于get操作。此外ResultScanner类提供了如下方法供用户进行迭代使用:用户可以选择一次返回一行或者多行。不过不要认为是服务器端一次性返回多行。其实是客户端循环调用nbRows 次next()方法而已。服务器端在一个RPC里面还是只传送一行数据。这个确实有点影响心情,但Hbase就喜欢恶心下你,不过它也提供的相应的解决办法:Scanner Caching,默认是关闭的。
  
       Result next() throws IOException
  
       Result[] next(int nbRows) throws IOException
  
       void close()
  
close()方法表示释放ResultScanner实例。因为ResultScanner实例持有了一定的资源,如果不及时释放,可能随着时间推移会占用很大的内存空间。此外,close()操作最好放在finally模块,原因你懂得!
  
  
四、Hbase特性
  
HBase提供了许多赏心悦目的特性。如Filters,Counters,Coprocessors,Compaction,HTablePool等。
  
1)Filters
  
当你通过Scan或者Get操作检索数据时,会发现Scan和Get只支持基于RowKey,列族,列,时间戳等粗粒度的检索。如果用户想基于Key或者Value或者正则表达式等作为查询条件进行查询的话,Scan和Get是没办法做到的。而Filter就是干这事的。Hbase提供了一系列的Filters,用户只要实现Filter,也可以自定义Filters。
  
  
需要说明的是Hbase提供的这些Filters都是配置在客户端,但应用在服务器端,也叫做Predicate push-down。(比如用户在进行Scan操作时可以传入Filter,序列化后传送到服务器端,HRegionServer就会将其反序列化,并应用到内部Scanner)。这样可以有效减少数据传输带来的网络开销。
  
需要注意的是:Filters的通用约定是过滤掉你不需要的数据,而不是用来指定你需要的数据。不过凡是继承CompareFilter过滤器的Filter,其作用刚好相反,用来指定你需要的数据。
  
Hbase提供的Filters有:
  
Ⅰ. Comparison Filters
  
Compartison Filters是基于比较的过滤器。定义如下:
  
CompareFilter(CompareOp    valueCompareOp,WritableByteArrayComparable valueComparator)
  
该构造器有两个特定的参数,一个是比较运算符,另一个是比较器。
  
A、常见的比较运算符有:
  
LESS,LESS_OR_EQUAL,EQUAL,NOT_EQUAL,GREATER_OR_EQUAL,GREATER,NO_OP。前面几个运算符根据名字定义就能判断其意思,最后一个是NO_OP,表示排除任何数据。
  
B、常见的比较器有:其中NullComparator是判断给定的值是否为空或者非空。最后三个比较器只能搭配使用EQUAL,NOT_EQUAL比较运算符,返回0表示匹配,1表示不匹配。
  
      BinaryComparator
  
      BinaryPrefixComparator
  
      NullComparator
  
      BitComparator
  
      RegexStringComparator
  
      SubstringComparator
  
C、基于Comparison Filter的过滤器有好多种,比如:
  
      1、RowFilter
  
      2、FamilyFilter
  
      3、QualifierFilter
  
      4、ValueFilter
  
      5、DependentColumnFilter
  
(1)   RowFilter过滤器顾名思义就是根据RowKey来过滤数据。所以RowFilter中的比较运算符和比较器参数都是基于RowKey来比较的。比如如下Filter表示RowKey包含-4的数据。
  
Filter filter = new RowFilter(CompareFilter.CompareOp.EQUAL,new SubstringComparator("-4"))。
  
(2)   FamilyFilter过滤器跟RowFilter类似,不过FamilyFilter是基于ColumnFamily的比较。
  
QualifierFilter和ValueFilter过滤器也类似,分别是基于列和数值的比较。
  
(3) DependentColumnFilter过滤器稍微复杂一点。它可以说是timeStamp Filter和ValueFilter的结合。因为DependentColumnFilter需要指定一个参考列,然后获取跟改参考列有相同时间戳的所有列,再在此基础上获取满足ValueFilter的列值。构造函数如下:用户可以根据自己喜好省略valueFilter或者通过设置dropDependentColumn为true省略timestamp Filter。不过需要注意的是:此过滤器不能跟Scan中的Batch操作结合使用。
  
A、DependentColumnFilter(byte[] family, byte[] qualifier)
  
B、DependentColumnFilter(byte[] family, byte[] qualifier,boolean dropDependentColumn)
  
C、DependentColumnFilter(byte[] family, byte[] qualifier,boolean dropDependentColumn, CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)
  
  
Ⅱ. Dedicated Filters
  
专有的一些过滤器,Hbase提供了许多个性化的专有过滤器。常见的Dedicated Filters有:
  
A、SingleColumnValueFilter
  
B、SingleColumnValueExcludeFilter
  
C、PrefixFilter
  
D、PageFilter
  
E、KeyOnlyFilter
  
F、FirstKeyOnlyFilter
  
G、InclusiveStopFilter
  
H、TimestampsFilter
  
I、ColumnCountGetFilter
  
J、ColumnPaginationFilter
  
K、ColumnPrefixFilter
  
L、RandomRowFilter
  
(1)   如果你想分页获取数据,可以通过PageFilter来完成。ColumnPaginationFilter跟PageFilter类似,只不过PageFilter是基于行的分页,而ColumnPaginationFilter是基于列的分页。如:
  
ColumnPaginationFilter(int limit, int offset),表示获取从offset列开始的连续limit列的数据。
  
(2) 如果只想获取每一行的第一列的值,那么FirstKeyOnlyFilter是不错的选择。此外,因为前面提到的Scan操作需要用户指定一个startRow和EndRow,其中这两个参数时左闭右开区间的。如果想EndRow也包含,可以通过InclusiveStopFilter来解决。如下:获取从Row5至Row10的数据
  
。不过因为Hbase是字典排序的,所以得到的结果中可能会包含Row51,Row52等这些行的数据。
  
Filter filter = new InclusiveStopFilter(Bytes.toBytes("row-9"));
  
Scan scan = new Scan();
  
scan.setStartRow(Bytes.toBytes("row-5"));
  
scan.setFilter(filter);
  
ResultScanner scanner = table.getScanner(scan);
  
(3) 如果想获取某个版本的所有数据。可以通过TimestampsFilter来设置,用户需要传入版本号。如下:
  
TimestampsFilter(List timestamps)
  
(4) PrefixFilter和ColumnPrefixFilter都是基于前缀的过滤器,不过PrefixFilter是基于行的前缀过滤,而后者是基于列的前缀过滤。
  
(5) RandomRowFilter是基于随机行的过滤器,用户需要指定一个在0到1之间的随机数,构造函数如下:如果chance大于1,则会返回所有行。如果小于0,则过滤掉所有行。
  
RandomRowFilter(float chance)
  
  
Ⅲ. Decorating Filters
  
Decorating Filters称为装饰型的过滤器。它的作用是为其他过滤器返回的结果提供一些附加的校验操作。常见的Decorating Filters有:
  
A、SkipFilter
  
B、WhileMatchFilter
  
(1)   SkipFilter包装了其它的过滤器,只要被包装的过滤器返回的结果中有一行的某一列或者某个KeyValue被过滤掉了,那么SkipFilter会将该列或者KeyValue所处的整行全部过滤。被包装的过滤器必须实现filterKeyValue()方法。因为SkipFilter会依靠filterKeyValue()返回的结果进行附加的处理。比如:
  
Filter filter = new ValueFilter(CompareFilter.CompareOp.NOT_EQUAL,new BinaryComparator(Bytes.toBytes("val-1")));
  
上面这样一个filter,表示返回的结果中值不能等于val-1,这样值为val-1的那个列就不会展示,但该行的其他列只要满足值不等于val-1都会返回。
  
不过一旦使用了SkipFilter,如:Filter filter2 = new SkipFilter(filter);只要存在某一行中的某个列的值等于val-1,那么该行的所有数据都不会返回。
  
(2)   WhileMatchFilter跟SkipFilter类似,不过区别之处在于WhileMatchFilter一旦找到某一行中的某些列值或者KeyValue不满足条件,那么整个Scan操作就会被终止。SkipFilter只是会将此行过滤,不作为返回值,但Scan操作会继续。
  
  
Ⅳ. Custom Filters
  
如果想实现自定义的Filter,可以实现Filter接口或者扩展FilterBase类。FilterBase类提供了基本的Filter实现。
  
如果用户想在一次检索数据的过程中使用多个Filter,那么可以使用FilterList特性。其构造函数如下:
  
FilterList(List rowFilters)
  
FilterList(Operator operator)
  
FilterList(Operator operator, List rowFilters)
  
其参数operator其枚举值有两个:MUST_PASS_ALL(表示返回的结果集数据必须通过所有过滤器的过滤),MUST_PASS_ONE(表示返回的结果集数据只要通过了其中一个过滤器就行)。
  
  
2)Counters
  
Hbase提供了计数器Counters机制。它将列当做Counters,通过对列的操作来完成计数。在命令行下用户可以通过如下命令增加计数。
  
  
incr ‘
  
’,’’,’’,[]
  
  
如果想获取当前计数器的值,可以通过get命令或者get_counter或者incr命令。如下:
  
get ‘
  
’,’’;
  
  
get_counter ‘
  
’,’’;
  
  
incr ‘
  
’,’’,’’,0;
  
  
第一个和第二个的区别就是第一个返回的值是字节数组类型,用户很难立刻知道到底代表什么值。第二个返回的是可读的值。第三个命令采用比较投机取巧的办法,通过incr计数加0来返回当前值。如果将减少计数,可以通过incr命令来增加一个负数的值。
  
HTable提供了单个计数器(Single Counters)和多个计数器(Multiple Counters)。对于单个的Counters,需要指定准确的列名,跟命令行的incr一样,可以通过增加正数和负数或者零来达到增加计数,减少计数以及访问当期计数的目的。构造函数如下:
  
long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier,long amount) throws IOException
  
long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier,long amount, boolean writeToWAL) throws IOException
  
对于多重计数器,HTable提供的方法如下:
  
Result increment(Increment increment) throws IOException
  
1、用户需要创建一个Increment实例,可以采用如下构造函数:
  
       Increment() {}
  
       Increment(byte[] row)
  
       Increment(byte[] row, RowLock rowLock)
  
2、如果想为这个Increment实例添加必要的条件,如列名,或者时间戳范围,可以通过如下方法来完成。可以在一个Increment实例中通过增加多列来实现多重计数器。
  
       Increment addColumn(byte[] family, byte[] qualifier, long amount)
  
       Increment setTimeRange(long minStamp, long maxStamp) throws IOException
  
  
3)Coprocessors
  
Coprocessors是Hbase提供的另一大特性。可以认为是简化的MapReduce组件。Coprocessors是一组内嵌于RegionServer和HMaster进程的框架(BigTable的coprocessors拥有独立进程和地址空间),支持用户请求在每个Region上并行运行,类似于传统数据库中触发器的功能。
  
  
1、Hbase提供的Coprocessors有两种类型:observer和endpoint。其中observer类似于RDBMS中的触发器,即钩子函数,其代码部署在服务器端运行,在真实的方法前添加pre(),实现后加入
  
post(),以实现对真实方法的辅助操作。而endpoint类似于存储过程。
  
2、Coprocessors框架有三个模块组成:Coprocessors,CoprocessorEnvironment,
  
CoprocessorHost。CoprocessorEnvironment提供Coprocessors实例运行的环境以及持有
  
Coprocessors实例的生命周期状态。CoprocessorHost是用来维护Coprocessors实例和
  
Coprocessors运行环境的。
  
三元体类图如下(Hbase94版本):用户可以通过继承BaseRegionObserver, WALObserver,
  
BaseMasterObserver或者BaseEndpointCoprocessor来实现自定义的Coprocessors。 




A、coprocessors Load
  
Coprocessors有两种加载方式:通过配置文件方式的静态加载和动态加载方式。
  
a、配置文件加载
  
静态加载方式就是通过hbase-site.xml配置文件配置指定的coprocessors来加载。配置方式如下,其执行顺序就是按照配置文件指定的顺序:
  
      hbase.coprocessor.region.classes
  
      coprocessor.RegionObserverExample,coprocessor.otherCoprocessor
  
      hbase.coprocessor.master.classes
  
      coprocessor.MasterObserverExample
  
  
  
      hbase.coprocessor.wal.classes
  
      coprocessor.WALObserverExample, bar.foo.MyWALObserver
  
  
  
  
  
需要注意的是:通过这种方式加载的RegionObserver是针对所有Region和表的。用户无法指定某一具体的Region或者table。
  
b、通过table description加载
  
通过这种方式的加载是细化到具体的表的维度。只有跟该表有关的Region操作才会加载。所以这种方式的加载只能针对RegionCoprocessor。加载方法是:
  
  
HTableDescriptor.setValue(),其中key是Coprocessor,value是||
  
B、observer
  
observer又有三种实现类型:
  
a、RegionObserver
  
RegionObserver一般用来进行数据操作的coprocessor,比如数据访问前的权限身份验证,Filter,二级索引等。如:
  
void preFlush(...) / void postFlush(...) MemStore中内容flush到Storefile前后添加辅助型操作。
  
void preGet(...) / void postGet(...)    获取数据的前后添加辅助操作
  
b、MasterObserver
  
MasterObserver是面向整个集群的事件,比如基于管理员的操作和DDL类型的操作的监控。如:
  
void preCreateTable(...) / void postCreateTable(...)   创建表前后做些辅助操作
  
void preAddColumn(...) / void postAddColumn(...)   创建列前后做些辅助操作
  
void preMove(...) / void postMove(...)       移动Region的前后添加辅助操作
  
c、WALObserver
  
WALObserver则是提供钩子函数对Write Ahead Log的的操作。
  
C、Endpoint
  
Endpoint动态扩展了RPC协议。只支持Region的操作,不支持Master和WAL的操作。用户可以通过Endpoint完成一些聚集函数的功能,如AVG,Count,SUM等。其原理是通过包装客户端的实现,类似于MapReduce,比如getSum()操作,Map端endpoint通过并行的scan完成对每个Region的操作,每个Region的scan结果汇总到endpoint包装的客户端,将每个Region反馈的结果进行汇总即可得到getSum()的结果。
  
D、小结
  
a、Coprocessors有两种类型:observer和endpoint。observer类似于传统的关系型数据库中的触发器,通过钩子函数来完成对被钩的方法的辅助功能,endpoint类似于关系型数据库中的存储过程,用来实现聚合函数的相关功能。
  
b、Coprocessors支持动态加载,拥有多种加载方式。
  
c、Coprocessors可以将多个Coprocessor链接在一起使用,类似于Servlet中的filters过滤器。
  
d、Coprocessors中有优先级的概念,SYSTEM级别的Coprocessor优先处理,USER级别的Coprocessor优先级更低。
  
  
4)Split And Comcaption
  
A、Region Split
  
当创建一个表时,此时该表只对应一个Region。随着不断了往表中插入记录,表数据越来越多,当超过设定的值hbase.hregion.max.filesize时,该Region就会Split成两个子Region。原来的那个Region就会被删除。具体操作如下:
  
a、HRegionServer创建一个splits目录,并且关闭其父Region以防接收其它请求。
  
b、HRegionServer会在splits目录准备好两个子Region,父Region的RowKey对半切。然后将其移动到表目录下,并且更细.Meta.表的数据,指示该父Region正在被执行Split操作。
  
c、读取父Region的数据到子Region中。更新.Meta.表。
  
d、清理父Region,通知HMaster将新的子Region迁移到其它RegionServer中。
  



B、Compaction
  
当Hbase将MemStore中的内容flush到StoreFile中后,由于每次flush都会产生一个新的HFile文件。随着一次次的flush,HFile文件越来越多,当达到设定的阀值时,Hbase提供了Compaction特性,会通过此机制将HFile文件进行压缩。
  
Compaction机制分为两种方式:minor compactions和major compactions 。minor compactions是将相邻的一些小的HFile合并成一个稍大的HFile,表演一个多路合并的过程,其文件的数目由(hbase.hstore.compaction.min)指定;而major compactions会将一个Store中的所有HFile合并成一个HFile,并且在压缩的过程中会进行版本合并和删除过滤操作。比如对于那些同一个Cell中且同一个时间戳的数据,只保留最新的那个值,其他的值将被废弃。此外标记了删除样式的数据以及过期的数据也将被过滤。
  
其实Compaction就是将多个有序的HFile文件合并成一个有序的HFile文件的一个过程。它会创建一个StoreFileScanner来包装每一个StoreFile,然后再通过一个StoreFileScanner实例来组装StoreFile对应的StoreFileScanner列表。通过StoreFileScanner实例提供的next()和seek()方法获取每个storeFile中的数据,最后再将此数据append到一个新的HFile中。
  
  
5)HTablePool
  
如果用户每次发起一个请求时都创建一个HTable实例,如下创建方式:
  
  
Configuration conf = HBaseConfiguration.create();
  
HTable table = new HTable(conf, "testtable");
  
这种方式虽然可以满足要求,但对于请求数比较多的情况或者要求响应时间比较快的情况,如上创建HTable实例就比较落伍了。因为创建Htable是一个比较耗时的过程,此外,HTable并不能保证线程安全,在多线程处理下就可能产生莫名其妙的问题。
  
HBase提供了HTable池特性可以解决此问题。用户可以直接从HTable池中获取HTable实例。
  
1、可以通过如下构造函数来创建HTablePool实例,如下:
  
       HTablePool()
  
       HTablePool(Configuration config, int maxSize)
  
       HTablePool(Configuration config, int maxSize,HTableInterfaceFactory tableFactory)
  
上面的第一个构造函数会默认获取classpath下的配置,并且创建无穷大的HTable个数。用户可以提供定制的创建的HTable实例的工厂来,这样创建的HTablePool中的HTable就是用户定制的
  
HTable实例。maxSize参数是指定HTable池中最大持有多少个HTable实例。比如如果此size为5,
  
而用户通过getTable获取了10次引用,那么当用户通过putTable方法将实例放回HTable池中时,只能放回5个实例,另外的5次将被忽略掉了。
  
2、创建HTablePool实例后,就可以通过getTable方法获取对应的表的HTable实例了。如下:
  
      HTableInterface getTable(String tableName)
  
      HTableInterface getTable(byte[] tableName)
  
3、当使用完HTable实例后,需要将HTable实例关闭,可以采用如下方法:
  
     void closeTablePool(String tableName)
  
     void closeTablePool(byte[] tableName)
  
     void putTable(HTableInterface table)
  
closeTablePool(tableName)相当于直接将此Table实例关闭。建议使用此方法。PutTable(FilterBase)表示将此实例放回HTable池中供下次使用。建议不要使用此方法,目前此方法也在逐渐废弃。需要注意的是以上操作最好放到finally模块进行处理。
  
五、总结
  
A、总的来说Hbase因为其面向列族的key-value存储特性使得其拥有列式数据库的优势。分布式的Hbase应用是由客户端和服务端进程组成,通过HDFS作为其持久层,采用Zookeeper来完成集群的管理和状态监控协调服务。对于全表扫描和大数据的加载通过MapReduce来完成。Hbase无缝集成了Apache的这几大组件来实现可伸缩,面向列族的分布式存储系统。
  
  
B、Hbase是严格一致性的分布式存储系统,从两个方面来保证严格一致性问题:它提供行锁,但不提供多行锁和事务,保证了读写的原子性。此外Hbase数据存储支持多版本和时间戳的特性。
  
C、Hbase可以认为是BigTable的开源实现,但跟BigTable还是有很多区别。比如:Hbase的Coprocessors跟BigTable不同。Hbase支持服务器端的Filter以减少网络传输开销。此外Hbase支持可插拔的文件系统,目前文件系统是HDFS,BigTable是GFS。
  
D、Hbase通过实现服务器端的钩子(Coprocessors)来完成二级索引。这也是BigTable没有实现的。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值