实时检索之HBase

1、概念

HBase是一个分布式的NoSQL数据库,其特点高可靠、高性能、面向列、可伸缩。

  • 适合存储大表数据 ,并且可实时读写大表数据。大表:表的规模可以达到数十亿行以及数百万列
  • 表结构稀疏。稀疏:对于为空的列,并不占用存储空间
  • 数据底层存储于Hadoop HDFS分布式文件系统。
  • 利用ZooKeeper作为协同服务。

2、特点

HBase适合具有如下需求的应用:

  • 海量数据 (TB、PB) 。
  • 不需要完全拥有传统关系型数据库所具备的ACID特性。
  • 高吞吐量。
  • 需要在海量数据中实现高效的随机读取。
  • 需要很好的性能伸缩能力。
  • 能够同时处理结构化和非结构化的数据。

ACID原则是数据库事务正常执行的四个特性,分别指原子性、一致性、独立性及持久性。

  • 事务的原子性(Atomicity):指一个事务要么全部执行,要么不执行。也就是说一个事务不可能只执行了一半就停止了。比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱,不可能划了卡,而钱却没出来.这两步必须同时完成,要么就不完成。
  • 事务的一致性(Consistency):指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变。
  • 独立性(Isolation):事务的独立性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致。
  • 持久性(Durability):事务的持久性是指事务执行成功以后,该事务所对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚。

3、各组件

 

 

ZooKeeper为HBase集群中各进程提供分布式协作服务。各RegionServer将自己的信息注册到Zookeeper中,主用Master据此感知各个RegionServer的健康状态。

Client使用HBase的RPC机制与Master、RegionServer进行通信。Client与Master进行管理类通信,与RegionServer进行数据操作类通信。

RegionServer负责提供表数据读写等服务,是HBase的数据处理和计算单元。RegionServer一般与HDFS集群的DataNode部署在一起,实现数据的存储功能。

HMaster,在HA模式下,包含主用Master和备用Master。

  • 主用Master:负责HBase中RegionServer的管理,包括表的增删改查;RegionServer的负载均衡,Region分布调整;Region分裂以及分裂后的Region分配;RegionServer失效后的Region迁移等。
  • 备用Master:当主用Master故障时,备用Master将取代主用Master对外提供服务。故障恢复后,原主用Master降为备用。

HDFS为HBase提供高可靠的文件存储服务,HBase的数据全部存储在HDFS中。

Store:一个Region由一个或多个Store组成,每个Store对应图中的一个Column Family。

MemStore:一个Store包含一个MemStore,MemStore缓存客户端向Region插入的数据。

StoreFile:MemStore的数据flush到HDFS后成为StoreFile。

Hfile:HFile定义了StoreFile在文件系统中的存储格式,它是当前HBase系统中StoreFile的具体实现。

Hlog:HLog日志保证了当RegionServer故障的情况下用户写入的数据不丢失,RegionServer的多个Region共享一个相同的Hlog。

4、Hbase存储形式

HBase的底层数据以KeyValue的形式存在,KeyValue具有特定的格式。

  • KeyValue中拥有时间戳、类型等关键信息。
  • Key部分被用来快速的检索一条数据记录,Value部分用来存储实际的用户数据信息。
  • 同一个Key值可以关联多个Value,每一个KeyValue都拥有一个Qualifier标识。
  • 支持动态增加列,容易适应数据类型和结构的变化。以块为单元操作数据,列间、表间并无关联关系。
  • 即使是Key值相同,Qualifier也相同的多个KeyValue,也可能有多个版本,此时使用时间戳来区分,这就是同一条数据记录的多版本。
  • KeyValue型数据库数据分区方式---按Key值连续范围分区。
  • 数据按照RowKey的范围 (按RowKey的字典顺序),划分为一个个的子区间。每一个子区间都是一个分布式存储的基本单元。

5、HBase缓存机制

HBase提供2种类型的缓存结构:MemStore和BlockCache。

MemStore:HBase数据先写入HLog 之中,并同时写入MemStore,待满足一定条件后将MemStore中数据刷到磁盘,能提升HBase的写性能和读性能。

BlockCache:HBase会将一次文件查找的Block块缓存到Cache中,以便后续同一请求或者相邻数据查找请求,可以直接从内存中获取,避免IO操作。

其中MemStore是写缓存,BlockCache是读缓存。

一个HRegionServer只有一个BlockCache,在HRegionServer启动的时候完成BlockCache的初始化,常用的BlockCache包括LruBlockCache,以及 CombinedBlockCache(LruBlockCache + BucketCache)。

  • LRUBlockCache 是HBase默认的BlockCache实现方案。实际上就是一个ConcurrentHashMap管理BlockKey到Block的映射关系,缓存Block只需要将BlockKey和对应的Block放入该HashMap中,查询缓存就根据BlockKey从HashMap中获取即可。
  • LRUBlockCache 分层策略:Block的数据存储在JVM 堆中,由JVM进行管理。它在逻辑上分为三个区:Single-Access、Multi-Access、In-Memory,分别占整个BlockCache的25%、50%、25%。一次随机读中,一个Block块从HDFS中加载出来之后首先放入Single-Access区,后续如果有多次请求访问到这块数据的话,就会将这块数据移到Multi-Access s区。而In-Memory区表示数据可以常驻内存,一般用来存放访问频繁、数据量小的数据,比如元数据,用户也可以在建表的时候通过设置列族属性IN-MEMORY= true将此列族放入In-Memory区。因此设置数据属性InMemory= true需要非常谨慎,确保此列族数据量很小且访问频繁,否则有可能会将hbase.meta元数据挤出内存,严重影响所有业务性能。无论哪个区,系统都会采用严格的Least-Recently-Used算法,当BlockCache总量达到一定阈值之后就会启动淘汰机制,最少使用的Block会被置换出来,为新加载的Block预留空间。
  • LRU淘汰算法实现:系统在每次cache block时将BlockKey和Block放入HashMap后都会检查BlockCache总量是否达到阈值,如果达到阈值,就会唤醒淘汰线程对Map中的Block进行淘汰。系统设置三个MinMaxPriorityQueue队列,分别对应上述三个分层,每个队列中的元素按照最近最少被使用排列,系统会优先poll出最近最少使用的元素,将其对应的内存释放。
  • CombinedBlockCache:是一个LRUBlockCache和BucketCache的混合体。LRUBlockCache中主要存储Index Block和Bloom Block,而将Data Block存储在BucketCache中。故一次随机读需要先在LRUBlockCache中查到对应的Index Block,然后再到BucketCache查找对应数据块。BucketCache有三种工作模式:heap、offheap、file。heap模式表示这些Bucket是从JVM Heap中申请,offheap模式使用DirectByteBuffer技术实现堆外内存存储管理,而file模式使用类似SSD的高速缓存文件存储数据块。无论在哪一种工作模式,BucketCache都会申请许多带有固定大小标签的Bucket,一种Bucket只是一种指定的BlockSize的数据块,初始化的时候申请14个不同大小的Bucket,而且即使在某一种Bucket空间不足的情况下,系统也会从其他Bucket空间借用内存使用,不会出现内存使用率低下的情况。
  • BucketCache内存组织形式:HBase启动时,会在内存申请大量bucket,每一个bucket默认大小是2MB,每一个Bucket都有一个offsetBase属性和size标签,其中offsetBase表示这个bucket在实际物理空间的offset, 故可以根据offsetBase的属性,和block在该bucket的offset确定block实际物理存储地址;size标签表示这个bucket可以存放的block块的大小,比如size 标签是9k, 那么他只能存储8k的block,如果size标签是129k,那么bucket只能存储128k的block;HBase使用BucketAllocator对bucket进行管理, HBase会根据size进行分组,相同size的标签由同一个BucketSizeInfo来管理,如64k的block的由size 标签是65k的BucketSizeInfo来管理, 如128k的block的由size 标签是129k的BucketSizeInfo来管理;默认标签有(4+1)K、(8+1)K、(16+1)K … (48+1)K、(56+1)K、(64+1)K、(96+1)K … (512+1)K,且系统会先从小到大遍历一次所有size标签,为每种size标签分配一个bucket,最后剩余的bucket都分配最大的size标签,默认分配 (512+1)K;bucket size标签,可以动态调整。比如64K的block数目比较多,65K的bucket被用完了以后,其他size标签的完全空闲的bucket可以转换成为65K的bucket,但是至少保留一个该size的bucket。

6、Hbase检测一个元素是不是集合中的一个成员

BloomFilter用来优化一些随机读取的场景,即Get场景。它可以被用来快速的判断一条数据在一个大的数据集合中是否存在。Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员。

  • BloomFilter在判断一个数据是否存在时,拥有一定的误判率。误判率:在万分之一以下由哈希函数个数k、位数组大小m、数据量n共同确定。但对于“该条数据不存在”的判断结果是可信的。
  • HBase的BloomFilter的相关数据,被保存在HFile中。
  • Bloom Filter的数据存在StoreFile的meta中,一旦写入无法更新,因为StoreFile是不可变的。
  • Bloomfilter是一个列族(cf)级别的配置属性,如果你在表中设置了Bloomfilter,那么HBase会在生成StoreFile时包含一份bloomfilter结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。
  • Bloom Filter用k个hash function将它hash得到bloom filter中k个bit位,将这k个bit位置1。若这k bits全为1,则此元素在集合中。 

7、HBase性能优化

表设计

  • Region:区域。预先创建多个Region,当数据写入HBase时,会按照RowKey对应Region分区情况,在集群内做数据的负载均衡。创建HBase表的时候会自动创建一个region分区,直到这个region足够大了才进行切分,当导入数据的时候,所有的HBase客户端都向这一个region写数据。
  • RowKey:行键。满足实际业务需求情况下,长度越小越好,考虑散列性(连续的Row Key易导致负载不均衡)。散列存储可采用取反或Hash来实现。可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes,存为byte[]字节数组,一般设计成定长的。RowKey是按照字典序存储,因此,设计RowKey时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。 取反:将指连续的id等,倒排作为HBase RowKey设计。如 10000,10001,10002 --> 00001,10001,20001 Hash:连续的id可根据集群节点数由Hash计算出结果,再将hash值放到id前面拼接成RowKey。如 id --> 0-id1 , 3-id2
  • Column Family:列簇 。一张表里不要定义太多的列簇,因为某个列簇在flush的时候,它邻近的列簇也会因关联效应被触发flush,最终导致系统产生更多的I/O。
  • Max Version:最大版本数量。如果只需要保存最新版本的数据,那么可以设置最大版本数为1。
  • Time To Live:数据存活时间(秒)。例如只需要存储最近两天的数据,那么可以设置存活时间为2 * 24 * 60 * 60。

写表操作

Table参数

Write Buffer:实际写入数据量的多少来设置Table客户端的写buffer大小。

WAL Flag:对于相对不太重要的数据,(谨慎使用)放弃写WAL日志,从而提高数据写入的性能。

批量写:通过调用Table实例的put(List)批量写入多行记录,只需一次网络I/O开销,可以明显的提升写性能。

 

posted @ 2021-02-18 11:30  夏日的向日葵  阅读(1052)  评论(0编辑  收藏  举报