HBase架构的组成
HBase大体上按照master-slave架构可以分解成三种类型的服务。Region server服务于数据的读和写。当有数据需要被访问,client与HBase RegionServer直接通讯。Region assignment,DDL(create,delete tables)操作由HBase Master处理。Zookeeper,可以理解成HDFS的一部分,用来维护集群状态。
Hadoop DataNode存储由RegionServer管理的数据。所有HBase数据存储在HDFS的文件中。RegionServer搭配DataNodes来使用,这样可以使用HBase的Locality特性(就是把RegionServer负责管理数据其中一份副本放在本地的DataNode)。HBase在数据被写入时本地会存储一份副本,但是当一个Region被挪到其他RS上,Region数据直到下一次compaction才会被写到本地DN。
NameNode负责管理由物理文件构成的data block所产生的metadata信息。
Regions
HBase表会按照key range被水平的拆分成一个个的“Regions”。一个Region包含这张表从start key到end key之间的所有数据行(类似于Mysql应对海量数据所使用的分库分表策略),并且对这个范围内的数据读写提供服务。一个RegionServer可对大概1000个Region提供服务(1000是一个默认值,可使用配置项修改,超过这个值后将不能split)。
HBase HMaster
Region assignment,DDL(create,delete表)等操作由HBase的Master负责处理。
一个Master具体负责的任务如下:
- 协调RegionServer
在启动阶段assigning region,或者recovery和balancing阶段重新assigning regions
监控集群上所有RegionServer实例(通过监听zookeeper上的通知消息方式实现) -
管理操作
create, delete, update表等操作。
ZooKeeper: 集群协调器
HBase使用Zookeeper来作为分布式服务协调器,用来维护集群中的状态。Zookeeper维护Server的存活和可用,也会通知server异常后的事件。Zookeeper使用内部的一致性协议(paxos)来维护共享的状态。但是要注意要有三台或者五台(奇数)机器来提供一致性服务。
组件们是如何协同工作的
Zookeeper用来协调分布式系统中各成员的共享状态信息。RegionServer和active的HMaster与ZooKeeper之间会保持一个session通讯。Zookeeper通过临时znode的方式维护各组件的心跳。
每个RegionServer创建一个临时znode。HMaster监控这些znode来发现可用的RegionServer,并且也通过监控这些Znodes来得知那些Server异常。HMasters之间通过创建临时znode的方式竞争active master(具体来说就是在zk上成功创建znode则为active,失败的为backup)。Active HMaster向Zookeeper发送心跳,Backup HMaster在zk上监控Active HMaster的失败通知。
如果一个RegionServer或者Active HMaster发送心跳失败,session则会超时,临时znode会被删除。所有的Listeners会受到关于删除失败znode的时间通知。Active HMaster监听RegionServer,并会进行RegionServer失败后的恢复工作。Backup HMaster监听Active HMaster的失败信息,如果Active HMaster发生异常,Backup HMaster会成为Active。
HBase第一次Read或Write
在HBase上有一张特别的catalog表被成为META表,它管理所有region在集群中的位置信息。ZooKeeper存储META表的位置信息。
下面是一个client第一次读或写时发生的事情:
- client通过zk获得META表所在的RegionServer。
- client与持有META表的Regionserver通讯,在.META表中查询所希望访问的rowkey所在的RegionServer。client会缓存数据表和META的位置信息。
- client通过前面所拿到的rowkey所在的RegionServer信息,与其通信拿到所需要的数据。
后面的read,client会使用之前从META表中取回的位置信息来读取rowkey。这样则不会再次重新访问META表,除非由于Region被移动走而没有访问到;这种情况下会重新访问META表并更新cache。
HBase Meta Table
- META表是一张HBase表,存储了整个集群中所有region的信息
- META表像是一张B树结构
-
META表按下面的结构存储:
-Key:region start key,region encode id
-Value:RegionServer
RegionServer的组成
一个RegionServer通常在一个HDFS的DataNode运行,并且有如下的组件:
- WAL:Write Ahead Log是HDFS上的一个文件。WAL用来存储还没有被持久化到存储层的新数据;用来作RS异常时的恢复工作中。
- BlockCache:是一个读缓存。在内存中存储了频繁被访问的数据。当BlockCache空间将要用光时会进行evict操作,清理访问最少的数据
- MemStore:是一个写缓存。存储了还未被写到磁盘上的新数据。在写到磁盘之前它是经过排序的。每个column family的每个region会持有一个Memstore。
-
HFile将排序过的KeyValue存储在磁盘上
HBase写步骤1
当请求put一行数据,第一步是将数据写到write-ahead log,即WAL:
- WALEdits会被append到WAL文件的最末端,并存储在磁盘上
-
WAL用来在集群挂掉时恢复还未被持久化到HFile的数据。
HBase写步骤2
一旦数据写到了WAL,它也会被放置在MemStore。之后,client发出的put请求会受到一条ack信息,表示写入成功。
HBase MemStore
MemStore在内存中存储update数据并排序(字典序正序),并按照同样的方式存储在一个HFile中。每个column family的一个Region有一个MemStore。数据的update则在column family的范围内排序。
HBase Region Flush
当MemStore积累了足够的数据后,会将整个排序后的结果写到HDFS上的一个新的HFile。HBase每个column falimy有多个HFile,包含了真实的cell,或者KeyValue实例。这些文件被创建为排序后的KeyValue edits形式,并随着时间的推移,flush到磁盘。
这里要注意一个问题,HBase中的column family有一个数量的限制。这是因为每个CF的每个Region持有一个MemStore,无论哪个MemStore写满数据,所有的MemStore都会做flush操作。在这个过程中保存一个最近写文件时用到的sequence id,这样系统可以知道存储了多少文件。
最高的sequence id会作为一个meta变量存储在每个HFile上,用来响应当时写到了哪个id,可以从哪个id继续。在Region startup阶段,sequence id会被读取,最高的sequence id用来写新的edits。
HBase HFile
数据在HFile中按照排序过的Key/Values存储。当MemStore积累了足够的数据,整个排序过的KeyValue会被重写成HDFS上一个新的HFile。这个过程是一次顺序写入,速度很快,避免了移动磁盘的磁头。
HBase HFile结构
一个HFile包含了一个多层的Index结构,这样避免了HBase查找数据时要将所有Index加载到内存。这个多层Index结构类似于b+树:
- Key value对按增序存储
- Index通过rowkey指向Key value写成的64K block块(64K可在建表时动态修改,更大有助于顺序scan,更小有助于get)
- 每个block都有指向自己的Leaf index
- 每个block的last key会被放在intermediate index中
-
root index指向intermediate index
HFile的Trailer指向meta block,trailer被写在hfile文件的最后。trailer也包含像bloom filter和time range信息。Bloom filters帮助确定文件中是否不包含指定的rowkey。Time range信息帮助在读操作时跳过过期的文件。
HFile Index
刚才讨论的Index,会在HFile被打开时被加载到内存。对在单磁盘的seek操作性能提升有帮助。
HBase Read Merge 1
我们已经知道,一行数据KeyValue单元格可能被存储在多个位置,row单元格已经被持久化在HFile中,近期更新的单元格在MemStore,近期被读取的单元格存储在BlockCache。这样的话,当读一行数据,系统如何返回对应的单元格回来?总的来说会在BlockCache,MemStore,HFile之间进行一次Read Merge操作,具体步骤如下:
- 首先,scanner从BlockCache中读取Row cell。近期被读过的Key values会在这里被缓存,并且当BlockCache空间不足时读取最少的数据将被evicted出BlockCache
- 接下来,scanner在MemStore中查找数据
-
如果scanner没有在MemStore和BlockCache找到所有的数据时(这里提出一个问题,HBase通过什么条件判断读写缓存中的数据不是要查询的全部?),HBase会使用BlockCache index和Bloom filter加载可能包含数据的HFile到内存,来查找目标单元格。
HBase Read Merge 2
如之前所讨论的,每个MemStore会有多个HFile,这意味着一次读,多个文件可能会被检查,将会影响到整体性能。这就是所谓的“读放大”。
HBase Minor Compaction
HBase会自动挑选小一点的HFiles并重写为一个相对大一些的HFile。这个过程称为minor compaction。Minor compaction通过将过小的存储文件重写成更大的文件的方式减少文件数量,并在过程中进行归并排序。
HBase Major Compaction
Major compaction重写并合并每个CF中的一个region内的所有HFiles,并且在过程中,丢掉被删除和已经过期的单元格。这个过程会提升度性能;但是,当major compaction重写所有文件时,大量的磁盘和网络I/O会在过程中产生。这个过程叫做“写放大”
Major compactions可被系统定时自动执行。因为写放大的关系,major compaction经常在周末或者夜间执行。一次Major compaction也会重写由于server挂掉或load balancing交接过来的region数据到本地。
Region = Continguous Keys
我们对region进行一次快速回顾:
- 一张HBase table可被水平的拆分成一个或多个region。一个region包含一段连续的,在startkey和endkey之间有序(字典序正序)的数据
- 每个Region最大1GB(可配置,社区默认1G)
- 一张表的一个Region呗一台RegionServer持有,向client提供服务
-
一个RegionServer可以对1000个Region进行管理(这些region可能会属于不同的HBase table)
Region Split
起初每张表一个Region。当一个Region增长的过于大时,会split成两个child region。两个chlid region,持有original region的一半数据,并行的在与original region相同的RegionServer中打开,之后split操作会报告给HMaster。并且由于load balancing的关系,HMaster可能会将新Region assign到其他RegionServer上。
Read Load Balancing
Split起初会发生在阈值相同的RegionServer上,但是由于load balancing的原因,HMaster可能会将新Region移动到其他Server。结果是新的Region使用远端的HDFS DataNode来提供服务,直到下一次Major Compaction才会将数据写到本地。
HDFS Data Replication 1
所有write和read操作会优先通过主节点。HDFS会复制WAL和HFile blocks。HFile block repication会自动发生。HBase依赖HDFS提供数据安全性。当数据写到HDFS时,一份数据写到RegionServer本地的Datanode,之后复制到第二个节点,最后拷贝到第三个节点。
HDFS Data Replication 2
WAL文件和HFile被持久化在磁盘并被replicated。这是HBase恢复MemStore中没有被持久化为HFile的数据并保障数据安全的方式。
HBase Crash Recovery
当一台RegionServer异常,上面的Region在recovery过程执行完成之前会是不可用状态。Zookeeper会在RegionServer失去心跳时决定Node异常。HMaster之后会被通知,得知RegionServer挂掉的消息。
当HMaster检测到RegionServer挂掉,HMaster会重新assign挂掉RegionServer上的所有Region到可用的RegionServer上。为了恢复挂掉RegionServer的Memstore中没有被持久化到磁盘上的edit信息,HMaster会将属于挂掉RegionServer的WAL按照新的RegionServer拆分成新的文件存储到datanode。每个RegionServer使用各自的split WAL回访数据,重建出那个region的Memstore的信息。
Data Recovery
WAL文件包含一系列的edits数据,每个edit代表依次put或delete操作。Edits会按时间顺序重写,这样,为了持久化,新的数据被追加到WAL的最后,存储在磁盘。
如果挂机发生在数据仍然在内存中并且还没被持久化到HFile时会发生什么?WAL已经被replay。replay会将旧的edits写到新的WAL中,并在当前的Memstore中排序。最后,MemStore被flush,将数据写到HFile。
总结:HBase架构的优点
HBase提供如下优点:
- 强一致性模型:当一次write操作范围,所有的read操作看到的都是相同的value
- 自动扩展:Region在过大时会自动split。使用HDFS进行数据多副本化。
- 内置的Recovery:使用Write Ahead Log(类似于文件系统的日志,mysql的binlog)
- 与Hadoop生态整合:在HBase上的MapReduce会非常简单
总结:HBase架构的缺点
HBase的强一致模型带来如下问题:
- WAL replay过程过于慢
- Server Crash和Recovery过程过于慢和复杂
- Major Compaction 带来的I/O风暴。