Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1959650
  • 博文数量: 1000
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7921
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-20 09:23
个人简介

storage R&D guy.

文章分类

全部博文(1000)

文章存档

2019年(5)

2017年(47)

2016年(38)

2015年(539)

2014年(193)

2013年(178)

分类: 服务器与存储

2015-02-10 11:20:53

一、初始化HTable(Configuration conf, final String tableName)
1、用常量"default"与tableName初始化TableName对象,并存入tableCache缓存中;
2、获取HConnection对象(HConnectionImplementation),HConnectionManager.getConnection(conf);
3、初始化线程池pool;
4、设置HTable的参数,包括operationTimeout、operationTimeout、operationTimeout、AsyncProcess对象、Set队列等;   重点介绍获取Set队列(Region服务器的列表):
     1)根据tableName,row ,99999999999999  生成metaKey【注:row值为HConstants.EMPTY_START_ROW=new byte [0],是为了从Region的开始处查找】  
     2)先查找MetaRegion服务器(HRegionServer)地址HRegionLocation(表.META所在的服务器),通过调用ZooKeeperRegistry.getMetaRegionLocation()
方法查找

   3)连接HRegionServer,从.META表中查找满足metaKey的行,然后解析出Region服务器的地址信息(HRegionInfo对象,包括regionname);
  4)根据上一步查到的Region服务器信息初始化Region服务器地址对象HRegionLocation;
  5)将Region服务器的名称存入Set队列中;并把表名存入cachedRegionLocations:ConcurrentMap>中,key为tablename,value为Map(其中,key为startKey,value为HRegionLocation);

二、读过程解析
存储结构背景:
   1)每个HRegionServer维护一个Map队列onlineRegions
   2)每个HRegion维护一个Map队列stores
1、根据解析到的regionname(与初始化HTable一样)从onlineRegions队列中获取HRegion对象;
2、调用HRegion.getClosestRowBefore(byte[] row, byte[] family)获取满足条件的行内容;
3、根据family从stores队列中获取HStore对象;
4、调用HStore.getRowKeyAtOrBefore(byte[] row)获取从包含此row的最近的KeyValue;
5、根据查到的包含条件row的最近的row(KeyValue.getRow())以及条件family构建Get对象;
6、在HRegion内部调用get(Get get)方法;
7、根据get对象初始化Scan对象,
8、根据Scan对象获取RegionScanner对象,实质调用instantiateRegionScanner(Scan scan,List additionalScanners)初始化HRegion$RegionScannerImpl对象,
   最关键的一点:定位! 定scan开始的位置,不过这也是你在客户端调用时指定的,也就是Scan.setStartRow,Scan.setStopRow,如果不指定,那默认就是全表扫描了。具体步骤如下
    1)初始化region、maxResultSize、filter、filter、filter、readPt等变量;
    2)遍历条件family中的列,
     2.1)根据列名从stores队列中获取HStore对象;(假设有个表test,下面有一个Column Family 为A,则HStore表示一个Column Family的存储,也就是对应A)
     2.2)调用HStore对象的getScanner(Scan scan, NavigableSet targetCols)方法获取KeyValueScanner;此方法是构造StoreScanner对象并返回
       new StoreScanner(Store store, ScanInfo scanInfo, Scan scan, final NavigableSet columns)
        1)首先会new一个ScanQueryMatcher,这是一个查询匹配器,关键方法是match,枚举MatchCode指示scan如何处理当前KeyValue。
        2)获取Collection列表,并根据每个StoreFile创建StoreFile.Reader,HFile.Reader用来封装客户端对HFile的open and iterate操作 
        3)通过StoreFile.Reader包装的HFileReaderV2来创建ScannerV2(HFileScanner),这个类能通过使用HFile的索引,快速定位。 
        4)把StoreFile.Reader,ScannerV2包装成StoreFileScanner,然后获取StoreFileScanner对象并存入List列表中;
        5)获取MemStoreScanner 对象的集合List;MemStoreScanner就是引用了MemStore的数据缓存区。 
        6)将上两步的列表合并为一个列表List
        7)对合并的列表,通过selectScannersFrom来选择合适的,返回满足条件的List。主要是StoreFile.Reader的passesTimerangeFilter过滤scan是否过期和passesBloomFilter判断startRow是否命中;
       8)对List列表中的每个KeyValueScanner都定位到开始的row;
       9)将最终合适的列表List 和 KVComparator初始化KeyValueHeap对象StoreScanner.heap;
    2.3)根据filter的条件判断将StoreScanner对象是存入scanners:List或joinedScanners:List中;
    2.4)根据scanners和KVComparator对象构建KeyValueHeap对象RegionScannerImpl.storeHeap:KeyValueHeap(优先级队列)
    2.5)若joinedScanners不为空,则根据joinedScanners和KVComparator对象构建KeyValueHeap对象RegionScannerImpl.joinedHeap;
