1  Master

hbase meta的位置 hbase中hmaster_数据

        HMaster是主服务器的实现。主服务器负责监控集群中所有的regionserver实例,并为所有元数据发生变化提供接口。在一个分布式的集群,Master通常运行在NameNode节点。HMaster没有单点故障问题,可以启动多个HMaster,通过ZooKeeper的Master Election机制保证同时只有一个Hmaster处于Active状态,其他的HMaster则处于热备状态。一般情况下会启动两个HMaster,非Active的HMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。



1.1   启动行为

        如果在多主环境中运行,所有的Master会在运行的集群中竞争。如果active master挂掉,那么剩余的Master会去竞争接管主机的角色。



1.2   运行时的影响

协调HRegionServer:

l  启动时Region的分配,以及负载均衡和修复Region的重新分配。

l  监控集群中所有HRegionServer的状态(通过Heartbeat和监听ZooKeeper中的状态)。



1.3   接口

主要面向元数据的方法,通过HMasterInterface暴露:

l  Table (createTable, modifyTable, removeTable, enable, disable)

l  ColumnFamily (addColumn, modifyColumn, removeColumn)

l  Region (move, assign, unassign), 当Admin的disabletable方法被调用时,这个服务是由Master服务器提供。



1.4   流程

Master运行多个后台线程:

LoadBalancer

        当在过渡时期没有regions时,负载均衡器会将运行和删除region来保证集群的负载均衡,这是周期性的。可以查看 Balancer配置属性。

CatalogJanitor

周期性的检查和清理hbase:meta表。



2  RegionServer

hbase meta的位置 hbase中hmaster_数据_02

        HRegionServer管理一些HRegion对象。一个ResionServer运行在一个DataNode上。每个HRegion对应Table中一个Region,HRegion由多个HStore组成。每个Hstore(HBase存储的核心,由MemStore和StoreFile组成)对应Table中一个Column Family的存储。Column Family就是一个集中的存储单元,故将具有相同IO特性的Column放在一个Column Family会更高效。

数据流程:

1.   Client写入,存入MemStore,一直到MemStore塞满 , Flush成一个StoreFile。

2.   直至增长到一定阈值,触发Compact合并操作,多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除。

3.   当StoreFiles Compact后,逐步形成越来越大的StoreFile,单个StoreFile大小超过一定阈值后,触发Split操作。

4.   把当前Region Split成2个Region,Region会下线,新Split出的2个子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。

5.   由此过程可知,HBase只是增加数据,更新和删除操作,都是在Compact阶段做的,所以,用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能。

引入HLog原因:

        在分布式系统环境中,无法避免系统出错或者宕机,一旦HRegionServer意外退出,MemStore中的内存数据就会丢失,引入HLog就是为了灾备。

HLog工作机制:

        每个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件,HLog文件定期会滚动出新的WAL,并删除旧的文件(已持久化到StoreFile中的数据),这个过程称之为归档(Archiving)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的HLog文件,将不同region的log数据拆分,分别放到相应region目录下,然后再将失效的region重新分配,领取到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。

hbase meta的位置 hbase中hmaster_数据_03

                                如图所示:HRegion

        HBase使用RowKey将表水平切割成多个HRegion,从HMaster的角度,每个HRegion都纪录了它的StartKey和EndKey(第一个HRegion的StartKey为空,最后一个HRegion的EndKey为空),由于RowKey是排序的,因而Client可以通过HMaster快速的定位每个RowKey在哪个HRegion中。HRegion由HMaster分配到相应的HRegionServer中,然后由HRegionServer负责HRegion的启动和管理,和Client的通信,负责数据的读(使用HDFS)。每个HRegionServer可以同时管理1000个左右的HRegion(这个1000的数字是从BigTable的论文中来的(5 Implementation节):Each tablet server manages a set of tablets(typically we have somewhere between ten to a thousand tablets per tablet server))。



2.2   接口

l  Data (get, put, delete, next, etc.)。

l  Region (splitRegion, compactRegion, etc.),例如当Admin的majorCompact方法在表中执行时,客户端实际上遍历了所有的region并且直接向每个region做major compact(合并所有的StoreFile为一个文件)请求。



2.3   流程

l  CompactSplitThread:检查拆分和处理minor compactions(Minor操作只用来做部分文件的合并操作以及包括minVersion=0并且设置ttl的过期版本清理,不做任何删除数据、多版本数据的清理工作)。

l  MajorCompactionChecker:检查major compactions。

l  MemStoreFlusher:周期性的将MemStore的数据刷到StoreFiles中。

l  LogRoller:周期性的检查RegionServer的 WAL。



2.4   协调处理器

协处理器有两种:observer和endpoint。

l  Observer允许集群在正常的客户端操作过程中可以有不同的行为表现。

l  Endpoint允许扩展集群的能力,对客户端应用开放新的运算命令。

在0.92版本中增加。参考这篇博文:

原文:

Blog Overview of CoProcessors



2.5   Block Cache

HBase提供了两种不同的BlockCache,默认的on-heap LruBlockCache和BucketCache。

一般的配置选项详细可参考:

http://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/io/hfile/CacheConfig.html



2.5.1 LruBlockCache设计

LruBlockCache是一个LRU(最近最少使用又叫缓存淘汰)算法实现的cache,包含三级优先级:

l  Single access priority:在一次随机读中,一个BLOCK块从HDFS中加载之后首先放入到Single区域。

