HBase架构从物理结构上看,HBase包括zookeeper、HMaster、regionserver三种服务器,采用主从结构。regionserver主要用于服务读写操作。当用户通过客户端访问数据时,客户端直接与HBaseRegionServer通信。HMaster主要进行regionserver管理、DDL(创建、删除表)操作等。Zookeeper是HDFS(Hadoop分布式文件系统)的一部分,主要用于维护整个集群的生存,保证HA和自动故障转移。底层存储仍然依赖于HDFS。Hadoop的DataNode存储RegionServer管理的数据,HBase的数据全部存储在HDFS中。Hadoop的NameNode维护着所有物理数据块的元数据。1.1regionserverHBase的表按照rowkey的范围进行水平拆分,拆分后分布到各个region。区域包含表中起始键和结束键处的所有行。region会分配给集群中的各个regionserver,用户会和regionserver进行读写交互。一个区域一般推荐5-10G大小。1.2HBaseHMaster一般也称为HMaster。HMaster的主要职责包括两个方面:与regionserver交互,统一管理regionserver:启动时的region分布,崩溃恢复后的region重新分布,负载均衡的region重新分布Admin相关功能:创建,删除等DDL操作,以及更新表结构1.3ZookeeperHBase使用Zookeeper作为分布式协调服务来维护集群中的服务器状态。Zookeeper通过心跳维护哪些服务器处于活动状态和可用状态,并提供服务器故障通知。同时采用共识协议保证各分布式节点的一致性。在这里,我们需要特别注意。Zookeeper负责HMaster的选举。如果一个HMaster节点宕机,会选择另一个HMaster节点进入active状态。1.4这些组件如何协同工作Zookeeper用于共享分布式系统中成员的状态。它与regionserver和HMaster保持会话(active),并通过heartbeat与这些ephemeralnodes(zk中的临时节点概念)保持activesession。在下面,我们可以看到zk在其中起着核心作用。多个HMaster会竞争成为zookeeper上的临时节点,zookeeper会将最先创建成功的HMaster视为当前唯一活跃的HMaster,其他HMaster进入standby状态。活跃的HMaster会不断的向zk发送心跳,其他处于standby状态的HMaster节点会监听活跃的HMaster的故障信息。一旦发现activeHMaster宕机,就会重新竞争新的activeHMaster。这样就实现了HMaster的高可用。每个区域服务器都会创建一个临时节点。HMaster将监控这些节点,以确认哪些区域服务器可用以及哪些节点因故障而宕机。如果一个regionserver或者一个活跃的HMaster没有给zk发送heatbeat,那么与zk的session就会过期,这个临时节点就会在zk上被删除,认为这个节点已经失效,需要下线。其他监听节点会收到故障节点被删除的消息。比如Actvie的HMaster会监听regionserver的消息。如果发现某个regionserver掉线了,会重新分配regionserver来恢复对应的region数据。再比如,standbyHMaster节点会监听activeHMaster节点,一旦收到失败通知,就会竞争上线成为新的activeHMaster。1.5第一次访问HBase时,有一个特殊的HBase目录表,叫做META表,里面存放着集群中各个region的位置。这个元表的位置信息保存在zookeeper中。当我们第一次访问HBase集群时,我们会做以下事情:1)客户端从zk中获取metatable的位置信息,知道metatable存放在哪个regionserver上,并将这个位置信息缓存在客户;2)客户端会查询保存元表的具体regionserver,查询元表信息,获取表中要访问的rowkey所在region的regionserver。3)client直接访问targetregionserver获取对应的row进一步了解metatable的存储结构。元表保存了所有区域信息的表。Meta表存储的数据类似于b-tree,以keyvalue的形式保存数据Key:region的表名,startkey等信息Values:regionserver的相关信息,深入到regionserver的某个region服务器运行在HDFS数据节点上,有以下组件:WAL:全称WriteAheadLog,属于分布式系统上的文件。主要用于存储尚未持久化到磁盘的新数据。如果新数据还没有持久化,节点挂了,那么可以用WAL恢复数据。BlockCache:是读缓存。它存储经常访问的数据。当缓存已满时,清除最近最少访问的数据。MenStore:是写缓存。它存储尚未写入磁盘的数据。它会在写入磁盘之前对自己的数据进行排序,以保证数据的顺序写入。每个区域的每个columfamily都会有一个对应的memstore。(没错,如果节点宕机了,这个缓存中的数据还没有放到磁盘上,可以使用WAL来保证数据不会丢失)HFiles:将每一行的键值存储在字典顺序。2.1HBase写入数据与regionserver的交互整个写入过程比较复杂,其中最重要的部分是与regionserver的交互,这里只介绍与regionserver的交互。主要分为两步,写WAL和写缓存。“其实除了保证数据不丢失外,还关系到提高写入效率,具体我会在后面写一篇相关的文档来说明。”1)写入WAL当客户端提交put请求时,regionserver需要先写入WAL(write-ahead-log)。需要注意三点:hlog是服务器上的region,不是region。写入的数据是日志末尾添加到日志中的数据。主要目的是保证服务器崩溃后,还没有入盘的数据不会丢失。2)写入缓存数据写入WAL成功后,再继续写入MemStore。然后会向客户端返回ack,表示写入成功。2.2HBaseMemStoreMemStore主要是将数据更新保存在内存中,以字典序的KeyValue的形式,与HFile中保存的相同。每个列族都会有一个对应的memstore。更新后的数据会以key-value的形式排序存储在memstore中。注意图片,是按照字典顺序和版本倒序排列的。我们可以看到key的组成包括rowkey-cf-col-version。2.3HBaseregionflush当MemStore存储了足够多的数据后,会将整个有序集写入一个新的HFile文件,存储到HDFS中。HBase中的每个columfamily都会有多个HFiles来存储实际的keyValue。请注意,这解释了为什么HBase中的columnfailies数量有限(多少?)。每个cf都有对应的MemStore。当一个MemStore已满时,该区域中的所有memstores将被刷新到磁盘。所以MemStore的flush的最小单位是region,而不是一个MemStore。在刷新的同时,它还会存储一些额外的信息,比如最后写入的序列号,让系统知道它当前持久化到哪里了。最大的序列号会作为元数据存储在每个HFile中,表示持久化在哪,下一次持久化应该从哪里继续。当一个region开始时,读取每个HFile的序号,然后取最大的序号作为新的起始序号。深入HFile3.1HFile写入HBase,数据以有序KV的形式存储在HFile中。当MemStore存储足够的数据时,所有的kv对被写入HFile并存储在HDFS中。这里写文件的过程是顺序写,避免了硬盘大量移动磁头的过程,比随机写效率高很多。HFile的逻辑结构如图所示:主要分为四部分:扫描块段、非扫描块段、开场数据段和尾部。Scannedblocksection:表示在扫描HFile时,会读取这部分的所有数据块,包括LeafIndexBlock和BloomBlock。Non-scannedblocksection:表示扫描HFile时不读取,主要包括MetaBlock和IntermediateLevelDataIndexBlocks。Load-on-open-section:表示在HBaseregionserver启动时,会加载到内存中。包括FileInfo、Bloomfilterblock、datablockindex和metablockindex。Trailer:表示HFile的基本信息,各部分的偏移值和寻址信息。文件采用类似b+树的多层索引:Kv对按升序存储;根索引指向非叶子节点每个数据块的最后一个键放入中间索引(b+树的非叶子节点)每个数据块都有自己的叶子索引(b+树的叶子节点)。叶子索引通过rowkey指向64kb的kv数据块文件。文件末尾有一个尾节点,指向元块。Trailer节点还保存其他信息,例如布隆过滤器和时间范围信息。Bloomfilter帮助我们过滤掉那些不包含在这个HFilfe中的rowkeys。时间范围信息用于跳过不在此HFilie时间范围内的行。因此,当读取一个HFile时,会将HFile的索引信息缓存在BlockCache中,这样查询只需要一次磁盘查询操作,后续的搜索只需要读取blockcache中的索引信息即可。regionserver上的实体结构关系如下:regionserver:region=1:n,每个regionserver有多个region。region:store=1:n,每个region有多个storestore:memstore=1:1。内存库:Hfile=1:n。