9、调用HRegion$RegionScannerImpl对象的next(List outResults)方法获取满足要求的行内容,并放入参数outResults队列中,主要步骤在nextInternal(List results, int limit)方法中实现,此方法是一个无限循环处理,达到停止行或达到最后一行或达到查询记录的最大值才退出。主要步骤分析如下:
   1)调用storeHeap.peek()获取队列中的第一个KeyValue:
        内部调用KeyValueHeap.current.peek(); 其中KeyValueHeap.current为队列中的第一个StoreScanner对象(在初始化RegionScannerImpl.storeHeap:KeyValueHeap时设置的);故实质是调用storeHeap队列中第一个StoreScanner对象的peek();
   2)调用RegionScannerImpl.populateResult(List results, KeyValueHeap storeHeap, int limit,byte[] currentRow, int offset, short length)方法获取满足条件的记录并存入results列表中,直到获取的下一行与当前行不匹配为止就跳出到nextInternal()方法,内部调用顺序:storeHeap.next(List result, int limit) ---> (StoreScanner)current.next(List outResult, int limit),最终是在StoreScanner.next(List outResult, int limit)方法中解析满足条件的记录;

三、基本结构: 
HRegion$RegionScannerImpl 
    —KeyValueHeap 
            —StoreScanner 
                  —KeyValueHeap 
                         —StoreFileScanner 
                                —StoreFile.Reader 
                                        —HFileReaderV2 
                                —ScannerV2 
                         —MemStoreScanner 
    
HBase中的scanner实例关系

首先,无论是GET还是scan的读数据,都是从RegionScanner的next接口中获取

第二,scanner可分为两种InternalScanner和KeyValueScanner,区别如下:
1.InternalScanner,可以理解为包含其他scanner的scanner,它的主要接口为next(),作用是从其包含的scanner中获取下一个KeyValue,它的角色可以理解为雇佣KeyValueScanner
2.KeyValueScanner,从内存或文件中获取KeyValue的scanner,它的主要接口有peek(),seek(KeyValue key),next()等,其中next和peek都能获取scanner中的下一个KeyValue,但是next会移动iterator,peek不会,而seek就是将iterator定位到指定的KeyValue,如果不存在该KeyValue则定位到其后面的那个KeyValue,在scanner初始化的时候都会调用下seek接口,它的角色可以理解为服务InternalScanner

所以,RegionScanner、StoreScanner属于InternalScanner,而MemstoreScanner、StoreFileScanner、StoreScanner属于KeyValueScanner

第三,KeyValue 和 KeyValueScanner是可以比较大小的,即他们在优先队列里是排序存放的
1.KeyValue的大小比较规则,优先级从大到小依次为RowKey cf+cq timestamp type,具体点比如说,在比较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

看明白以上3点后,则读的流程就比较好懂了,
1.RegionScanner中有一个scanner的优先队列,当然里面放的是StoreScanner
2.StoreScanner中也有一个scanner的优先队列,里面放着地是MemStoreScanner和StoreFileScanner,
3.RegionScanner通过调用next()获取数据时,其实际是从他的scanner队列中poll出一个StoreScanner,然后调用StoreScanner.next()来获取数据,最后再将该StoreScanner继续添加进优先队列中,从而保证队列中的scanner是一直正确有序的
4.第3点中的StoreScanner.next(),其实际是从他的scanner队列中poll出一个StoreFileScanner或者是MemStoreScanner,然后调用next(),再将该scanner添加进队列中

阅读(799) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~