HBase的原理及设计

HBase是一个开源的非关系型的高可靠、高性能、面向列、可伸缩的分布式存储系统。它使用ZooKeeper集群来管理,基于HDFS存储海量数据。参考了谷歌的BigTable建模,典型的Key/Value系统。
HBase其实并不快,但是在处理海量数据的时候它不慢。所以在数据量超级大的时候,使用HBase并不会让你失望。但是HBase并不适合做细腻的数据分析,它的一切操作都是添加操作。这没有关系,因为HBase是Hadoop生态系统中重要的一员,它可以通过和MapReduce等相结合,来弥补这个缺陷。

Hbase表的数据模型:

RowKey:行键,是byte数组,是表中唯一区分每条记录的“主键”,方便快速查找,RowKey的设计很重要,后面我会说的。
Column Family:列族,拥有一个名称,其中可以包含N个qualifier,列族名的设计不要太长,越短越好,因为这些信息都会存储到单元格中,因为哪怕多一个字母,那有上亿条数据就会多上亿个字母的空间。
Qualifier:列,属于某个列族,是列族下的修饰符,修饰一列。
Version:版本,即timestamp时间戳,类型为Long。可以指定该列族可以存储的版本数量,每条数据有一个时间戳,当有新数据进入时,原数据不会消失,会被存储,通过时间戳还可以获得到原来的数据。
Cell:单元格,row和qualifier的交叉点,单元格中会存储数据,即value,value没有类型的选择,都为byte类型,但是基本所有的数据都能转为byte类型,所以HBase可以存储的数据类型很丰富。并且会存储一些能确定这个单元格位置关键信息,如RowKey、ColumnFamily、Qualifier等等,因此,行键和列族的名字越短越好。

HBase中的数据是三维有序的{RowKey=>{Column Family=>{Qualifier=>{Version=>Cell}}}}
HBase中数据就是按照这个顺序排序的。

HBase的组件:

大体结构为:
HMaster
HRegion Server
    WAL(底层实现HLog)
    HRegion
        HStore
            MemStore
            StoreFile(底层实现HFile)

Client:

使用HBase的RPC机制与HMaster和HRegionServer进行通信,提交请求和获取结果。对于管理类操作,Client与HMaster进行RPC;对于数据读写类操作,Client与HRegionServer进行RPC。

HMaster

是HBase中的管理者(主),它的作用:
管理所有的HRegionServer,告诉其需要维护哪些HRegion,并监控所有HRegionServer的运行状态。当一个新的HRegionServer登录到HMaster时,HMaster会告诉它等待分配数据;而当某个HRegionServer死机时,HMaster会把它负责的所有HRegion标记为未分配,然后再把它们分配到其他HRegionServer中。HMaster没有单点问题,HBase可以启动多个HMaster,通过Zookeeper的选举机制保证集群中总有一个HMaster运行,从而提高了集群的可用性。

HRegionServer

是HBase中的劳动者(从),它的作用:
HBase中的所有数据从底层来说一般都是保存在HDFS中的,用户通过一系列HRegionServer获取这些数据。集群一个节点上一般只运行一个HRegionServer,且每一个区段的HRegion只会被一个HRegionServer维护。HRegionServer主要负责响应用户I/O请求,向HDFS文件系统读写数据,是HBase中最核心的模块。HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了逻辑表中的一个连续数据段。HRegion由多个HStore组成,每个HStore对应了逻辑表中的一个列族的存储,可以看出每个列族其实就是一个集中的存储单元。因此,为了提高操作效率,最好将具备共同I/O特性的列放在一个列族中。

WAL(Write-Ahead-Log)预写日志:

是Hbase的RegionServer在处理数据插入和删除的过程中用来记录操作内容的一种日志。在每次Put、Delete等一条记录时,首先将其数据写入到RegionServer对应的HLog文件的过程。

HLog:

每个HRegionServer中都有一个HLog对象,它是一个实现了Write Ahead Log的预写日志类。在每次用户操作将数据写入MemStore的时候,也会写一份数据到HLog文件中,HLog文件会定期滚动刷新,并删除旧的文件(已持久化到StoreFile中的数据)。当HMaster通过Zookeeper感知到某个HRegionServer意外终止时,HMaster首先会处理遗留的 HLog文件,将其中不同HRegion的HLog数据进行拆分,分别放到相应HRegion的目录下,然后再将失效的HRegion重新分配,领取到这些HRegion的HRegionServer在加载 HRegion的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后Flush到StoreFiles,完成数据恢复。

HRegion:

当表的大小超过预设值(默认为10G)的时候,HBase会自动将表划分为不同的区域,每个区域包含表中所有行的一个子集。对用户来说,每个表是一堆数据的集合,靠主键(RowKey)来区分。从物理上来说,一张表被拆分成了多块,每一块就是一个HRegion。我们用表名+开始/结束主键,来区分每一个HRegion,一个HRegion会保存一个表中某段连续的数据,一张完整的表数据是保存在多个HRegion中的。

HStore:

