1. 架构原理

HMaster主要负责DDL操作,ZK主要负责Client发送的DML操作,因此当HMaster即使挂掉,也不会影响DML操作,但如果执行DDL操作会报Error:Can’t get master address from zookeeper; znode data ==null,说明Client在执行DDL操作时,实际上也是通过zookeeper找的HMaster再执行DDL操作,而DML操作直接通过zookeeper找regionserver去执行命令。因此在写代码的时候只用配zookeeper地址即可。

image.png

  • **WAL(Hlog)** ——— 由于数据要经MemStore排序后才能刷写到HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做**Write-Ahead logfile(Hlog)**的预写日志文件中,然后再写入MemStore中。所以在系统出现故障的时候,数据可以通过这个日志文件重建
  • **MemStore** ——— 写缓存,由于HFile中的数据要求是有序的,所以数据是先存储在 MemStore中,排好序后,等到达刷写时机才会刷写到 HFile每次刷写都会形成一个新的HFile。
  • **StoreFile** ——— 保存实际数据的物理文件,StoreFile以HFile的形式存储在HDFS上。每个 Store会有一个或多个 StoreFile(HFile),数据在每个 StoreFile中都是有序的

    2. 写流程

    源码写流程(实际就是put操作)——— 直接看HRegion源码,在里面搜STEP1-STEP8

image.png

1)Client 先访问 zookeeper,查询 hbase:meta 表位于哪个 Region Server。 2)访问对应的 Region Server,获取 hbase:meta 表信息,根据读请求的 namespace:table/rowkey,查询出 hbase:meta 表中存储的目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。 3)与目标 Region Server 进行通讯,发送 Put 请求; 4)RegionServer将数据顺序写入(追加)到 WAL中(Hlog预写入日志); 5)再将数据写入对应的 MemStore,数据会在 MemStore 进行排序; 6)向客户端发送 ack; 7)等达到 MemStore 的flush刷写时机后,将数据刷写到 HFile上(即HDFS)。

3. MemStore Flush

flush一次就会生成一次HFile,如果太频繁会生成很多小文件(Hbase小文件处理参考:5.2 StoreFile Compaction删除数据时机

image.png
MemStore 刷写时机:

3.1 region级别

  1. 当某个 memstrore 的大小达到了 **hbase.hregion.memstore.flush.size(默认值 128M)** , 其所在 region 的所有 memstore 都会刷写。
  2. 当 memstore 的大小达到了 **hbase.hregion.memstore.flush.size(默认值 128M)× hbase.hregion.memstore.block.multiplier(默认值 4)** 时,会阻止继续往该 memstore 写请求,并强制刷写。

    3.2 regionServer级别

  3. 当 region server 中 memstore 的总大小达到 **java_heapsize × hbase.regionserver.global.memstore.size(默认值 0.4)× hbase.regionserver.global.memstore.size.lower.limit(默认值 0.95)** ,region 会按照其所有 memstore 的大小顺序(由大到小)依次进行刷写。直到 region server中所有 memstore 的总大小减小到上述值以下。

  4. 当 region server 中 memstore 的总大小达到 **java_heapsize*hbase.regionserver.global.memstore.size(默认值 0.4)** 时,会阻止继续往所有的 memstore 写数据。

    3.3 时间级别

    到达自动刷写的时间,也会触发 memstore flush。自动刷新的时间间隔由该属性进行配置 **hbase.regionserver.optionalcacheflushinterval(默认 1 小时)**

    3.4 WAL文件级别

    当 WAL(预写入日志)文件的数量超过 **hbase.regionserver.max.logs** ,region 会按照时间顺序依次进行刷写,直到 WAL 文件数量减小到 **hbase.regionserver.max.log** 以下(该属性现已经废弃,在hbase内部无需手动设置,最大值为 32)。

    4. 读流程

    image.png

    1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。 2)访问对应的 Region Server,获取 hbase:meta 表数据,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。 3)与目标 Region Server 进行通讯; 4)分别在 Block Cache(读缓存),MemStore(写缓存) 和 Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。 5)将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到Block Cache。 6)将合并后的最终结果返回给客户端。

5. 删除数据时机

flush和major_compact时会删除数据

5.1 flush删除数据时机

  • 更新(多版本) :过期数据和最新数据都在memStore内存中时,直接标记过时的版本,flush时不会落盘过期的数据,即为删除
  • 删除标记 :当某条数据被标记删除时,flush后会删除内存中的数据,但是删除标记会保留,直到触发major_compact后,也会查找已落盘的HFile中的过期版本数据

    5.2 StoreFile Compaction删除数据时机

  • 更新(多版本)minor_compact不会删除数据,而major_compact会,因为在major_compact大合并时会将所有HFile中的数据加载进内存进行比对然后rewrite落盘,因此对比后只保留最新数据,过时的数据会删除

  • 删除标记 :当major_compact时,从内存中保留的删除标记与HFile中已落盘的数据进行对比,删除过期版本数据

    6. Region Split时机

    默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑,HMaster 有可能会将某个 Region 转移给其他的 Region Server。

    注意:一般不使用自动Region Split,因为会导致数据倾斜 解决:使用预分区 + 设计RowKey(参考:Hbase优化

image.png