HBase读写流程简单总结

目录

HBase读写流程简单总结

1、HBase写流程

2、HBase Flush过程

整个 RegionServer 的 MemStore 占用内存总和大于相关阈值

定期自动刷写

Region 中所有 MemStore 占用的内存超过相关阈值

WAL数量大于相关阈值

手动触发刷写

3、HBase读流程

参考


HBase读写流程简单总结

HBase是一个读比写还慢的一个神奇的数据库。

1、HBase写流程

简略的说一下HBase的写流程:

  1. 客户端put 一条数据,假设写入stu表,首先向zookeeper请求meta表所在的regionServer(meta表存储了其他表的信息,而meta表存在zookeeper里)。zookeeper返回meta表所在的regionServer。
  2. 客户端接收到zookeeper返回的信息后,会进行缓存在meta cache缓存中,以便后续快速查找。
  3. 客户端向meta表所在的region Server发送请求,请求查询stu表所在的regionServer,meta表所在的regionServer返回stu表所在的regionServer。(有点绕,看图理解下)
  4. 客户端发送put请求给stu表的regionServer中,regionServer返回ack确认收到的信息。并且写入到WAL预写入日志中。
  5. region Server批量将WAL中的新增数据写入MemStore。

 

 

接下来就是源码层面的解析一下写入操作:

1. try to acquire as many locks as we can, and ensure we acquire at least one. 请求获取一些JUC锁,为了就是要求读写分离。

2. update any LATEST_TIMESTAMP timestamps.   更新最新的时间戳。

3. build WAL edit.  在内存中构建WAL预写入日志。

            walEdit = new WALEdit(cellCount, isInReplay); //创建walEdit对象

4. append the final edit to WAL. Do not sync wal.   追加到WAL日志中,但是不去同步到HDFS。

5. write back to memstore.It is ok to write to memstore first without syncing the WAL, because we do not roll forward the memstore MVCC. the MVCC will be moved up when the complete operation is done.these changes are not yet visible to scanners till we update the MVCC. the MVCC is moved only when the sync is complete.  写入内存。

6. release row rocks, etc. 释放锁。

      // STEP 6. Release row locks, etc.
      if (locked) {
        this.updatesLock.readLock().unlock();
        locked = false;
      }
      releaseRowLocks(acquiredRowLocks);

7. sync wal. 同步到HDFS。doRollBackMemstore = false; // 如果失败会进行回滚--为true时进行回滚

 // -------------------------
      // STEP 7. Sync wal.
      // -------------------------
      if (txid != 0) {
        syncOrDefer(txid, durability);
      }

      doRollBackMemstore = false; // 如果失败会进行回滚--为true时进行回滚
      // calling the post CP hook for batch mutation
      if (!isInReplay && coprocessorHost != null) {
        MiniBatchOperationInProgress<Mutation> miniBatchOp =
          new MiniBatchOperationInProgress<Mutation>(batchOp.getMutationsForCoprocs(),
          batchOp.retCodeDetails, batchOp.walEditsFromCoprocessors, firstIndex, lastIndexExclusive);
        coprocessorHost.postBatchMutate(miniBatchOp);
      }

8. advance mvcc. This will make this put visible to scanners and getters.   更新mvcc(数据库行锁机制),(如果失败会进行回滚)

 // ------------------------------------------------------------------
      // STEP 8. Advance mvcc. This will make this put visible to scanners and getters.
      // ------------------------------------------------------------------
      if (writeEntry != null) {
        mvcc.completeAndWait(writeEntry);
        writeEntry = null;
      } else if (isInReplay) {
        // ensure that the sequence id of the region is at least as big as orig log seq id
        mvcc.advanceTo(mvccNum);
      }

      for (int i = firstIndex; i < lastIndexExclusive; i ++) {
        if (batchOp.retCodeDetails[i] == OperationStatus.NOT_RUN) {
          batchOp.retCodeDetails[i] = OperationStatus.SUCCESS;
        }
      }

9. 执行coprocessor的post方法,put执行postPut,delete执行postDelete.

 	  // ------------------------------------
      // STEP 9. Run coprocessor post hooks. This should be done after the wal is
      // synced so that the coprocessor contract is adhered to.
      // ------------------------------------

回滚源码

      // if the wal sync was unsuccessful, remove keys from memstore //如果没有同步成功,将memstore和wal中的数据全部删除
      if (doRollBackMemstore) {
        for (int j = 0; j < familyMaps.length; j++) {
          for(List<Cell> cells:familyMaps[j].values()) {
            rollbackMemstore(cells);
          }
        }

而且神奇的是,put和delete调用的其实是相同的代码,只是会判断事务是put还是非put。

在这里插入图片描述

 

2、HBase Flush过程

每次flush的时候都会在HDFS当中生成新的Store File,并且同一个region的store是不同的列族,在HDFS当中体现的就是两个不同的文件夹,这样可以做到隔离的好处。如果只查询某个列族时,则不需要进行全盘扫描了。

触发MemStore刷写:

  • 整个 RegionServer 的 MemStore 占用内存总和大于相关阈值
  • 定期自动刷写
  • Region 中所有 MemStore 占用的内存超过相关阈值
  • WAL数量大于相关阈值
  • 数据更新超过一定阈值
  • 手动触发刷写

配置文件当中的属性:

整个 RegionServer 的 MemStore 占用内存总和大于相关阈值

整个regionServer的memstore全部加到一起如果达到或超过了JVM堆(hbase_heapsize)内存的40%(默认40%)的时候,并且flush会阻塞客户端的读写操作。

而在阻塞之前,还有个配置参数表示最低刷写阈值。如果整个 RegionServer 的 MemStore 占用内存总和大于 hbase.regionserver.global.memstore.size.lower.limit  *  hbase.regionserver.global.memstore.size * hbase_heapsize 的时候,将会触发 MemStore 的刷写。其中 hbase.regionserver.global.memstore.size.lower.limit 的默认值为 0.95。也就是说,在默认值的前提下,在占用了JVM堆38%的时候就会开始进行刷写。

这个是属于RegionServer 级别的 Flush 策略是每次找到 RS 中占用内存最大的 Region 对他进行刷写,这个操作是循环进行的,直到总体内存的占用低于全局 MemStore 刷写阈值。需要注意的是,如果达到了 RegionServer 级别的 Flush,那么当前 RegionServer 的所有写操作将会被阻塞,而且这个阻塞可能会持续到分钟级别。

定期自动刷写

RegionServer 在启动的时候会启动一个线程 PeriodicMemStoreFlusher 每隔 hbase.server.thread.wakefrequency 时间去检查属于这个 RegionServer 的 Region 有没有超过一定时间都没有刷写,这个时间是由 hbase.regionserver.optionalcacheflushinterval 参数控制的,默认是 3600000,也就是1小时会进行一次刷写。如果设定为0,则意味着关闭定时自动刷写。为了防止一次性有过多的 MemStore 刷写,定期自动刷写会有 0 ~ 5 分钟的延迟,具体参见 PeriodicMemStoreFlusher 类的实现。

Region 中所有 MemStore 占用的内存超过相关阈值

当一个 Region 中所有 MemStore 占用的内存(包括 OnHeap + OffHeap)大小超过刷写阈值的时候会触发一次刷写,这个阈值由 hbase.hregion.memstore.flush.size 参数控制,默认为128MB。我们每次调用 put、delete 等操作都会检查的这个条件的。但是如果我们的数据增加得很快,达到了 hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier 的大小,hbase.hregion.memstore.block.multiplier 默认值为4,也就是128*4=512MB的时候,那么除了触发 MemStore 刷写之外,HBase 还会在刷写的时候同时阻塞所有写入该 Store 的写请求!这时候如果你往对应的 Store 写数据,会出现 RegionTooBusyException 异常。

WAL数量大于相关阈值

WAL(Write-ahead log,预写日志)用来解决宕机之后的操作恢复问题的。这个配置在新版本当中已经移除,并不是表示不存在了,而且代表不暴露给用户去设置。

手动触发刷写

除了 HBase 内部一些条件触发的刷写之外,我们还可以通过执行相关命令或 API 来触发 MemStore 的刷写操作。比如调用可以调用 Admin 接口提供的方法:

void flush(TableName tableName) throws IOException;
void flushRegion(byte[] regionName) throws IOException;
void flushRegionServer(ServerName serverName) throws IOException;

分别对某张表、某个 Region 或者某个 RegionServer 进行刷写操作。也可以在 Shell 中通过执行 flush 命令:

hbase> flush 'TABLENAME'
hbase> flush 'REGIONNAME'
hbase> flush 'ENCODED_REGIONNAME'
hbase> flush 'REGION_SERVER_NAME'

需要注意的是,以上所有条件触发的刷写操作最后都会检查对应的 HStore 包含的 StoreFiles 文件超过 hbase.hstore.blockingStoreFiles 参数配置的个数,默认值是16。如果满足这个条件,那么当前刷写会被推迟到 hbase.hstore.blockingWaitTime 参数设置的时间后再刷写。在阻塞刷写的同时,HBase 还会请求 Split 或 Compaction 操作。

3、HBase读流程

简单的写一下读流程:

  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),并且Block Cache的策略是LRU策略(最近最少使用算法)。
  5. 将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到Block Cache。
  6. 将合并后的最终结果返回给客户端。

最后说一下为什么HBase读比写慢的原因:

首先,需要明确的是,HBase写入速度比读取速度要快,根本原因LSM存储引擎。

  • Hbase底层的存储引擎为LSM-Tree(Log-Structured Merge-Tree)。
  • LSM核心思想的核心就是放弃部分读能力,换取写入的最大化能力。LSM Tree ,这个概念就是结构化合并树的意思,它的核心思路其实非常简单,就是假定内存足够大,因此不需要每次有数据更新就必须将数据写入到磁盘中,而可以先将最新的数据驻留在内存中,等到积累到最后多之后,再使用归并排序的方式将内存内的数据合并追加到磁盘队尾(因为所有待排序的树都是有序的,可以通过合并排序的方式快速合并到一起)。
  • LSM树的设计思想非常朴素:将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘,不过读取的时候稍微麻烦,需要合并磁盘中历史数据和内存中最近修改操作,所以写入性能大大提升,读取时可能需要先看是否命中内存(这里的内存其实是上图的Block Cache),否则需要访问较多的磁盘文件。极端的说,基于LSM树实现的HBase的写性能比MySQL高了一个数量级,读性能低了一个数量级。
  • LSM树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。

其次,HBase在读取的时候,HBase能提供实时计算服务主要原因是由其架构和底层的数据结构决定的。

即由LSM-Tree(Log-Structured Merge-Tree) + HTable(region分区) + Cache决定。HBase在读取数据的时候,会读取内存数据以及磁盘数据,进行比较时间戳,所以涉及到磁盘的IO的时候,速度基本上不会很快。

HBase做的优化就是,有一个Block Cache读缓存,当从磁盘读取完文件之后,就会进行存入读缓存当中,缓存策略是LRU算法。所以,可以减少扫描磁盘文件的数量,但是对于数据量大的情况下,缓存无法存储全部的数据,所以难以避免需要读取磁盘文件。

 

参考

1  https://blog.csdn.net/qq1010234991/article/details/86180258

2  https://www.bilibili.com/video/BV1Y4411B7jy?p=19&spm_id_from=pageDriver

3  https://www.iteblog.com/

4  https://blog.csdn.net/qq_31821675/article/details/79158353

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值