`
wangzjie
  • 浏览: 74714 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

hbase读 源码分析(0.96)

阅读更多
一、调用RPC框架:Caller和Callable

主要对象用途

caller主要是进行rpc的重复尝试调用;

callable由caller来调用,负责寻找目标regionserver以及进行rpc调用。

流程图

说明

1、hbase客户端的rpc调用框架主要通过RpcRetryingCaller来调用,它的核心方法callwithRetires()会尝试一定次数直到成功或超时。

2、callWithRetires()中的核心是调用RegionServerCallable的prepare()和call()方法。

prepare:获得目标regionserver的HRegionLocation(在缓存中获取,缓存中找不到则从hbase:meta中定位寻找),以及进行rpc交互的客户端stub。

call:进行probuf的rpc调用。

二、hdfs上的文件结构

hbase:meta数据

通过hbase hfile -p -f /user/hbase/data/hbase/meta/1588230740/info/4308912b2ee74a37be992667d3f3ffbe  可以查看hfile中的值

K: device,,1386514827053.600cdf147050cefc646b3ba5b84c6513./info:/1386515047128/DeleteFamily/vlen=0/mvcc=0 V:
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:regioninfo/1386514885956/Put/vlen=47/mvcc=0 V: PBUF\x08\x92\xD6\xE8\x95\xAD(\x12\x11\x0A\x07default\x12\x06device\x1A\x00"\x09551086096(\x000\x00
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:seqnumDuringOpen/1386514886485/Put/vlen=8/mvcc=0 V: \x00\x00\x00\x00\x00\x00\x01t
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:seqnumDuringOpen/1386514885956/Put/vlen=8/mvcc=0 V: \x00\x00\x00\x00\x00\x00\x00\x01
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:server/1386514886485/Put/vlen=19/mvcc=0 V: 192.168.1.103:60020
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:server/1386514885956/Put/vlen=19/mvcc=0 V: 192.168.1.103:60020
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:serverstartcode/1386514886485/Put/vlen=8/mvcc=0 V: \x00\x00\x01B\xD2\xB7\xE1\x12
K: device,,1386514885394.fcd218f786702d8660a707e96d04ca94./info:serverstartcode/1386514885956/Put/vlen=8/mvcc=0 V: \x00\x00\x01B\xD2\xB7\xE1\x12
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:regioninfo/1386514885956/Put/vlen=47/mvcc=0 V: PBUF\x08\x92\xD6\xE8\x95\xAD(\x12\x11\x0A\x07default\x12\x06device\x1A\x09551086096"\x00(\x000\x00
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:seqnumDuringOpen/1386514886475/Put/vlen=8/mvcc=0 V: \x00\x00\x00\x00\x00\x00\x01u
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:seqnumDuringOpen/1386514885956/Put/vlen=8/mvcc=0 V: \x00\x00\x00\x00\x00\x00\x00\x01
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:server/1386514886475/Put/vlen=19/mvcc=0 V: 192.168.1.103:60020
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:server/1386514885956/Put/vlen=19/mvcc=0 V: 192.168.1.103:60020
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:serverstartcode/1386514886475/Put/vlen=8/mvcc=0 V: \x00\x00\x01B\xD2\xB7\xE1\x12
K: device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416./info:serverstartcode/1386514885956/Put/vlen=8/mvcc=0 V: \x00\x00\x01B\xD2\xB7\xE1\x12
K: hbase:namespace,,1386514747061.b1fc08a6dffc6bf4d781fcdc7fddfd41./info:regioninfo/1386514747384/Put/vlen=39/mvcc=0 V: PBUF\x08\xB5\x9D\xE0\x95\xAD(\x12\x12\x0A\x05hbase\x12\x09namespace\x1A\x00"\x00(\x000\x00
K: hbase:namespace,,1386514747061.b1fc08a6dffc6bf4d781fcdc7fddfd41./info:seqnumDuringOpen/1386514747621/Put/vlen=8/mvcc=0 V: \x00\x00\x00\x00\x00\x00\x00\x01
K: hbase:namespace,,1386514747061.b1fc08a6dffc6bf4d781fcdc7fddfd41./info:server/1386514747621/Put/vlen=19/mvcc=0 V: 192.168.1.103:60020
K: hbase:namespace,,1386514747061.b1fc08a6dffc6bf4d781fcdc7fddfd41./info:serverstartcode/1386514747621/Put/vlen=8/mvcc=0 V: \x00\x00\x01B\xD2\xB7\xE1\x12

device有几个region就会在device下出现多条,device后的名称是device,startRow,建表时间戳.MD5(device,startRow,建表时间戳)中的MD5值
例:

device,,1386514885394.fcd218f786702d8660a707e96d04ca94
device,551086096,1386514885394.3d48139a0840cc6aa349988bc05b4416

各表在hdfs中的位置

hbase:meta : /user/hbase/data/hbase/meta/

其他表:/user/hbase/data/default下,以device表为例(/user/hbase/data/default/device/)看其hdfs文件路径:

/user/hbase/data/default/device下的信息
/user/hbase/data/default/device/.tabledesc  #表信息
/user/hbase/data/default/device/.tmp
#device表有两个region,则其目录下有两个文件夹,命名方式是MD5(device,startRow,建表时间戳)
/user/hbase/data/default/device/3d48139a0840cc6aa349988bc05b4416
/user/hbase/data/default/device/fcd218f786702d8660a707e96d04ca94
 
以3d48139a0840cc6aa349988bc05b4416为例看该region下的信息
/user/hbase/data/default/device/3d48139a0840cc6aa349988bc05b4416/.regioninfo
/user/hbase/data/default/device/3d48139a0840cc6aa349988bc05b4416/info  #info是ColumnFamily=info名称
/user/hbase/data/default/device/3d48139a0840cc6aa349988bc05b4416/action  #info是ColumnFamily=action名称
/user/hbase/data/default/device/3d48139a0840cc6aa349988bc05b4416/recovered.edits
 
/user/hbase/data/default/device/3d48139a0840cc6aa349988bc05b4416/info/25f785b2abb2470ca6b094f2c8f3bd9e就是hfile对应的hdfs文件了

三、get/scan client源码

主要对象说明

HTable: 负责表的get、scan操作。初始化时会创建HConnection对象以及缓存一些hbase:meta中的记录(默认10条)

HConnection/HConnectionManager.HConnectionImplementation:负责定位目标row对应的region(HRegionLocation对象)、缓存rpc stub、缓存相应表的region信息以及与zookeeper、与regionserver建立rpc交互。

HRegionLocation: region对象,包括了region信息(HRegionInfo)以及regionserver服务器信息(ServerName)。

ZookeeperRegistry:与zookeeper进行交互,主要获得hbase:meta的相关信息(meta表所在的regionserver)。

ResultScanner/ClientScanner: client端进行Scan操作的主要对象,其会创建Caller进行rpc调用(该调用中的callable是ScannerCallable)。

ScannerCallable:RegionServerCallable的子类,其call方法中有三个动作:打开scanner、获得数据、关闭scanner。

client get代码

HTable table = new HTable(conf, "device");
Get g = new Get(Bytes.toBytes("200006518"));
Result rs = table.get(g);
for(KeyValue kv : rs.raw()){
    System.out.println(kv);
}

源码流程

 

说明

1、创建HTable对象时会创建HConnectionImplementation对象,其中初始化了zookeeperRegitry专门用于与zookeeper进行交互。

2、HTable.finishSetup():判断目标表是否存在以及预先缓存hbase:meta表中的一些HRegionLocation记录。

3、HConnection.locateRegion()用于定位目标表的HRegionLocation记录。其中会调用:

     a、HConnetion.locateRegionInMeta()用于非meta表,即在hbase:meta表中定位目标表的HRegionLocation记录。

     b、HConnection.registry.getMetaRegionLoaction()用于定位hbase:meta的HReionLocation,从而创建ClientService与habse:meta所在的regionserver进行rpc交互。

4、prefetchRegionCache()预获取hbase:meta中的记录的操作中需要加锁,防止多个线程重复获取。预获取缓存过程是通过MetaScanner来实现的。

5、MetaScanner会先通过getRowOrBefore()尝试获得一条目标表的记录来判断目标表region记录是否存在,再进行scan操作获得相应记录(默认10条)。

6、scan操作主要通过ScannerCallable来实现。每次scan时都会与regionserver进行交互:

     a、打开scanner实始化scannerId。在openScanner()时服务器会保存存key=scannerId, value=RegionScannerHolder的Map,在查询时只要根据scannerId就可以得到对应的InternalScanner以及Region。

     b、rpc ScanRequest操作获得相应记录,在ClientScanner.next()时调用,next()方法第一次调用时获得数据并缓存,以后调用next时只从缓存中取数据。

     c、关闭该次scanner

7、ClientScanner的next()在缓存无数据时,如果ClientScanner未关闭(即currentRegion的endKey非空或者未达到stopRow),还会使用ScannerCallable进行下一轮scan。否则scan操作就结束。

8、HTable创建完成后,就开始调用HTable.get/scan方法(scan方法使用的是前面的ClientScanner逻辑)。get方法中就直接调用caller来进行rpc,其中调用callable的prepare()时,会从创建HTable时预先得到的region缓存中获得HRegionLocation,如果找不到的话就会重新去loateRegion。

HRegionLocation缓存

Map<TableName, SoftValueSortedMap<byte[], HRegionLocation>> cachedRegionLocations:  即<tableName,<startKey, HRegionLocation>>

四、读server端

HRegionServer上的get操作最终也会转化成Scan操作。而Scan的操作主要使用Hbase上的各种scanner(相当于迭代器)。hbase返回的结果会按KeyValue从小到大的顺序返回。

Scanner类图

scanner说明

scanner可分成四类:

1、InternalScanner: 可以认为是scanner的scanner,其每个元素都有子scanner。如RegionScanner中有StoreScanner,StoreScanner中有KeyValueScanner。

2、KeyValueScanner: 用于访问KeyValue值,主要是MemStoreScanner访问MemStore中的KeyValue和StoreFileScanner访问StoreFile中的KeyValue。

查询时由于目标数据还在memstore中未flush到storefile中 或已经多次flush到多个storefile,所以这些都是Scanner的目标。

3、KeyValueHeap: 保存了KeyValueScanner的优先队列。

4、HFileScanner: 访问HFile。

其中InternalScanner的next方法是next(List<Cell> result), 而KeyValueScanner的next方法没有参数,直接返回KeyValue。

next方法是获取数据并往后移,peek方法是获取数据但不往后移。

get/scan源码过程

说明

1、scanner初始化过程

     a、在Scan过程中先创建RegionScanner对象,其中会创建其familyColumn下的各个StoreScanner并存在RegionScanner的storeHeap中。

     b、StoreScanner在初始化时会分别从MemStore和StoreFile中获得Scanner并存到StoreScanner.heap中。其中的selectScannersFrom()方法会调用KeyValueScanner.shouldUseScanner()进行bloom filter(只针对storefile)、time range、memOnly、filesOnly等过滤。

2、scanner遍历过程

     a、起点:RegionScanner.next(),会调用其storeHeap.next方法即得到StoreScanner

     b、StoreScanner.next()会循环调用其heap.next()获得相应的KeyValueScanner,并调用MemStoreScanner/StoreFileScanner的next()方法。

优先级队列中scanner的大小比较规则

1、KeyValue的大小比较规则,优先级从大到小依次为RowKey cf+cq timestamp type, 除了ts是降序外,其他都是升序。比如说,在比较2个KeyValue时,先比较RowKey的大小('a' < 'b'),相同的情况下比较cf+cq的大小('cf1:q1'<'cf2:q1'<'cf2:q2'),如果还是相同的话就比较时间戳(3042211081<3042211080,注意 时间戳的long值越大,表示数据越新,在从小到大的队列中越靠前),如果上述仍然还相同则比较TYPE('DeleteFamily' < 'DeleteColumn' < 'Delete' < Put) 

2、KeyValueScanner的大小比较规则:其大小有peek()即第一个元素(即最小的)获取到KeyValue大小决定,即 KeyValueScanner1.peek() < KeyValueScanner2.peek() 则KeyValueScanner1 < KeyValueScanner2

优先队列KeyValueHeap.next(List<Cell> result):InternalScanner.next(List<Cell> result)

public boolean next(List<Cell> result, int limit) throws IOException {
    if (this.current == null) {
      return false;
    }
    InternalScanner currentAsInternal = (InternalScanner)this.current;//regionScanner中的storeHeap中的storeScanner走此路
    boolean mayContainMoreRows = currentAsInternal.next(result, limit);//这个是StoreScanner.next()
    KeyValue pee = this.current.peek();
    if (pee == null || !mayContainMoreRows) {
      this.current.close();
    else {
      this.heap.add(this.current);//还有数据则再加入到优先队列中
    }
    this.current = pollRealKV();
    return (this.current != null);
  }

优先队列KeyValueHeap.next():KeyValueScanner.next()

public KeyValue next()  throws IOException {
    if(this.current == null) {
      return null;
    }
    KeyValue kvReturn = this.current.next();//取出当前KeyValueScanner的要返回的KeyValue,并移除该元素
    KeyValue kvNext = this.current.peek();  //取出当前KeyValueScanner的下个元素
    if (kvNext == null) {
      this.current.close();
      this.current = pollRealKV();
    else {
      KeyValueScanner topScanner = this.heap.peek();//只取第一个数据不移除,即获得位于优先队列的头KeyValueScanner
      if (topScanner == null ||
          this.comparator.compare(kvNext, topScanner.peek()) >= 0) {//如果当前KeyValueScanner大于头Scanner,则下一个要取的数据位于头Scanner中。
        this.heap.add(this.current); //把当前的KeyValueScanner放到优先队列中排序
        this.current = pollRealKV();//取新的KeyValueScanner
      }
    }
    return kvReturn;
  }

KeyValueScanner.next说明

1、优先队列的初始化:

      a、在添加storefile前会根据timestamp,columns,bloomfilter过滤掉一部分。同时,storefile中最大的rowkey比当前查询的rowkey小的记录也会被过滤。

      b、storefile的内部有三维有序的,但是各个storefile之间并不是有序的。比如,storefile1中可能有rowkey为100到110的记录,而storefile2可能有rowkey为105到115的数据,所以storefile之间的rowkey的范围很有可能有交叉,所以查询数据的过程也不可能是对storefile的顺序查找。hbase采用的是优先队列方式来存储,其排序算法是根据上面的KeyValueScanner从小到大的顺序进行排序。

2、查询(以KeyValueScanner都是storefile为例,MemStoreScanner和其一样的规则):

      a、获取数据。通过poll取出队列的头storefile,会从storefile读取一条记录返回。

      b、决定下一条要scanner的KeyValue。当前storefile的下条记录并不一定是查询结果的下一条记录,因为队列的比较顺序是比较的每个storefile的第一条符合要求的KeyValue。所以,hbase会继续从队列中剩下的storefile取第一条记录,把该记录与当前storefile的第二条记录做比较,如果前者大,那么返回当前storefile的第二条记录;如果后者大,则会把当前storefile放回队列重新排序,在重新取队列的头storefile。然后重复上面的整个过程。

3、举例:表device,有两个storefile,storefile1中包括rowkey100,rowkey110;storefile2中包括rowkey104,rowkey108。 执行scan ‘device′扫描表device中的所有的记录。

根据前面提到的排序规则,队列中会有2个元素,按顺序分别为storefile1,storefile2。

       a、取出storefile1中的第一条记录rowkey100,并返回该结果

       b、取出storefile1中的下一条记录rowkey110,同时取出队列剩余storefile的第一条记录rowkey104,经过比较rowkey110大于rowkey104,则将storefile1放回队列中

       c、因为队列是有序的队列,会重新对storefile进行排序,因为此时storefile1的最小rowkey为110,而storefile2的最小rowkey为104,所以排序的结果为storefile2,storefile1

       d、重复上面的过程,直到查不到记录为止。

 最后查到的结果为:rowkey100,rowkey104,rowkey108,rowkey110。

BlockCache:从storefile中获得相应KeyValue时会先从BlockCache中查看是否有该数据,有则在BlockCache中获得数据,否则就得去查询HFile数据。

 

MemStore以及StoreFile以及HFile这几块还未深入研究,待更新...

 

note:图片请右键在新页面打开

分享到:
评论

相关推荐

    HBase源码分析与开发实战

    HBase源码分析与开发实战视频技术讲解高阶视频教程以及课件,内部讲解资料 内容非常详细 值得想要提高薪水的人去学习了解

    HBase源码分析

    HBase源码分析揭示了HBase在RPC通信机制方面的一些关键技术点,这包括了角色分配、通信信道建立、通信接口协议定义、对象序列化、传输控制和会话管理,以及在传输过程中可能出现的错误处理和重试机制。 HBase中的...

    hbase-0.96 jar包

    这个“hbase-0.96 jar包”是HBase 0.96版本的二进制发行版,它包含了运行和开发HBase应用所需的所有库文件。HBase 0.96是一个重要的里程碑,因为它在性能和稳定性上都有显著提升,特别是在处理大规模数据集时。 ...

    HBase实战源码

    源码分析是理解HBase工作原理和技术细节的重要途径。HBase在大数据领域扮演着关键角色,它能够处理海量数据并提供实时访问。下面,我们将深入探讨HBase的核心概念和源码中的关键组件。 1. **HBase架构**:HBase基于...

    hbase 的java代码 集合 hbase 0.96

    HBase 0.96版本是一个较早但仍然重要的发行版,它引入了许多功能和改进,使得处理大规模数据变得更加高效。下面我们将深入探讨HBase 0.96版本中的关键Java编程知识点。 首先,理解HBase的基础架构至关重要。HBase是...

    基于Java开发的分布式NoSQL数据库HBase设计源码分析

    该项目为基于Java开发的分布式NoSQL数据库HBase的设计源码,包含5289个文件,涵盖各类编程语言和文件类型,其中Java源文件4465个,Ruby脚本221个,XML配置112个,Protobuf定义66个,以及少量Shell、Python、...

    hadoop2.6及hbase0.96伪分布式安装配置文件

    HBase 0.96则是基于Hadoop的数据库,设计为高度可扩展的列式数据库,适用于实时查询。 **Hadoop 2.6的伪分布式安装** 在伪分布式模式下,所有的Hadoop组件都运行在一个单一节点上,这对于测试和学习Hadoop的环境...

    hbase源码包和测试用例

    HBase的源码分析有助于理解其内部工作原理。例如,`HRegionServer`是数据服务的主要组件,负责Region的管理和数据操作;`HMaster`负责Region的分配和负载均衡;`HStore`管理Column Family,包含一系列的`HStoreFile...

    hbase-0.98.1源码包

    源码包“hbase-0.98.1-src.tar.gz”提供了HBase 0.98.1版本的完整源代码,对于理解其内部工作原理、进行二次开发或调试是非常有价值的。 HBase的核心概念包括: 1. 表:HBase中的表由行和列族组成,表名全局唯一。...

    如何使用Java API操作Hbase(基于0.96新的api)

    在Java中操作HBase,尤其是基于0.96版本的新API,涉及到许多核心概念和方法。HBase是一个分布式的、可扩展的列式数据库,它建立在Hadoop之上,适用于实时读写大数据。本篇文章将深入探讨如何使用Java API与HBase进行...

    使用kafka,spark,hbase开发日志分析系统

    本使用kafka,spark,hbase开发日志分析系统。 ![architecture](/docs/images/architecture.png "architecture") ### 软件模块 * Kafka:作为日志事件的消息系统,具有分布式,可分区,可冗余的消息服务功能。...

    hbase权威指南源码

    通过分析和实践《HBase权威指南》的源码,读者不仅可以深化理论知识,还能掌握实际操作技巧,为解决实际项目中的问题提供有力支持。对于想深入理解HBase工作原理和优化技巧的开发者来说,这份源码是一份宝贵的资源。

    Hadoop2.2+Zookeeper3.4.5+HBase0.96集群环境搭建

    Hadoop2.2+Zookeeper3.4.5+HBase0.96集群环境搭建 Hadoop2.2+Zookeeper3.4.5+HBase0.96集群环境搭建是大数据处理和存储的重要组件,本文档将指导用户从零开始搭建一个完整的Hadoop2.2+Zookeeper3.4.5+HBase0.96集群...

    java操作Hbase之从Hbase中读取数据写入hdfs中源码

    在Java编程环境中,操作HBase并将其数据写入HDFS(Hadoop Distributed File System)是一项常见的任务,特别是在大数据处理和分析的场景下。本篇将详细介绍如何使用Java API实现这一功能,以及涉及到的关键技术和...

    hbase源码分析

    ### HBase源码分析 #### 一、HBase性能测试要点与分析 ##### 1.1 测试环境 - **硬件配置**: - 客户端:1台 - RegionServer:5台 - Master:1台 - ZooKeeper:3台 - **软件配置**: - CPU:每台服务器配备8...

    hbase 1.2.0源码

    HBase 1.2.0是该数据库的一个稳定版本,包含了众多优化和改进,对于想要深入理解HBase工作原理或者进行大数据分析的学习者来说,研究其源码是非常有价值的。 一、HBase架构与核心概念 1. 表与Region:HBase中的...

    hbase 源码包

    HBase 0.94.4的源码分析有助于我们深入了解其内部机制,从而更好地进行系统设计和优化。无论是对于开发者还是管理员,掌握HBase的核心原理都将极大地提升在大数据领域的实践能力。通过不断学习和实践,我们可以更好...

    HBase性能深度分析

    ### HBase性能深度分析 HBase,作为BigTable的一个开源实现,因其卓越的分布式数据库特性在大数据处理领域占据了重要地位。然而,随着HBase在各行业的广泛应用,用户对其性能表现的关注日益增强,尤其是实时数据...

Global site tag (gtag.js) - Google Analytics