l  Multi access priority:后续如果有多次请求访问到Single access priority这块数据的话,就会将这块数据移到mutil-access区。

l  In-memory access priority :in-memory区表示数据可以常驻内存,一般用来存放访问频繁、数据量小的数据,比如元数据,用户也可以在建表的时候通过设置列族属性IN-MEMORY= true将此列族放入in-memory区。

这种设计策略类似于JVM中young区、old区以及perm区。



2.5.2 LruBlockCache使用

计算有多少内存可以缓存HBase的方式:

RegionServers的数量*heap size*hfile.block.cache.size * 0.99

Block cache默认为0.25表示25%的可用堆,示例:

l  一个region server,heap大小为1GB,默认block大小0.25,将会有253MB的block cache可用空间(1*1GB*0.25*0.99)

l  20个region servers,heap大小为8GB,默认block大小0.25,将会有39.6G的block cache可用空间(20*8GB*0.25*0.99)

l  100个region servers,heap大小为24GB,block大小0.5,将会有1.16TB的block cache可用空间(100*24GB*0.5*0.99)



2.5 BucketCache

    HBase将BucketCache和LRUBlockCache搭配使用,称为CombinedBlockCache。和DoubleBlockCache不同,系统在LRUBlockCache中主要存储Index Block和Bloom Block,而将Data Block存储在BucketCache中。因此一次随机读需要首先在LRUBlockCache中查到对应的Index Block,然后再到BucketCache查找对应数据块。

启用CombinedBlockCache:

hbase.bucketcache.ioengine和hbase.bucketcache.size>0

假设Region server已经设置运行5G的heap,即HBASE_HEAPSIZE=5g。

l  hbase-env.sh中设置HBASE_HEAPSIZE=5g

l  hbase-site.xml:

<property>
  <name>hbase.bucketcache.ioengine</name>
  <value>offheap</value>
</property>
<property>
  <name>hfile.block.cache.size</name>
  <value>0.2</value>
</property>
<property>
  <name>hbase.bucketcache.size</name>
  <value>4196</value>
</property>

l  重启集群,并检查日志启动是否存在问题

上面设置了BucketCache为4G,配置了on-heap LruBlockCache为0.2,RegionServer的大小(0.2*5G=1G),还可以配置多个大小,表示从最小到最大,如:

<property>

  <name>hfile.block.cache.sizes</name>

  <value>4096,8192</value>

</property>

这里介绍的非常有限,详细参考官网

http://hbase.apache.org/book.html#regionserver.arch



2.6   RegionServer的拆分实现

        Region server处理的写请求,他们会先堆积在内存存储系统称之为memstore。一旦memstore满了,它的内容就会被写到磁盘中作为额外的存储文件,称之为内存刷新。存储文件的积累,RegionServer会合并它们为较大的文件。之后的每一次刷新或者合并完成存储在该区域的数据量会发生变化。如果region过大或者应该被拆分开来,RegionServer就会使用拆分策略。

从逻辑上讲,拆分过程是简单的。找到一个合适的点一分为二然后分布到两个新的region上。然而并非这么简单,让我们看看其实现的步骤,其中操作来自RegionServers或者HMaster的标记为红色,来自客户端的标记为绿色。

 

1、RegionServer决定本地的region拆分,并准备拆分工作。第一步是,在zookeeper的/hbase/region-in-reansition/region-name下创建一个znode,并设为SPLITTING状态。

2、Master通过父region-in-transition znode的watcher监测到刚刚创建的znode。

3、RegionServer在HDFS中父region的目录下创建名为“.splits”的子目录。

4、RegionServer关闭父region,并强制刷新缓存内的数据,之后在本地数据结构中将标识为下线状态。此时来自Client的对父region的请求会抛出NotServingRegionException ,Client将重新尝试向其他的region发送请求。

5、RegionServer在.splits目录下为子regionA和B创建目录和相关的数据结构。然后RegionServer拆分store文件,这种拆分是指,为父region的每个store文件创建两个Reference文件。这些Reference文件将指向父region中的文件。

6、RegionServer在HDFS中创建实际的region目录,并移动每个子region的Reference文件。

7、RegionServer向.META.表发送Put请求,并在.META.中将父region改为下线状态,添加子region的信息。此时表中并单独存储没有子region信息的条目。Client扫描.META.时会看到父region为拆分状态,但直到子region信息出现在表中,Client才知道他们的存在。如果Put请求成功,那么父region将被有效地拆分。如果在这条RPC成功之前RegionServer死掉了,那么Master和打开region的下一个RegionServer会清理关于该region拆分的脏状态。在.META.更新之后,region的拆分将被Master回滚到之前的状态。

8、RegionServer打开子region,并行地接受写请求。

9、RegionServer将子region A和B的相关信息写入.META.。此后,Client便可以扫描到新的region,并且可以向其发送请求。Client会在本地缓存.META.的条目,但当她们向RegionServer或.META.发送请求时,这些缓存便无效了,他们将重新学习.META.中新region的信息。

10、RegionServer将zookeeper中的znode /hbase/region-in-transition/region-name更改为SPLIT状态,以便Master可以监测到。如果子Region被选中了,Balancer可以自由地将子region分派到其他RegionServer上。

11、拆分之后,元数据和HDFS中依然包含着指向父region的Reference文件。这些Reference文件将在子region发生紧缩操作重写数据文件时被删除掉。Master的垃圾回收工会周期性地检测是否还有指向父region的Reference,如果没有,将删除父region。