写操作流程
(1) Client通过ZooKeeper的调度,向RegionServer发出写数据请求,在Region中写数据。
(2) 数据被写入Region的MemStore,直到MemStore达到预设阈值。
(3) MemStore中的数据被Flush成一个StoreFile。
(4) 随着StoreFile文件的不断增多,当其数量增长到一定阈值后,触发Compact合并操作,将多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除。
(5) StoreFiles通过不断的Compact合并操作,逐步形成越来越大的StoreFile。
(6) 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个新的Region。父Region会下线,新Split出的2个子Region会被HMaster分配到相应的RegionServer上,使得原先1个Region的压力得以分流到2个Region上。
可以看出HBase只有增添数据,所有的更新和删除操作都是在后续的Compact历程中举行的,使得用户的写操作只要进入内存就可以立刻返回,实现了HBase I/O的高性能。
读操作流程
(1) Client访问Zookeeper,查找-ROOT-表,获取.META.表信息。
(2) 从.META.表查找,获取存放目标数据的Region信息,从而找到对应的RegionServer。
(3) 通过RegionServer获取需要查找的数据。
(4) Regionserver的内存分为MemStore和BlockCache两部分,MemStore主要用于写数据,BlockCache主要用于读数据。读请求先到MemStore中查数据,查不到就到BlockCache中查,再查不到就会到StoreFile上读,并把读的结果放入BlockCache。
寻址过程:client–>Zookeeper–>-ROOT-表–>META表–>RegionServer–>Region–>client
HBase写流程
插入一条数据到某个表,因为HBase通过ZooKeeper协调,Client 首先连接ZooKeeper,从ZooKeeper中获取表Region相关信息。一般会获取HBase的Region的位置信息。大概步骤为:
- 从ZooKeeper中获取.ROOT.表的位置信息,在ZooKeeper的存储位置为/hbase/root-region-server
- 根据.ROOT.表中信息,获取.META.表的位置信息
- .META.表中存储的数据为每一个region存储位置
根据要插入的rowkey,获取指定的RegionServer信息,如果是批量提交的话,会把rowkey根据HRegion Location进行分组。
当得到了需要访问的Regionserver之后,Client会向对应的Regionserver发起写请求,并将对应的数据发送到该Regionserver检查操作,看Region是不是只读状态,BlockMemorySize大小限制等。
客户端向RegionServer端提交数据的时候,先记录在本地的WAL中,但是不同步到HDFS,然后再把数据写入到MemoryStore,开始将WAL同步到HDFS,最后如果WAL同步成功则结束,如果同步失败则回滚MemoryStore。
写入MemoryStore 和HLog的过程比较复杂,需要获取相关的锁,而且写入MemoryStore 和HLog是原子性的,要么都成功,要么都失败。
当写入MemoryStore的数据大小达到设置的阈值之后,会将MemoryStore flush 成 StroeFile文件,当StoreFile文件增长到一定数量之后,会触发 Compact合并机制,将多个StoreFile 合并为一个大的StoreFile文件,如果Region大到一定的阈值,会将Region一分为二,HBase给两个Region分配相应的Regionserver进行管理,从而分担压力。
Regionserver处理数据的输入输出请求,Regionserver管理多个Region,Region是数据存储的基本单元。
一个Region只存储一个Column Family的数据,或者一个Column Family的一部分。当Region达到一定大小之后,会根据Rowkey的排序,划分多个Region每一个Region又划分多个Store对象,每个Store对象,包括一个MemoryStore 和一个到多个StoreFile,MemoryStore是数据在内存中的实体,并且是有序的。
当有数据写入的时候,会先写入MemoryStore,当MemoryStore达到一定大小的时候会Flush到Storefile,Storefile是HFile的一层封装,HFile存储在HDFS上,所以存储到HBase中的数据最终会存储到HDFS。
Memstore Flush条件
MemoryStore flush是以Region作为单位,而不是单个MemoryStore,当满足条件需要进行MemoryStore flush时会获取该Region上满足条件的store进行MemoryStore flush,这也是为什么官网建议一个表不要定义太多列簇(一个列簇对应一个store),当多个列簇中的一个列簇对应的store中的MemoryStore达到了flush条件,会导致该Region上其他store中的MemoryStore也会进行flush,从而导致flush之后生成Hfile小文件。以下6个条件满足其一就会触发MemoryStore flush:
- Region上的任意一个memstore大小大于
hbase.hregion.memstore.flush.size
的值(默认128M) - 如果region上总的memstore的大小大于
blockingMemStoreSize = hbase.hregion.memstore.block.multiplier*hbase.hregion.memstore.flush.size
,写入操作会报错RegionTooBusyException - 周期性(周期值:
hbase.server.thread.wakefrequency
)线程检查Regionserver上所有的store,当store中memestore的最后一个操作时间与当前时间相差hbase.regionserver.optionalcacheflushinterval
(默认1小时)则需要进行flush - RegionServer上的所有memstore的总内存大于
globalMemStoreLimitLowMark
,则会进行flush,并阻塞,直到所有memstore的总内存小于globalMemStoreLimitLowMark
- 当RegionServer中Hlog的数量达到上限
hbase.regionserver.maxlogs
,则会选取最早一个Hlog对应的一个或多个Region进行flush - 手动执行flush,通过shell命令
flush ‘tablename’
或者flush ‘region name’
分别对一个表或者一个Region进行flush
HBase如何保证内存中数据不会丢失
当HRegionServer意外终止后,Zookeeper上面没收到regionserver心跳,发现掉线,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的 HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取 到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复
HBase storefile合并排序
由于内存里MemStore是在数据插入的时候按照顺序插入,所以MemStore里的数据是有序的。当MemStore的数据刷写到磁盘时,生成的storefile里的数据也是有序的。合并的时候需要将各个有序的storefile合并成一个大的有序的storefile。
首先将各个需要合并的storefile封装成StoreFileScanner最后形成一个List加载到内存,然后再封装成StoreScanner对象,这个对象初始化的时候会对各个StoreFileScanner进行排序放到内部的队列里,排序是按照各个StoreFileScanner最小的rowkey进行排序的。然后通过StoreScanner的next()方法可以拿到各个StoreFileScanner最小rowkey中的最小rowkey对应的KV对。然后就把取出的KV对追加写入合并后的storefile。因为每次取出的都是各个storefile里最小的数据,所以追加写入合并后的storefile里的数据就是按从小到大排序的有序数据。
HBase的大合并和小合并
HBase 根据合并规模将 Compaction 分为了两类:MinorCompaction
和 MajorCompaction
。
Minor Compaction选取一些小的、相邻的StoreFile将它们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。
Major Compaction将所有的StoreFile合并成一个StoreFile,这个过程还会清理三类无意义数据:被删除的数据、TTL过期数据、版本号超过设定版本号的数据。
合并storefile的原因以及什么时候合并
HBase为了防止小文件(被刷到磁盘的menstore)过多,以保证保证查询效率,HBase需要在必要的时候将这些小的store file合并成相对较大的store file。触发compaction的方式有三种:Memstore刷盘、后台线程周期性检查、手动触发。
Memstore Flush
memstore flush会产生HFile文件,文件越来越多就需要compact。因此在每次执行完Flush操作之后,都会对当前Store中的文件数进行判断,一旦文件数大于配置,就会触发compaction。compaction都是以Store为单位进行的,而在Flush触发条件下,整个Region的所有Store都会执行compact,所以会在短时间内执行多次compaction。
后台线程周期性检查
后台线程定期触发检查是否需要执行compaction,检查周期可配置。线程先检查文件数是否大于配置,一旦大于就会触发compaction。如果不满足,它会接着检查是否满足major compaction条件,如果当前store中hfile的最早更新时间早于某个值mcTime,就会触发major compaction(默认7天触发一次,可配置手动触发),HBase预想通过这种机制定期删除过期数据。
手动触发
手动触发compaction通常是为了执行major compaction。需要手动触发合并:担心自动major compaction影响读写性能,因此会选择低峰期手动触发;用户在执行完alter操作之后希望立刻生效,执行手动触发major compaction;HBase管理员发现硬盘容量不够的情况下手动触发major compaction删除大量过期数据。
HBase读流程
先跟Zookeeper建立连接,通过访问Meta Regionserver节点信息,HBase的meta表缓存到本地,获取要访问的表的Region的信息。当Client知道要访问的表在哪个Regionserver之后,Client就对那个Regionserver 发起读请求。
Regionserver接收该读请求之后,经过复杂的处理之后,就将结果返回给客户端。(Regionserver先扫描自己的Memorystore,如果没有找到,就会扫描BlockCache加速读内容的缓冲区,如果还没有找到,就会到StoreFile中查找这条数据,然后将这条数据返回给client),并把读的结果放入BlockCache。BlockCache采用的算法为LRU(最近最少使用算法),因此当BlockCache达到上限后,会启动淘汰机制,淘汰掉最老的一批数据。
为什么Client只需要知道Zookeeper地址就可以了呢?
注意读写流程中,Client对HBase读写请求,与HMaster无关,客户端只需要知道Zookeeper的地址即可。
HMaster启动的时候,会把Meta信息表加载到ZooKeeper。Meta信息表存储了HBase所有的表,所有的Region的详细的信息,比如Region开始的key,结束的key,所在Regionserver的地址。Meta信息表就相当于一个目录,通过它,可以快速定位到数据的实际位置,所以读写数据,只需要跟ZooKeeper对应的Regionserver进行交互就可以了。HMaster只需要存储Region和表的元数据信息,协调各个Regionserver,因此 负载就小了很多。
HBase三大模块如何一起协作的。(HMaster,RegionServer,Zookeeper)
- 当HBase启动的时候发生了什么?
- 如果Regionserver失效了,会发生什么?
- 当HMaster失效后会发生什么?
当HBase启动的时候发生了什么?
HMaster启动的时候会连接zookeeper,将自己注册到Zookeeper,首先将自己注册到Backup Master上,因为可能会有很多的节点抢占Master,最终的Active Master要看他们抢占锁的速度。
将会把自己从Backup Master删除,成为Active Master之后,才会去实例化一些类,比如Master Filesytem,table state manager。当一个节点成为Active Master之后,他就会等待Regionserver汇报。
首先Regionserver注册Zookeeper,之后向HMaster汇报。HMaster现在手里就有一份关于Regionserver状态的清单,对各个Regionserver(包括失效的)的数据进行整理,最后HMaster整理出了一张Meta表,这张表中记录了,所有表相关的Region,还有各个Regionserver到底负责哪些数据等等。然后将这张表,交给Zookeeper。之后的读写请求,都只需要经过Zookeeper就行了。
Backup Master 会定期同步 Active Master信息,保证信息是最新的。如果Regionserver失效了,会发生什么?
如果某一个Regionserver挂了,HMaster会把该Regionserver删除,之后将Regionserver存储的数据,分配给其他的Regionserver,将更新之后meta表,交给Zookeeper。所以当某一个Regionserver失效了,并不会对系统稳定性产生任何影响。当HMaster失效后会发生什么?
如果Active 的HMaster出现故障,处于Backup状态的其他HMaster节点会推选出一个转为Active状态。当之前出现故障的HMaster从故障中恢复,它也只能成为Backup HMaster,等待当前Active HMaster失效了,它才有机会重新成为Active HMaster
对于HA高可用集群,当Active状态的HMaster失效,会有处于Backup 的HMaster可以顶上去,集群可以继续正常运行。如果没有配置HA,那么对于客户端的新建表,修改表结构等需求,因为新建表,修改表结构,需要HMaster来执行,会涉及meta表更新。那么 会抛出一个HMaster 不可用的异常,但是不会影响客户端正常的读写数据请求。
为什么HBase适合写多读少业务
不需要复杂查询条件来查询数据的应用,HBase只支持基于rowkey的查询,对于HBase来说,单条记录或者小范围的查询是可以接受的,大范围的查询由于分布式的原因,可能在性能上有点影响,而对于像SQL的join等查询,HBase无法支持。