HBase-原理总结

先放一张自己总结的图

[图片上传失败…(image-619f9f-1540812453635)]

数据模型

  • 一个RegionServer会管理多个Region,一个表的一段键值会生成一个Region,个别情况一行数据太大也会导致同一段Region根据列族切分为不同Region。
  • 每个Region包含多个Store,一个列族分为一个Store
  • 一个Store只有一个MemStore,和0到多个HFile
  • 一个Store有多个HFile,每次Memstore刷写都会生成一个HFile

角色

Master

虽说是Master,但其实可以理解为打杂的。负责启动的时候分配Region到具体的RegionServer,Master不负责查询相关,只负责创建表、修改列族以及Region分割、合并、移动并等。所以如果Master宕机,集群仍能提供服务,只是不能做表级修改操作了。

RegionServer

负责管理一个或多个Region,是存放Region的容器,一般来说一个服务器只有一个RegionServer,但是也允许启动多个,客户端从Zookeeper获取数据的地址后,会直接从RegionServer读取数据,不经过Master,数据的插入删除也是直接在RegionServer上进行的。

Region

表的一部分数据,相当于关系型数据库中的一个分区,Region不能跨服务器,每个Region包含起始rowkey和结束rowkey来确定其存储的数据范围

Zookeeper

虽然是第三方的组件,但是其重要性要超过Master。读取数据的元数据表hbase:meata的位置存储在Zookeeper上,没有Zookeeper就什么都做不了。Zookeeper负责监控RegionServer的活性,如果RegionServer死了,Zookeeper检测到后会通知Master将数据迁移到其他RegionServer。

预写日志WAL

Write-AHead Log,用来解决宕机之后的操作恢复问题。当操作到达Region的时候,HBase先将数据写入到WAL中,保存在HDFS的/hbase/.logs中。之后才会将数据写到Memstore中,如果超时,那么将其刷写到最终的HFile中,如果过程数据丢失那么就可以通过WAL来恢复,WAL中不区分Store,数据不能被直接读取和使用。
WAL默认是开启的,可以使用下边的语句关闭:

Mutation.setDurability(Durability.SKIP_WAL)

Put/Append/Increment/Delete都是Mutation的子类,都有setDurability。关闭WAL可以让数据导入更快一些,但是一般不建议这么做。不过有一个折中方案,一步写入WAL来实现提高写入性能;正常的WAL(同步)都是在数据来到Region时候先放入内存中,这些改动会立刻被写入WAL,就算只有一个改动也会调用HDFS接口来同步数据。而异步写入会等到条件满足的时候才写入WAL,这里主要使用hbase.regionserver.optionallogflushinterval,也就是每隔多长时间将数据写入WAL,默认1s。设置方式也是setDurability:

Mutation.setDurability(Durability.ASYNC_WAL)

但是异步写入灭有事务保证,异步数据及时写入成功,失败的时候也会丢失,所以除非对系统性能要求极高,对数据一致性要求不高,并且系统的性能总是出现在WAL上的时候才需要考虑异步写入

预写日志滚动机制

WAL是一个环状滚动日志结构。数据放入WAL时候会被写入HDFS的/hbase/.logs目录下,而超过检查时间并且数据已经持久化了,那么就会移动到/hbase/.oldlogs目录下,这个过程叫滚动。之后如果超过TTL时间,或者不需要作为恢复数据的备份了,那么数据会从/hbase.oldlogs中删除,至此一份数据在WAL的旅程就走完了。触发滚动的条件有:

  • 每隔hbase.regionserver.logroll.period时间,检查WAL中数据是否已经被持久化到HDFS上了
  • WAL所在的Block块快要满了
  • WAL所占的空间大于等于阈值:hbase.regionserver.hlog.blocksize * hbase.regionserver.logroll.multiplier(默认0.95)

影响WAL文件从/hbase/.oldlogs完全删除的条件有:

  • TTL进程:该进程保证WAL文件一致存货到hbase.master.logcleaner.ttl定义的超时时间
  • replication被份机制:如果开启HBase备份机制,要保证备份集群已经不需要该WAL了。

MemStore

这个过程相当于我们在打扑克的时候,抽牌之后在手上对牌进行整理的过程。有时候我们会想,既然WAL已经写到HDFS上了,为什么还要再放入MemStore中呢。这是因为HDFS是只允许数据写入和追加,而不允许数据修改,Hbase为了高性能需要保证一个Region的数据是按照数据顺序存放的。而MemStore的意义就在于现在内存中实现数据排序之后在分别写入对应的数据位置。每个Store只有一个MemStore,MemStore内部先将数据整理为LSM树结构,然后再刷写到HFile中。所以区别于正常的理解,Memstore存入内存并不是为了写入快,而且就算增加Memstore大小也不能加快写入速度,Memstore的意义是维持数据按照rowkey顺序排列而不是做一个缓存。缓存有专门的BlockCache来实现。
另外,Memstore可以优化数据的存储,比如有些数据在插入之后马上删除了,刷写的时候就可以直接跳过该数据。LSM树是Google BigTable和Hbase的基本存储算法,注重的是在频繁的数据改动下保持系统读取速度的稳定性,算法的核心是尽量保证数据顺序存储到磁盘上。
当MemStore太大达到了阀值,或者达到了耍些时间间隔阀值,会将内容刷写为HFile。

