HBase存储原理、读写原理以及flush和合并过程


文章目录

  • `HBase`存储原理、读写原理以及`flush`和合并过程
  • `HBase`存储原理(架构)
  • `HBase`读原理
  • `HBase`写原理
  • `HBase`数据`flush`刷写过程
  • `HBase`数据`compaction`合并过程



HBase存储原理(架构)

hbase归并处理 hbase的合并机制_hbase归并处理

HBase依赖于ZookeeperHadoop的,所以在启动HBase前需要启动ZookeeperHadoop

HMaster用于管理整个HBase集群,即管理每个HRegionServer,它掌握着整个集群的元数据信息,同时会将相应的数据存储到Zookeeper(元数据信息、高可用信息等)。

  • HMaster的职责:
  • 1)管理用户对Table的增、删、改、查操作;
  • 2)记录 region在哪台 Hregion server 上;
  • 3)Region Split 后,负责新 Region 的分配;
  • 4)新机器加入时,管理 HRegion Server 的负载均衡,调整Region分布;
  • 5)HRegion Server 宕机后,负责失效 HRegion Server 上的Regions 迁移。

HRegionServer是每台机器上的一个Java进程(一台机器只有一个HRegionServer进程),用来处理客户端的读写请求(和HadoopDataNode类似),并维护着元数据信息。

  • HRegionServer的职责:
  • 1) HRegion Server 主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是 HBASE中最核心的模块。
  • 2) HRegion Server 管理了很多 table 的分区,也就是region

每个HRegionServer有一个HLog(有且仅有一个)。HLog是操作日志,用来做灾难恢复的,当客户端发起一个写请求时,会先往HLog中写再往Memory Store中写。假设没有HLog,我们进行一个写请求,会首先写到Memory Store上,等到Memory Store到达一定容量后,才会flushStoreFile中。但是如果在这之前主机断电了呢?那这部分操作的数据全丢失了。这显然不是我们想到的结果,于是就有了HLog

每个HRegionServer里面有多个HRegion,一个HRegion对应于HBase的一张表(也可能是表的一部分,因为表太大了会切分,表和HRegion的对应关系是一对多),当这张表到一定大小的时候会进行切分,切分成两个HRegion,切分出来的新的HRegion会保存到另一台机器上。每个HRegionServer里面有多个HRegion,可以理解为有多张表。

每个HRegion里面有多个Store(一张表中有多个列族),一个Store对应于HBase一张表的一个列族,。按照这个原理,我们在设计列族的时候,可以把经常查询的列放在同一个列族,这样可以提高效率,因为同一个列族在同一个文件里面(不考虑切分)。

每个Store有一个内存级别的存储Memory Store(有且仅有一个)。当Memory Store达到一定大小或一定时间后会进行数据刷写(flush),写到磁盘中(即HFile)。

每个Store有多个磁盘级别的存储StoreFileMemory Store每刷写一次就形成一个StoreFileHFileStoreFileHDFS上的存储格式。

HBase读原理

hbase归并处理 hbase的合并机制_HBase原理、读写过程_02

在上图中,我们模拟一下客户端读取数据过程,假设Zookeeper存放的meta表在RS1机器上,meta表存放的内容如下,Student表行键范围在1~100的存放在RS4上,在101~200的存放在RS3上,等等。

meta表:
_________________________________________________
|    表名    |   rowkey范围    |     所在位置     |
|____________|________________|_________________|
|   Student  |    1 ~ 100     |       RS4       |
|____________|________________|_________________|
|   Student  |  101 ~ 200     |       RS3       |
|____________|________________|_________________|
|   Teacher  |    1 ~ 500     |       RS1       |
|____________|________________|_________________|
|                    ···                        |
|_______________________________________________|

客户端现在要读取Student表的第100行,具体步骤如下:

1. 客户端向Zookeeper发起请求,请求元数据所在RegionServerZookeeper集群存放的是HBasemeta表所在位置。

2. Zookeeper返回给客户端元数据所在RegionServer(即RS1)。

3. 客户端收到应答后去请求RS1,请求查询Student表的rowkey=100数据所在位置。

4.RS1上查询meta表可知该数据在RS4机器上,即返回给客户端rowkey所在位置(RS4)。

5. 客户端收到应答后去请求RS4读数据。

6. RS4查询数据返回给客户端。查询时先去内存(MemStore)查找,因为内存是最新的数据,如果找到了就返回结果,如果没找到则去缓存(cache)找,如果找到了就返回结果,如果还没找到就去磁盘(StoreFile)找,如果在磁盘找到了,则先将结果写入缓存(cache),再返回给客户端,写入缓存是为了下次查询提高效率。

可以发现,在整个读过程中HMaster并没有参与,即读流程与HMaster无关,所以如果HMaster挂了,也是可以读数据的。

HBase写原理

hbase归并处理 hbase的合并机制_元数据_03

HBase的写是比读快的,为什么呢,看下面的写过程,同样假设Zookeeper存放的meta表在RS1机器上,meta表存放的内容如下,Student表行键范围在1~100的存放在RS4上,在101~200的存放在RS3上,等等。

meta表:
_________________________________________________
|    表名    |   rowkey范围    |     所在位置     |
|____________|________________|_________________|
|   Student  |    1 ~ 100     |       RS2       |
|____________|________________|_________________|
|   Student  |  101 ~ 200     |       RS1       |
|____________|________________|_________________|
|   Teacher  |    1 ~ 500     |       RS1       |
|____________|________________|_________________|
|                    ···                        |
|_______________________________________________|

客户端现在要插入数据给Student表,其中rowkey=100,具体步骤如下:

1. 客户端向Zookeeper发起请求,请求元数据所在RegionServerZookeeper集群存放的是HBasemeta表所在位置。

2. Zookeeper返回给客户端元数据所在RegionServer(即RS1)。

3. 客户端收到应答后去请求RS1,请求查询Student表的rowkey=100数据所在位置。

4.RS1上查询meta表可知该数据在RS2机器上,即返回给客户端rowkey所在位置(RS2)。

5. 客户端收到应答后去请求RS4写入数据。

6. RS2收到请求,先将数据写入HLog,再将数据写入MemStore,写入MemStore后就返回给客户端写入成功信息,此时,客户端的写流程完成了。

因为写入内存就结束了写流程,不用访问磁盘,所以总体比读流程是快一点的。

同样,在整个写流程中HMaster也没有参与,所以如果HMaster挂了,也是可以进行写数据的。但是,如果时间长了,表的大小一直变大,而HMaster却挂了,即不会触发Region切分,这样就会导致数据倾斜,系统就变得不安全了。

HBase数据flush刷写过程

hbase-default.xml配置文件中有这么几项配置(见下面),只要regionserver其中某一个MemStore满足第一点或者第二点,都会进行regionserver级别的flush,即所有MemStore都要flush;而满足第三点的,就会进行HRegion级别的flush,即某个HRegion下的所有MemStore都要flush

  • hbase.regionserver.global.memstore.sizeregionServer的全局MemStore的大小,超过该大小会触发flush到磁盘的操作,默认值是堆大小的40%,而且regionserver级别的flush会阻塞客户端读写。说明白点就是当regionserver中所有Memstore的大小加起来达到了当前regionserver堆内存的40%就触发flush操作,不管MemStore有多小,每个MemStore都要进行flush到磁盘。
  • hbase.regionserver.optionalcacheflushinterval:内存中的文件在自动刷新之前能够存活的最长时间,默认是1h,也就是说,当regionserver中某个MemStore存活时间达到了1h,所有MemStore都会进行flush
  • hbase.hregion.memstore.flush.size:单个regionMemStore的缓存大小,超过了这个大小那么整个HRegion就会flush,默认大小为128M

需要注意的是HBase的最小flush单元是HRegion而不是单个MemStore

Flush是由HMaster触发的,Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit)。

HBase数据compaction合并过程

由于在flush过程中,可能会产生很多小文件(这很好理解,比如有两个MemStore,一个很大,一个很小,然后就触发了flush操作,那么那个小的就形成了小文件),我们都知道,HDFS不适合存储小文件,所以在写入HDFS之前会进行合并操作。

hbase-default.xml配置文件中有这么几项配置:

  • hbase.hregion.majorcompaction:一个region进行 major compaction合并的周期,在这个点的时候, 这个region下的所有hfile会进行合并,默认是7天,major compaction非常耗资源,建议生产关闭(设置为0),在应用空闲时间手动触发。
  • hbase.hstore.compactionThreshold:一个store里面允许存的hfile的个数,超过这个个数会被写到新的一个hfile里面 也即是每个region的每个列族对应的memstorefulshhfile的时候,默认情况下当超过3hfile的时候就会对这些文件进行合并重写为一个新文件,设置个数越大可以减少触发合并的时间,但是每次合并的时间就会越长。

在我们利用shell命令或者API删除数据的时候,数据并没有被删除,而是被打上标记,而是在这里的compaction合并过程中才会被完全删除。