它是HBase存储的核心,由MemStore和StoreFiles两部分组成。MemStore是内存缓冲区,用户写入的数据首先会放入MemStore,当MemStore满了(系统默认128M)以后会Flush成一个StoreFile(底层实现是HFile),当StoreFile的文件数量增长到一定阈值后,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进行版本合并和数据删除操作。因此,可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的Compact过程中进行的,这样使得用户的写操作只要进入内存就可以立即返回,保证了HBaseI/O的高性能。当StoreFiles Compact后,会逐步形成越来越大的StoreFile,当单个StoreFile大小超过一定阈值后,会触发Split操作,同时把当前的HRegion 大致平均Split成2个HRegion,父HRegion会下线,新分出的2个子HRegion会被HMaster分配到相应的HRegionServer,使得原先1个HRegion的负载压力分流到2个HRegion上。

HFile:

是HBase中KeyValue数据的存储格式,是hadoop的二进制格式文件。
它分为六个部分:
Data Block 段–保存表中的数据,这部分可以被压缩
Meta Block 段 (可选的)–保存用户自定义的kv对,可以被压缩。
File Info 段–Hfile的元信息,不被压缩,用户也可以在这一部分添加自己的元信息。
Data Block Index 段–Data Block的索引。每条索引的key是被索引的block的第一条记录的key。
Meta Block Index段 (可选的)–Meta Block的索引。
Trailer–这一段是定长的。保存了每一段的偏移量,读取一个HFile时,会首先读取Trailer,Trailer保存了每个段的起始位置(段的Magic Number用来做安全check),然后,DataBlock Index会被读取到内存中,这样,当检索某个key时,不需要扫描整个HFile,而只需从内存中找到key所在的block,通过一次磁盘io将整个block读取到内存中,再找到需要的key。DataBlock Index采用LRU机制淘汰。
HFile的Data Block,Meta Block通常采用压缩方式存储,压缩之后可以大大减少网络IO和磁盘IO,随之而来的开销当然是需要花费cpu进行压缩和解压缩。
目标Hfile的压缩支持两种方式:Gzip,Lzo。

Hbase的特殊的表:

-ROOT-表:
记录了.META表的Region信息,-ROOT-表只有一个region,Zookeeper中记录了-ROOT-表的location。
.META表:
记录了用户表的Region信息,.META可以有多个region,.META表不会分裂。

HBash 0.96之后去掉了-ROOT-表。所以目前流程为:
1、从Zookeeper(/hbase/meta-region-server)中欧获取hbase:meta的位置(HRegionServer的位置),缓存该位置信息。

2、从HRegionServer中查询用户Table对应请求的RowKey所在的HRegionServer,缓存该位置信息。
3、从查询到HRegionServer中读取RowKey。

HBase写操作流程:

步骤1:Client通过Zookeeper的调度,向HRegionServer发出写数据请求,在HRegion中写数据。
步骤2:数据被写入HRegion的MemStore,直到MemStore达到预设阈值。
步骤3:MemStore中的数据被Flush成一个StoreFile。
步骤4:随着StoreFile文件的不断增多,当其数量增长到一定阈值后,触发Compact合并操作,将多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除。
步骤5:StoreFiles通过不断的Compact合并操作,逐步形成越来越大的StoreFile。
步骤6:单个StoreFile大小超过一定阈值后,触发Split操作,把当前HRegion Split成2个新的HRegion。父HRegion会下线,新Split出的2个子HRegion会被HMaster分配到相应的HRegionServer 上,使得原先1个HRegion的压力得以分流到2个HRegion上。

读操作流程

步骤1:client访问Zookeeper,查找-ROOT-表,获取.META.表信息。
步骤2:从.META.表查找,获取存放目标数据的HRegion信息,从而找到对应的HRegionServer。
步骤3:通过HRegionServer获取需要查找的数据。
步骤4:HRegionserver的内存分为MemStore和BlockCache两部分,MemStore主要用于写数据,BlockCache主要用于读数据。读请求先到MemStore中查数据,查不到就到BlockCache中查,再查不到就会到StoreFile上读,并把读的结果放入BlockCache。

设计原则

Column Family的设计原则

原则1: 长度短,列族其实是可以没有实际意义的,因为实际上列族也不会太多,有个文档对照一下也完全可以。所以不像mysql数据库表一样要把列名起的清楚。Hbase中不需要。
原则2: 业务上相似的内容放入同一列族下业务上相似的内容一般在一起查询的概率要高一些,所以放在同一列族下可以提高查询效率。

RowKey设计原则

A唯一原则:不用多说行键一定是要保证唯一的通过行键是可以唯一确定一行数据的。
B长度原则:Rowkey理论上长度能达到64k实际中一般是10-100kb一般是越短越好,原因是这样的。hbase存的是大量数据,如果rowkey大的话紧rowkey就占用了大量空间,比如100b的rowkey 存100条就是1k 一千条就是10k一万条就是100k十万条就是1M,一亿条就是一个G。所以row可以应该在能保证唯一和功能的前提下越短越好。
还有一个问题是rowkey的长度最好是一致的。比如7并不会排在11的前面而是后面,但是07会排在11的前面。

RowKey的热点问题

由于HRegion存储是按行键存储的,如果行键的值都是类似的那就容易导致数据都堆在一个HRegion上产生了热点问题。
所以可以通过,rowkey应该尽量的散列化。
1 加盐在rowkey前生成随机数
2 反转把最没有用的放在前面以增加随机性,比如手机号,一个号段太多,会热点但是反转就没事了。

你可能感兴趣的:(HBase的原理及设计)