HFile

MemStore的每一次刷写都会生成一个HFile,很多人管HFile叫做StoreFile,我们可以理解StoreFile就是HFile的抽象类。HFile是由一个个块组成的,在HBase中一个块的默认为64k,由列族上的BlockSize属性定义。HFile存储的数据如图:
[图片上传失败…(image-9edf50-1540812453635)]

这些块区分了不同的角色,有的块只负责存储目标块索引信息,也就是指定块的偏移值(offset):

  • Trailer:必选,存储FileInfo、DataIndex、MetaIndex
  • MetaIndex:可选,存储Meta块索引信息,有Meta块才会有MetaIndex
  • DataIndex:可选,存储Data块索引信息,有Data块才会有DataIndex
  • FileInfo:必选,文件信息,其实也是数据存储块,存储当前文件的信息,比如最后一个Last Key,平均Key的长度(Avg Key Len)等,只有在文件关闭的时候才会写入
  • Meta:可选,元数据块,也是只有在文件关闭的时候才会写入,Meta块存储了该Hfile文件的元数据信息
  • Data:可选,数据块,HBase的数据就存放在这里,虽然是可选,但是很难看到没有Data块的HFile

Data块

上述HFile中最重要的数据存储位置-Data块,其实内部也非常复杂:
[图片上传失败…(image-7ed10c-1540812453635)]

Data块会在第一位存储块的类型,后边存储的是多个keyValue键值对,也就是单元格Cell的实现类。Cell是一个接口,keyValue是它的实现类。
BlockType(块类型)随着Hbase的发展一直在增加,到目前有以下几种:

  • DATA
  • ENCODED_DATA
  • LEAF_INDEX
  • BLOOM_CHUNK
  • META
  • INTERMEDIATE_INDEX
  • ROOT_INDEX
  • FILE_INFO
  • GENERAL_BOOLM_META
  • DELETE_FAMILY_BLOOM_META
  • TRAILER
  • INDEX_V1

KeyValue类(Cell单元)

[图片上传失败…(image-74fd00-1540812453635)]

一个KeyValue类最后一个部分是存储的Value,前面的部分都是存储和该单元格相关的元数据信息,所以会导致有时候value很小,那么单元格大部分空间存储元数据的情况。
因为HDFS是不可修改的,那么HBase是怎么实现怎删改查的呢?其实都是基于增加记录实现的,修改数据的的时候,增加一个记录,只是版本号比之前的大而已。当需要删除一个数据的时候,依旧会在增加一个记录,但是没有value值,他的类型为Delete,被叫做墓碑标记(Tombstone),在major compaction会完全删除。

查询定位

读取数据的时候优先从BlocakCache中找,如果没有再去Memstore和HFile,而不是我们理解的上来直接寻找Memstore。另一个与我们常规认知不同的是,san操作在扫描到包含的数据依旧会继续进行扫描,因为有些数据的墓碑标记是很之前版本分开存放的,至少扫描出的数据大于给定的条件为止,这样它才知道应该返回哪些数据,所以过滤条件的使用并不能加快san速度,只有缩小行键范围才能明显加快扫描速度。另外,scan的过程中,store会创建StoreScanner实例,会把Memstore和HFile结合起来扫描,所以并不是所说的先扫Memstore后扫HFile。

Region定位

早期(0.96.0)版本之前是三层查询架构,这个只做了解。-ROOT- => .META. => Region 这种结构步骤为:

  • 从Zookeeper查询/hbase/root-region-server节点查询-ROOT-表的RegionServer位置
  • 访问-ROOT-表得到.META的RegionServer位置
  • 访问.META.表得到所需行键的Region范围
  • 得到具体数据的RegionServer范围,之后Scan

后来发现,上述的架构可以容纳多大17亿个Region,但实际上不可能用这么多。并且设计上允许多个.META表存在,但发现.META.一直只有一个,-ROOT-只是存了一行数据,形同虚设,并且三层架构较复杂,bug可能性更高。
后来改为了两层架构hbase:meta => Region。将-ROOT-表去掉了,同时zk中的/hbase/root-region-server也去掉了,.META.表改为了/hbase/meta-region-server存储,并把.META.表的名字也修改为hbase:meta了。流程为:

  • 客户端通过zk的/hbase/meta-region-server节点查询hbase:meta在哪个RegionServer上,
  • 客户端直接访问RegionServer的hbase:meta表,查询rowkey的Region范围,以及RegionServer是哪个
  • 客户端连接RegionServer获取rowkey
  • 一般会将meta信息缓存,下次操作就可以直接从第二步开始了,不需要访问zk了。
    原文作者:蠟筆小噺没有烦恼
    原文地址: https://www.jianshu.com/p/e870cb4d610b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