HBase开源增强特性
HBase开源增强特性:HIndex
HBase是一个Key-Value类型的分布式存储数据库。每张表的数据按照RowKey的字典顺序排序,因此,如果按照某个指定的RowKey去查询数据,或者指定某一个RowKey范围去扫描数据时,HBase可以快速定位到需要读取的数据位置,从而可以高效地获取到所需要的数据。
在实际应用中,很多场景是查询某一个列值为“XXX”的数据。HBase提供了Filter特性去支持这样的查询,它的原理是:按照RowKey的顺序,去遍历所有可能的数据,再依次去匹配那一列的值,直到获取到所需要的数据。可以看出,可能只是为了获取一行数据,它却扫描了很多不必要的数据。因此,如果对于这样的查询请求非常频繁并且对查询性能要求较高,使用Filter无法满足这个需求。
这就是HBase HIndex产生的背景。HIndex为HBase提供了按照某些列的值进行索引的能力。
- 索引数据不支持滚动升级。
- 组合索引限制。
- 用户必须在单次mutation中输入或删除参与组合索引的所有列。否则会导致不一致问题。
索引:IDX1=>cf1:[q1->datatype],[q2];cf2:[q2->datatype]
正确的写操作:
Put put = new Put(Bytes.toBytes("row")); put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA")); put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB")); put.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueC")); table.put(put);
错误的写操作:
Put put1 = new Put(Bytes.toBytes("row")); put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA")); table.put(put1); Put put2 = new Put(Bytes.toBytes("row")); put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB")); table.put(put2); Put put3 = new Put(Bytes.toBytes("row")); put3.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueC")); table.put(put3);
- 使用组合条件查询,仅支持组合索引列包含过滤条件的查询,或者不指定StartRow和StopRow的部分索引列的查询。
索引:IDX1=>cf1:[q1->datatype],[q2];cf2:[q1->datatype]
正确的查询操作:
scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',>=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true) "} scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true)" } scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',>=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true)",STARTROW=>'row001',STOPROW=>'row100'}
错误的查询操作:
scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',>=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true) AND SingleColumnValueFilter('cf2','q2',>=,'binary:valueD',true,true)"} scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf2','q1',>=,'binary:valueC',true,true)" } scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf2','q2',>=,'binary:valueD',true,true)" } scan 'table', {FILTER=>"SingleColumnValueFilter('cf1','q1',=,'binary:valueA',true,true) AND SingleColumnValueFilter('cf1','q2',>=,'binary:valueB',true,true)" ,STARTROW=>'row001',STOPROW=>'row100' }
- 用户必须在单次mutation中输入或删除参与组合索引的所有列。否则会导致不一致问题。
- 用户不要明确地为有索引数据的表配置任何分裂策略。
- 不支持其他的mutation操作,如increment和append。
- 不支持maxVersions>1的列的索引。
- 不支持一行数据索引列的更新操作。
索引1:IDX1=>cf1:[q1->datatype],[q2];cf2:[q1->datatype]
索引2:IDX2=>cf2:[q2->datatype]
正确的更新操作:
Put put1 = new Put(Bytes.toBytes("row")); put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA")); put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB")); put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q1"), Bytes.toBytes("valueC")); put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueD")); table.put(put1); Put put2 = new Put(Bytes.toBytes("row")); put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q3"), Bytes.toBytes("valueE")); put2.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q3"), Bytes.toBytes("valueF")); table.put(put2);
错误的更新操作:
Put put1 = new Put(Bytes.toBytes("row")); put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA")); put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB")); put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q1"), Bytes.toBytes("valueC")); put1.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueD")); table.put(put1); Put put2 = new Put(Bytes.toBytes("row")); put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q1"), Bytes.toBytes("valueA_new")); put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("q2"), Bytes.toBytes("valueB_new")); put2.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q1"), Bytes.toBytes("valueC_new")); put2.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("q2"), Bytes.toBytes("valueD_new")); table.put(put2);
- 添加索引的表不应拥有大于32KB的值。
- 当由于列族级TTL(生存周期)过期而导致用户数据删除时,对应的索引数据不会立即删除。索引数据会在进行major compaction操作时被删除。
- 用户列族的TTL在索引创建后不能修改。
- 如果在创建索引之后,列族的TTL值变大,应该删除并重新创建该索引。否则,一些已经生成的索引数据会先于用户数据被删除。
- 如果在创建索引之后,列族的TTL值变小。索引数据会晚于用户数据被删除。
- 索引查询不支持reverse;且查询结果是无序的。
- 索引不支持clone snapshot操作。
- 索引表必须使用HIndexWALPlayer回放日志,不支持WALPlayer回放日志。
hbase org.apache.hadoop.hbase.hindex.mapreduce.HIndexWALPlayer Usage: WALPlayer [options] <wal inputdir> <tables> [<tableMappings>] Read all WAL entries for <tables>. If no tables ("") are specific, all tables are imported. (Careful, even -ROOT- and hbase:meta entries will be imported in that case.) Otherwise <tables> is a comma separated list of tables. The WAL entries can be mapped to new set of tables via <tableMapping>. <tableMapping> is a command separated list of targettables. If specified, each table in <tables> must have a mapping. By default WALPlayer will load data directly into HBase. To generate HFiles for a bulk data load instead, pass the option: -Dwal.bulk.output=/path/for/output (Only one table can be specified, and no mapping is allowed!) Other options: (specify time range to WAL edit to consider) -Dwal.start.time=[date|ms] -Dwal.end.time=[date|ms] For performance also consider the following options: -Dmapreduce.map.speculative=false -Dmapreduce.reduce.speculative=false
- 使用deleteall操作索引表存在性能慢问题。
- 索引表不支持HBCK;如需使用HBCK修复索引表,需先删除索引数据后,再进行修复。
HBase开源增强特性:支持多点分割
当用户在HBase创建Region预先分割的表时,用户可能不知道数据的分布趋势,所以Region的分割可能不合适,所以当系统运行一段时间后,Region需要重新分割以获得更好的查询性能,HBase只会分割空的Region。
HBase自带的Region分割只有当Region到达设定的Threshold后才会进行分割,这种分割被称为单点分割。
为了实现根据用户的需要动态分割Region以获得更好的性能这一目标,开发了多点分割又称动态分割,即把空的Region预先分割成多个Region。通过预先分割,避免了因为Region空间不足出现Region分割导致性能下降的现象。
HBase开源增强特性:连接数限制
过多的session连接意味着过多的查询和MR任务跑在HBase上,这会导致HBase性能下降以至于导致HBase拒绝服务。通过配置参数来限制客户端连接到HBase服务器端的session数目,来实现HBase过载保护。
HBase开源增强特性:容灾增强
主备集群之间的容灾能力可以增强HBase数据的高可用性,主集群提供数据服务,备用集群提供数据备份,当主集群出现故障时,备集群可以提供数据服务。相比开源Replication功能,做了如下增强:
- 备集群白名单功能,只接受指定集群ip的数据推送。
- 开源版本中replication是基于WAL同步,在备集群回放WAL实现数据备份的。对于BulkLoad,由于没有WAL产生,BulkLoad的数据不会replicate到备集群。通过将BulkLoad操作记录在WAL上,同步至备集群,备集群通过WAL读取BulkLoad操作记录,将对应的主集群的HFile加载到备集群,完成数据的备份。
- 开源版本中HBase对于系统表ACL做了过滤,ACL信息不会同步至备集群,通过新加一个过滤器org.apache.hadoop.hbase.replication.SystemTableWALEntryFilterAllowACL,允许ACL信息同步至备集群,用户可以通过配置hbase.replication.filter.sytemWALEntryFilter使用该过滤其实现ACL同步。
- 备集群只读限制,备集群只接受备集群节点内的super user对备集群的HBase进行修改操作,即备集群节点之外的HBase客户端只能对备集群的HBase进行读操作。
HBase开源增强特性:HBase MOB
在实际应用中,用户需要存储大大小小的数据,比如图像数据、文档。小于10MB的数据一般都可以存储在HBase上,对于小于100KB的数据,HBase的读写性能是最优的。如果存放在HBase的数据大于100KB甚至到10MB时,插入同样个数的数据文件,其数据量很大,会导致频繁的compaction和split,占用很多CPU,磁盘IO频率很高,性能严重下降。
将MOB数据(即100KB到10MB大小的数据)直接以HFile的格式存储在文件系统上(例如HDFS文件系统),然后把这个文件的地址信息及大小信息作为value存储在普通HBase的store上,通过expiredMobFileCleaner和Sweeper工具集中管理这些文件。这样就可以大大降低HBase的compation和split频率,提升性能。
如图3所示,图中MOB模块表示存储在HRegion上的mobstore,mobstore存储的是key-value,key即为HBase中对应的key,value对应的就是存储在文件系统上的引用地址以及数据偏移量。读取数据时,mobstore会用自己的scanner,先读取mobstore中的key-value数据对象,然后通过value中的地址及数据大小信息,从文件系统中读取真正的数据。
HBase开源增强特性:HFS
HBase文件存储模块(HBase FileStream,简称HFS)是HBase的独立模块,它作为对HBase与HDFS接口的封装,应用在MRS的上层应用,为上层应用提供文件的存储、读取、删除等功能。
在Hadoop生态系统中,无论是HDFS,还是HBase,均在面对海量文件的存储的时候,在某些场景下,都会存在一些很难解决的问题:
- 如果把海量小文件直接保存在HDFS中,会给NameNode带来极大的压力。
- 由于HBase接口以及内部机制的原因,一些较大的文件也不适合直接保存到HBase中。
HFS的出现,就是为了解决需要在Hadoop中存储海量小文件,同时也要存储一些大文件的混合的场景。简单来说,就是在HBase表中,需要存放大量的小文件(10MB以下),同时又需要存放一些比较大的文件(10MB以上)。
HFS为以上场景提供了统一的操作接口,这些操作接口与HBase的函数接口类似。
HBase开源增强特性:多RegionServer共机部署
HBase支持一个节点部署多个RegionServer,提升HBase资源利用率。
单RegionServer资源利用率低:
- 单个RegionServer支持的Region数量有限,无法充分利用内存、CPU资源。
- 单个RegionServer数据量为20T,两副本为40T,三副本60T,无法用完96T的磁盘。
- 写入性能差:一台物理机一个RegionServer,只有一个HLog,只能同时写三块盘。
多RegionServer共机部署,提升HBase资源利用率:
- 一台物理机最多可以部署5个RegionServer,每台物理机上部署的RegionServer个数可以根据需要自由选择。
- 充分利用内存、磁盘、CPU等资源。
- 一台物理机最多5个HLog ,可以同时写15块盘,大幅提升写入性能。
HBase开源增强特性:HBase双读
在HBase存储场景下,因为GC、网络抖动、磁盘坏道等原因,很难保证99.9%的查询稳定性。为了满足用户大数据量随机读低毛刺的要求,新增了HBase双读特性。
HBase双读特性是建立在主备集群容灾能力之上,两套集群同时产生毛刺的概率要远远小于一套集群,即采用双集群并发访问的方式,保证查询的稳定性。当用户发起查询请求时,同时查询两个集群的HBase服务,在等待一段时间(最大容忍的毛刺时间)后,如果主集群没有返回结果,则可以使用响应最快的集群数据。原理图如下: