Hbase学习(十一)---habse的读写流程

版权声明:欢迎读者转载,如果有问题请给与评论。 https://blog.csdn.net/qq_41848006/article/details/87904293

1.hbase的架构图详解(列式存储的非关系型数据库)

hbase是大型分布式数据库,缺少很多RDBMS特性, 如列类型,第二索引,触发器,高级查询语言等。但是HBase 有许多特征同时支持线性化和模块化扩充。hbase集群通过增加regionserver服务器的数量,存储容量和处理事务的速度都有了很大的提升。

2.hbase的特性:

3.什么时候使用hbase?

       1.数据量大:确定数据量有上亿甚至上千亿,hbase是不二之选。如果数据量小的话,RDBMS是最佳选择,因为数据可能在         各位数个机器上就能存储,那么其他机器就会闲置。

        2.确信业务逻辑可以不依赖过多的RDBMS的特性。

         3.有足够的硬件支持(很多的服务器)

4. HBase 和 Hadoop/HDFS 的联系(hdfs为hbase提供底层存储支持)

HDFS 是分布式文件系统,适合保存大文件。官方宣称它并非普通用途文件系统,不提供文件的个别记录的快速查询。 另一方面,HBase基于HDFS且提供大表的记录快速查找(和更新)。这有时可能引起概念混乱。 HBase 内部将数据放到索引好的 "存储文件(StoreFiles)" ,以便高速查询。

5.目录表(-ROOT-  .META.  REGION)注意不同版本的Hbase之间有很大的区别。在中目录结构在hbase 0.94,在hbase 0.98.8就没有这种目录结构了。

目录表 -ROOT- 和 .META. 作为 HBase 表存在。他们被HBase shell的 list 命令过滤掉了, 但他们和其他表一样存在。

 1.ROOT表:存储MEAT表的存放的位置

root表的结构:<key,value>

Key:       .META. region key (.META.,,1)

Values:

2.MEAT表:存储系统中所有的region表

  • 强一致性读写: HBase 不是 "最终一致性(eventually consistent)" 数据存储. 这让它很适合高速计数聚合类任务。
  • 自动分片(Automatic sharding): HBase 表通过region分布在集群中。数据增长时,region会自动分割并重新分布。
  • RegionServer 自动故障转移
  • Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。
  • MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.
  • Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.
  • Thrift/REST API: HBase 也支持Thrift 和 REST 作为非Java 前端.
  • Block Cache 和 Bloom Filters: 对于大容量查询优化, HBase支持 Block Cache 和 Bloom Filters。
  • 运维管理: HBase提供内置网页用于运维视角和JMX 度量.
  • info:regioninfo (序列化.META.的 HRegionInfo 实例 )
  • info:server ( 保存 .META.的RegionServer的server:port)
  • info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)

Key:      Region key 格式 ([table],[region start key],[region id])

values:

 这个是hbase1.3.0的目录树,如下:

 6.hbase1.3.0的目录树的详细

  • info:regioninfo (序列化.META.的 HRegionInfo 实例 )
  • info:server ( 保存 .META.的RegionServer的server:port)
  • info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)

hbase:meta表(以前称为.META.)保留系统中所有区域的列表,并且该位置hbase:meta存储在ZooKeeper中。

hbase:meta表结构如下:

格式的区域键([table],[region start key],[region id]

当一个表处于拆分过程中时,将创建另外两个列,称为info:splitAinfo:splitB。这些列代表两个子区域。这些列的值也是序列化的HRegionInfo实例。区域分割后,最终将删除此行。

  • info:serverstartcode (包含此区域的RegionServer进程的开始时间)

  • info:regioninfo(此区域的序列化HRegionInfo实例)

  • info:server (服务器:包含此区域的RegionServer的端口)

自0.96版本之后,hbase 源码结构上做了很大的优化,目录结构也发生了变化,做了精简和优化,这里以0.98.8为例介绍,目录如下:

/hbase/.tmp

/hbase/WALs

/hbase/archive

/hbase/corrupt

/hbase/data

/hbase/hbase.id

/hbase/hbase.version

/hbase/oldWALs

1、/hbase/.tmp

这个目录不变还是原来的tmp目录,作用是一样的。

2、/hbase/WALs

这里对应0.94的.logs 目录,取名为 WALs 更加见名知意了,点个赞!

3、/hbase/archive

和0.94一样,只是去掉了.而已,估计是作者不想把它作为一个隐藏文件夹了吧

4、/hbase/corrupt

和0.94一样,去了.

5、/hbase/data

这个才是 hbase 的核心目录,0.98版本里支持 namespace 的概念模型,系统会预置两个 namespace 即:hbase和default

5.1 /hbase/data/default

     这个默认的namespace即没有指定namespace 的表都将会flush 到该目录下面。

5.2 /hbase/data/hbase

     这个namespace 下面存储了 HBase 的 namespace、meta 和acl 三个表,这里的 meta 表跟0.94版本的.META.是一样的,自0.96之后就已经将 ROOT 表去掉了,直接从Zookeeper 中找到meta 表的位置,然后通过 meta 表定位到 region。 namespace 中存储了 HBase 中的所有 namespace 信息,包括预置的hbase 和 default。acl 则是表的用户权限控制。

     如果自定义一些 namespace 的话,就会再/hbase/data 目录下新建一个 namespace 文件夹,该 namespace 下的表都将 flush 到该目录下。

6、/hbase/hbase.id

     它是一个文件,存储集群唯一的 cluster id 号,是一个 uuid。

7、/hbase/hbase.version

     同样也是一个文件,存储集群的版本号,貌似是加密的,看不到,只能通过web-ui 才能正确显示出来。

8、/hbase/oldWALs

这里对应0.94的.oldlogs 目录,取名为 oldWALs 是不是更好了呢!

 

7. 这个是hbase0.94的目录树

系统级别的一级目录如下,用户自定义的均在这个/hbase 下的一级子目录下

/hbase/-ROOT-

/hbase/.META.

/hbase/.archive

/hbase/.corrupt

/hbase/.hbck

/hbase/.logs

/hbase/.oldlogs

/hbase/.snapshot

/hbase/.tmp

/hbase/hbase.id

/hbase/hbase.version

1、/hbase/-ROOT-

     hbase读写数据的时候采用三级寻址方式,首先找到从 zk 中找到ROOT 表所在位置,通过 ROOT 表找到 META 表所在位置,然后再从 META 表定位到你要读写数据Region 所在的Regionserver。所以-ROOT-目录对应 HBase 中的系统表ROOT,就不多做解释了。

2、/hbase/.META.

     就是存储1中介绍的 META 表的存储路径。

3、/hbase/.archive

     HBase 在做 Split或者 compact 操作完成之后,会将 HFile 移到.archive 目录中,然后将之前的 hfile 删除掉,该目录由 HMaster 上的一个定时任务定期去清理。

4、/hbase/.corrupt

     存储HBase做损坏的日志文件,一般都是为空的。 

5、/hbase/.hbck

     HBase 运维过程中偶尔会遇到元数据不一致的情况,这时候会用到提供的 hbck 工具去修复,修复过程中会使用该目录作为临时过度缓冲。

6、/hbase/.logs

     大家都知道 HBase 是支持 WAL(预写入日志  Write Ahead Log) 的,HBase 会在第一次启动之初会给每一台 RegionServer 在.log 下创建一个目录,若客户端如果开启WAL 模式,会先将数据写入一份到.log 下,当 RegionServer crash 或者目录达到一定大小,会开启 replay 模式,类似 MySQL 的 binlog。

7、/hbase/.oldlogs

当.logs 文件夹中的 HLog 没用之后会 move 到.oldlogs 中,HMaster 会定期去清理。

8、/hbase/.snapshot

     hbase若开启了 snapshot 功能之后,对某一个用户表建立一个 snapshot 之后,snapshot 都存储在该目录下,如对表test 做了一个 名为sp_test 的snapshot,就会在/hbase/.snapshot/目录下创建一个sp_test 文件夹,snapshot 之后的所有写入都是记录在这个 snapshot 之上。

9、/hbase/.tmp

     当对表做创建或者删除操作的时候,会将表move 到该 tmp 目录下,然后再去做处理操作。

10、/hbase/hbase.id

     它是一个文件,存储集群唯一的 cluster id 号,是一个 uuid。

11、/hbase/hbase.version

     同样也是一个文件,存储集群的版本号,貌似是加密的,看不到,只能通过web-ui 才能正确显示出来。

                     

如果这是完全分布式的话,HRegionServer就是一台服务器,他包括(一个Hlog,多个Hregion),Hregion有包括多个Store,一个Store包括一个MemStore和多个StoreFile,一个Storefile对应一个Hfile。

 Hmaster,Hregionserver,client之间的通信都是RPC.

8.Hmaster结点的架构详解

  • hmaster一般是主服务器的实现,负责监督r所有的egionserver实例。

 9.HRegionserver结点的架构详解

regionserver后台运行这几个线程:

(1)依托ZookeeperWatcher进行的分布式信息共享与任务协调的工作。

MasterAddressTracker:捕获Master服务节点的变化。HBase使用多Master来解决Master单点故障的问题,主Master服务故障时,它与ZooKeeper的心跳延迟超过阈值,ZooKeeeper路径下的数据被清理,备Master上的ActiveMaserManager服务会竞争该Master路径,成为主Master。MasterAddresTracker是RS内部监听Master节点变化的追踪器。

ClusterStatusTracker:HBase集群状态追踪器。该选项可以标识当前集群的状态,以及它的启动时间。该设置选项有利于集群中的各个工作节点(RS)统一执行启动和退出操作。

CatalogTracker:跟踪-ROOT-、.META.表的Region的状态。在HBase支持的-ROOT-、.META.、以及User Region三层树级目录结构中,-ROOT-、.META.表用来定位Region的位置,追踪-ROOT-表和.META.表对应Region的变化,可以时刻保证整个层次目录树的完整性。

SplitLogWorker:基于Region的HLog文件切分器。在RS宕机之后,RS上的保存的HLog文件,需要按照Region进行切分。HMaster会把这些文件作为任务放置到Zookeeper的splitlog路径下,RS上SplitLogWorker会尝试获取任务,对获取到的HLog文件按照Region进行分组,处理的结果保存到相应Region的recovered.edits目录下。

(2)Region的管理

在开始之前首先说明一下RegionServer和Region之间的关系: 
Region是HBase数据存储和管理的基本单位。一个表中可以包含一个或多个Region。每个Region只能被一个RS(RegionServer)提供服务,RS可以同时服务多个Region,来自不同RS上的Region组合成表格的整体逻辑视图

RS内涉及到提供的有关Region维护的服务组件有:

MemStoreFlusher:控制RS的内存使用,有选择性地将Region的MemStore数据写入文件。该组件可以有效地控制RS的内存使用,flush文件的速度在一定程度上可以反应HBase写服务的繁忙状况。

CompactSplitThread:合并文件,清理不需要的数据,控制Region的规模。在Store内的文件个数超过阈值时,触发Compact合并文件操作,一是清理被删除的数据,二是多余版本的清理。在Region内的Store文件大小超过阈值,会触发Region的Split操作,一个Region被切分成两个Region。这两个操作都是在CompactSplitThread的各自的线程池中被触发。

CompactionChecker:周期性检查RS上的Region是否需要进行Compaction操作,确认需要进行Compaction操作的Region,提交给CompactSplitThread执行请求。

RS的内存的使用分为MemStore和BlockCache。其中MemStore提供写操作的缓存,而BlockCache是提供的读请求缓存。它们详细的内容会在后续章节中介绍。

(3)WAL的管理

HBase对于数据的更新和删除操作默认先Append到HLog文件,然后再更新到RS对应的Region上,因此,由HLog文件在RS的处理方式,被称为Write-Ahead-Log。多个Region的更新删除操作会被相继写入同一个文件,出于以下的原因,HLog文件会被截断,然后创建新HLog文件继续当前的Append操作。 
1) Append操作失败,避免因底层文件系统的文件异常,阻塞数据的操作。 
2) 降低存储空间的开销。当HLog上记录的数据完全从MemStore写入HDFS,此时如果多个HLog文件,有利于筛选冗余的HLog文件,提高存储空间的效率。 
3) 提高分布式HLog文件切分操作(Distributed Log Split)的效率。多个HLog文件就对应同样数目的LogSplit子任务,从而可以借助多个RS的SplitLogWorker组件快速完成HLog文件的切分,尽快恢复Region的服务。 
在RS内,LogRoller定期刷新出一个新的HLog文件。

(4)Metrics

Metrics对外提供了衡量HBase内部服务状况的参数。RegionServer内Metrics包含了内存使用、Region服务状况、Compaction、blockCache等一系列标识服务状况的参数。HBase Metrics继承Hadoop Metrics的实现,目前支持文件、Ganglia、以及数据流等多种输出方式,可以针对输出的Metrics信息灵活构建监控系统。

(5)HttpServer

RS内置了一个Jetty Web Server,用来对外提供RS的访问页面。访问页面目前支持实时Metrics信息查询、日志查询、线程的Dump、修改日志级别等操作。

**

RegionServer的启动过程分析

** 
RegionServer服务由org.apache.hadoop.hbase.regionserver.HRegionServer类提供。该类实现了四个接口,分别是HRegionInterface,RegionServerServices,HBaseRPCErrorHandler和Runnable。其中,HRegionInterface定义了RS对外提供的RPC访问接口,通过RPCServer内置的Handler来处理请求;RegionServerServices定义了基于RS内部的服务信息接口,例如onlineRegions增、删、查接口,以及获取HLog、文件系统等接口;HBaseRPCErrorHandler定义了RPCServer异常状态检测处理接口;Runnable是Java库中的线程接口,实现该接口意味着RegionServer生命周期会运行在run()的函数体内。 
RegionServer是一个独立的服务,有一个main函数在启动时被调用,main函数内通过HRegionServerCommandLine的反射机制在JVM内动态加载RegionServer实现类,并按照args解析参数情况,决定启动或者关闭RS服务。

2.hbase读数据机制

和写流程相比,HBase读数据是一个更加复杂的操作流程,这主要基于两个方面的原因:其一是因为整个HBase存储引擎基于LSM-Like树实现,因此一次范围查询可能会涉及多个分片、多块缓存甚至多个数据存储文件;其二是因为HBase中更新操作以及删除操作实现都很简单,更新操作并没有更新原有数据,而是使用时间戳属性实现了多版本。删除操作也并没有真正删除原有数据,只是插入了一条打上”deleted”标签的数据,而真正的数据删除发生在系统异步执行Major_Compact的时候。很显然,这种实现套路大大简化了数据更新、删除流程,但是对于数据读取来说却意味着套上了层层枷锁,读取过程需要根据版本进行过滤,同时对已经标记删除的数据也要进行过滤。
 

总之,把这么复杂的事情讲明白并不是一件简单的事情,为了更加条理化地分析整个查询过程,接下来笔者会用两篇文章来讲解整个过程,首篇文章主要会从框架的角度粗粒度地分析scan的整体流程,并不会涉及太多的细节实现。大多数看客通过首篇文章基本就可以初步了解scan的工作思路;为了能够从细节理清楚整个scan流程,接着第二篇文章将会在第一篇的基础上引入更多的实现细节以及HBase对于scan所做的基础优化。

10.Client-Server交互逻辑

运维开发了很长一段时间HBase,经常有业务同学咨询为什么客户端配置文件中没有配置RegionServer的地址信息,这里针对这种疑问简单的做下解释,客户端与HBase系统的交互阶段主要有如下几个步骤:

1.客户端首先会根据配置文件中zookeeper地址连接zookeeper,并读取/<hbase-rootdir>/meta-region-server节点信息,该节点信息存储HBase元数据(hbase:meta)表所在的RegionServer地址以及访问端口等信息。用户可以通过zookeeper命令(get /<hbase-rootdir>/meta-region-server)查看该节点信息。
2.根据hbase:meta所在RegionServer的访问信息,客户端会将该元数据表加载到本地并进行缓存。然后在表中确定待检索rowkey所在的RegionServer信息。
3.根据数据所在RegionServer的访问信息,客户端会向该RegionServer发送真正的数据读取请求。服务器端接收到该请求之后需要进行复杂的处理,具体的处理流程将会是这个专题的重点。

通过上述对客户端以及HBase系统的交互分析,可以基本明确两点:
1.客户端只需要配置zookeeper的访问地址以及根目录,就可以进行正常的读写请求。不需要配置集群的RegionServer地址列表。
2.客户端会将hbase:meta元数据表缓存在本地,因此上述步骤中前两步只会在客户端第一次请求的时候发生,之后所有请求都直接从缓存中加载元数据。如果集群发生某些变化导致hbase:meta元数据更改,客户端再根据本地元数据表请求的时候就会发生异常,此时客户端需要重新加载一份最新的元数据表到本地。

RegionServer接收到客户端的get/scan请求之后,先后做了两件事情:构建scanner体系(实际上就是做一些scan前的准备工作),在此体系基础上一行一行检索。举个不太合适但易于理解的例子,scan数据就和开发商盖房一样,也是分成两步:组建施工队体系,明确每个工人的职责;一层一层盖楼。


构建scanner体系-组建施工队
scanner体系的核心在于三层scanner:RegionScanner、StoreScanner以及StoreFileScanner。三者是层级的关系,一个RegionScanner由多个StoreScanner构成,一张表由多个列族组成,就有多少个StoreScanner负责该列族的数据扫描。一个StoreScanner又是由多个StoreFileScanner组成。每个Store的数据由内存中的MemStore和磁盘上的StoreFile文件组成,相对应的,StoreScanner对象会雇佣一个MemStoreScanner和N个StoreFileScanner来进行实际的数据读取,每个StoreFile文件对应一个StoreFileScanner,注意:StoreFileScanner和MemstoreScanner是整个scan的最终执行者。


对应于建楼项目,一栋楼通常由好几个单元楼构成(每个单元楼对应于一个Store),每个单元楼会请一个监工(StoreScanner)负责该单元楼的建造。而监工一般不做具体的事情,他负责招募很多工人(StoreFileScanner),这些工人才是建楼的主体。下图是整个构建流程图:

1.RegionScanner会根据列族构建StoreScanner,有多少列族就构建多少StoreScanner,用于负责该列族的数据检索
1.1 构建StoreFileScanner:每个StoreScanner会为当前该Store中每个HFile构造一个StoreFileScanner,用于实际执行对应文件的检索。同时会为对应Memstore构造一个MemstoreScanner,用于执行该Store中Memstore的数据检索。该步骤对应于监工在人才市场招募建楼所需的各种类型工匠。


1.2 过滤淘汰StoreFileScanner:根据Time Range以及RowKey Range对StoreFileScanner以及MemstoreScanner进行过滤,淘汰肯定不存在待检索结果的Scanner。上图中StoreFile3因为检查RowKeyRange不存在待检索Rowkey所以被淘汰。该步骤针对具体的建楼方案,裁撤掉部分不需要的工匠,比如这栋楼不需要地暖安装,对应的工匠就可以撤掉。


1.3 Seek rowkey:所有StoreFileScanner开始做准备工作,在负责的HFile中定位到满足条件的起始Row。工匠也开始准备自己的建造工具,建造材料,找到自己的工作地点,等待一声命下。就像所有重要项目的准备工作都很核心一样,Seek过程(此处略过Lazy Seek优化)也是一个很核心的步骤,它主要包含下面三步:

定位Block Offset:在Blockcache中读取该HFile的索引树结构,根据索引树检索对应RowKey所在的Block Offset和Block Size
Load Block:根据BlockOffset首先在BlockCache中查找Data Block,如果不在缓存,再在HFile中加载
Seek Key:在Data Block内部通过二分查找的方式定位具体的RowKey
整体流程细节参见《HBase原理-探索HFile索引机制》,文中详细说明了HFile索引结构以及如何通过索引结构定位具体的Block以及RowKey


1.4 StoreFileScanner合并构建最小堆:将该Store中所有StoreFileScanner和MemstoreScanner合并形成一个heap(最小堆),所谓heap是一个优先级队列,队列中元素是所有scanner,排序规则按照scanner seek到的keyvalue大小由小到大进行排序。这里需要重点关注三个问题,首先为什么这些Scanner需要由小到大排序,其次keyvalue是什么样的结构,最后,keyvalue谁大谁小是如何确定的:

为什么这些Scanner需要由小到大排序?
最直接的解释是scan的结果需要由小到大输出给用户,当然,这并不全面,最合理的解释是只有由小到大排序才能使得scan效率最高。举个简单的例子,HBase支持数据多版本,假设用户只想获取最新版本,那只需要将这些数据由最新到最旧进行排序,然后取队首元素返回就可以。那么,如果不排序,就只能遍历所有元素,查看符不符合用户查询条件。这就是排队的意义。

工匠们也需要排序,先做地板的排前面,做墙体的次之,最后是做门窗户的。做墙体的内部还需要再排序,做内墙的排前面,做外墙的排后面,这样,假如设计师临时决定不做外墙的话,就可以直接跳过外墙部分工作。很显然,如果不排序的话,是没办法临时做决定的,因为这部分工作已经可能做掉了。

HBase中KeyValue是什么样的结构?
HBase中KeyValue并不是简单的KV数据对,而是一个具有复杂元素的结构体,其中Key由RowKey,ColumnFamily,Qualifier ,TimeStamp,KeyType等多部分组成,Value是一个简单的二进制数据。Key中元素KeyType表示该KeyValue的类型,取值分别为Put/Delete/Delete Column/Delete Family等。KeyValue可以表示为如下图所示:

了解了KeyValue的逻辑结构后,我们不妨再进一步从原理的角度想想HBase的开发者们为什么如此对其设计。这个就得从HBase所支持的数据操作说起了,HBase支持四种主要的数据操作,分别是Get/Scan/Put/Delete,其中Get和Scan代表数据查询,Put操作代表数据插入或更新(如果Put的RowKey不存在则为插入操作、否则为更新操作),特别需要注意的是HBase中更新操作并不是直接覆盖修改原数据,而是生成新的数据,新数据和原数据具有不同的版本(时间戳);Delete操作执行数据删除,和数据更新操作相同,HBase执行数据删除并不会马上将数据从数据库中永久删除,而只是生成一条删除记录,最后在系统执行文件合并的时候再统一删除。


HBase中更新删除操作并不直接操作原数据,而是生成一个新纪录,那问题来了,如何知道一条记录到底是插入操作还是更新操作亦或是删除操作呢?这正是KeyType和Timestamp的用武之地。上文中提到KeyType取值为分别为Put/Delete/Delete Column/Delete Family四种,如果KeyType取值为Put,表示该条记录为插入或者更新操作,而无论是插入或者更新,都可以使用版本号(Timestamp)对记录进行选择;如果KeyType为Delete,表示该条记录为整行删除操作;相应的KeyType为Delete Column和Delete Family分别表示删除某行某列以及某行某列族操作;


不同KeyValue之间如何进行大小比较?
上文提到KeyValue中Key由RowKey,ColumnFamily,Qualifier ,TimeStamp,KeyType等5部分组成,HBase设定Key大小首先比较RowKey,RowKey越小Key就越小;RowKey如果相同就看CF,CF越小Key越小;CF如果相同看Qualifier,Qualifier越小Key越小;Qualifier如果相同再看Timestamp,Timestamp越大表示时间越新,对应的Key越小。如果Timestamp还相同,就看KeyType,KeyType按照DeleteFamily -> DeleteColumn -> Delete -> Put 顺序依次对应的Key越来越大。


2. StoreScanner合并构建最小堆:上文讨论的是一个监工如何构建自己的工匠师团队以及工匠师如何做准备工作、排序工作。实际上,监工也需要进行排序,比如一单元的监工排前面,二单元的监工排之后… StoreScanner一样,列族小的StoreScanner排前面,列族大的StoreScanner排后面。


scan查询-层层建楼
构建Scanner体系是为了更好地执行scan查询,就像组建工匠师团队就是为了盖房子一样。scan查询总是一行一行查询的,先查第一行的所有数据,再查第二行的所有数据,但每一行的查询流程却没有什么本质区别。盖房子也一样,无论是盖8层还是盖18层,都需要一层一层往上盖,而且每一层的盖法并没有什么区别。所以实际上我们只需要关注其中一行数据是如何查询的就可以。


对于一行数据的查询,又可以分解为多个列族的查询,比如RowKey=row1的一行数据查询,首先查询列族1上该行的数据集合,再查询列族2里该行的数据集合。同样是盖第一层房子,先盖一单元的一层,再改二单元的一层,盖完之后才算一层盖完,接着开始盖第二层。所以我们也只需要关注某一行某个列族的数据是如何查询的就可以。


还记得Scanner体系构建的最终结果是一个由StoreFileScanner和MemstoreScanner组成的heap(最小堆)么,这里就派上用场了。下图是一张表的逻辑视图,该表有两个列族cf1和cf2(我们只关注cf1),cf1只有一个列name,表中有5行数据,其中每个cell基本都有多个版本。cf1的数据假如实际存储在三个区域,memstore中有r2和r4的最新数据,hfile1中是最早的数据。现在需要查询RowKey=r2的数据,按照上文的理论对应的Scanner指向就如图所示:

这三个Scanner组成的heap为<MemstoreScanner,StoreFileScanner2, StoreFileScanner1>,Scanner由小到大排列。查询的时候首先pop出heap的堆顶元素,即MemstoreScanner,得到keyvalue = r2:cf1:name:v3:name23的数据,拿到这个keyvalue之后,需要进行如下判定:


检查该KeyValue的KeyType是否是Deleted/DeletedCol等,如果是就直接忽略该列所有其他版本,跳到下列(列族)
检查该KeyValue的Timestamp是否在用户设定的Timestamp Range范围,如果不在该范围,忽略
检查该KeyValue是否满足用户设置的各种filter过滤器,如果不满足,忽略
检查该KeyValue是否满足用户查询中设定的版本数,比如用户只查询最新版本,则忽略该cell的其他版本;反正如果用户查询所有版本,则还需要查询该cell的其他版本。

现在假设用户查询所有版本而且该keyvalue检查通过,此时当前的堆顶元素需要执行next方法去检索下一个值,并重新组织最小堆。即图中MemstoreScanner将会指向r4,重新组织最小堆之后最小堆将会变为<StoreFileScanner2, StoreFileScanner1, MemstoreScanner>,堆顶元素变为StoreFileScanner2,得到keyvalue=r2:cf1:name:v2:name22,进行一系列判定,再next,再重新组织最小堆…


不断重复这个过程,直至一行数据全部被检索得到。继续下一行…

11.hbase写数据机制

  • client与访问zookeeper,得到元数据
  • 通过RPC通信机制与RegionServer通信,首先写入HLog中,
  • 查找对应的region,然后写入MemStore,当memstore满的时候开始溢写到HFile中

猜你喜欢

转载自blog.csdn.net/qq_41848006/article/details/87904293