1 Master
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运行多个后台线程:
l LoadBalancer
当在过渡时期没有regions时,负载均衡器会将运行和删除region来保证集群的负载均衡,这是周期性的。可以查看 Balancer配置属性。
l CatalogJanitor
周期性的检查和清理hbase:meta表。
2 RegionServer
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,完成数据恢复。
如图所示: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版本中增加。参考这篇博文:
原文:
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。