hbase源码系列(十五)终结篇&Scan续集-->如何查询出来下一个KeyValue
这是这个系列的最后一篇了,实在没精力写了,本来还想写一下hbck的,这个东西很常用,当hbase的Meta表出现错误的时候,它能够帮助我们进行修复,无奈看到3000多行的代码时,退却了,原谅我这点自私的想法吧。
在讲《Get、Scan在服务端是如何处理?》当中的nextInternal流程,它的第一步从storeHeap当中取出当前kv,这块其实有点儿小复杂的,因为它存在异构的Scanner(一个MemStoreScanner和多个StoreFileScanner),那怎么保证从storeHeap里面拿出来的总是离上一个kv最接近的kv呢?
这里我们知道,在打开这些Scanner之后,就对他们进行了一下seek操作,它们就已经调整到最佳位置了。
我们看看KeyValueHeap的构造函数里面去看看吧。
public KeyValueHeap(List<? extends KeyValueScanner> scanners, KVComparator comparator) throws IOException {
this.comparator = new KVScannerComparator(comparator);
if (!scanners.isEmpty()) {
this.heap = new PriorityQueue<KeyValueScanner>(scanners.size(),
this.comparator);
//... this.current = pollRealKV();
}
}
它内部有一个叫heap的PriorityQueue<KeyValueScanner>队列,它会对所有的Scanner进行排序,排序的比较器是KVScannerComparator, 然后current又调用了pollRealKV通过比较获得当前的Scanner,后面会讲。
那好,我们直接进去KVScannerComparator看看它的compare方法就能知道怎么回事了。
public int compare(KeyValueScanner left, KeyValueScanner right) { // 先各取出来一个KeyValue进行比较 int comparison = compare(left.peek(), right.peek());
if (comparison != 0) {
return comparison;
} else {
// key相同,选择最新的那个
long leftSequenceID = left.getSequenceID();
long rightSequenceID = right.getSequenceID();
if (leftSequenceID > rightSequenceID) {
return -1;
} else if (leftSequenceID < rightSequenceID) {
return 1;
} else {
return 0;
}
}
}
额,从上面代码看得出来,把left和right各取出一个kv来进行比较,如果一样就比较SequenceID,SequenceID越大说明这个文件越新,返回-1,在升序的情况下,这个Scanner就跑到前面去了。
这样就实现了heap里面拿出来的第一个就是最小的kv的最新版。
在继续将之前,我们看一下在KeyValue是怎么被调用的,这样我们好理清思路。
//从storeHeap里面取出一个来 KeyValue current = this.storeHeap.peek(); //后面是一顿比较,比较通过,把结果保存到results当中 KeyValue nextKv = populateResult(results, this.storeHeap, limit, currentRow, offset, length);
接着看populateResult方法。
private KeyValue populateResult(List<Cell> results, KeyValueHeap heap, int limit,
byte[] currentRow, int offset, short length) throws IOException {
KeyValue nextKv;
do {
//从heap当中取出剩下的结果保存在results当中
heap.next(results, limit - results.size());
//如果够数了,就返回了
if (limit > 0 && results.size() == limit) {
return KV_LIMIT;
}
nextKv = heap.peek();
} while (nextKv != null && nextKv.matchingRow(currentRow, offset, length));
return nextKv;
}
我们对KeyValueHeap的使用,就是先peek,然后再next,我们接下来就按这个顺序看吧。
先从peek取出来一个,peek就是从heap队列取出来的current的scanner取出来的当前的KeyValue。
if (this.current == null) {
return null;
}
return this.current.peek();
然后我们看next方法。
public boolean next(List<Cell> result, int limit) throws IOException {
if (this.current == null) {
return false;
}
InternalScanner currentAsInternal = (InternalScanner)this.current;
boolean mayContainMoreRows = currentAsInternal.next(result, limit);
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);
}
1、通过currentAsInternal.next继续获取kv,它是只针对通过通过检查的当前行的剩下的KeyValue,这个过程在之前那篇文章讲过了。
2、如果后面没有值了,就关闭这个Scanner。
3、然后还有,就把这个Scanner放回heap上,等待下一次调用。
4、使用pollRealKV再去一个新的Scanner出来。
private KeyValueScanner pollRealKV() throws IOException {
KeyValueScanner kvScanner = heap.poll();
if (kvScanner == null) {
return null;
}
while (kvScanner != null && !kvScanner.realSeekDone()) {
if (kvScanner.peek() != null) {
//查询之前没有查的
kvScanner.enforceSeek();
//把之前的查到位置的kv拿出来
KeyValue curKV = kvScanner.peek();
if (curKV != null) {
//再选出来下一个的scanner
KeyValueScanner nextEarliestScanner = heap.peek();
if (nextEarliestScanner == null) {
// 后面没了,只能是它了
return kvScanner;
}
// 那下一个Scanner的kv也出来比较比较
KeyValue nextKV = nextEarliestScanner.peek();
if (nextKV == null || comparator.compare(curKV, nextKV) < 0) {
// 它确实小,那么就把它放出去吧
return kvScanner;
}
// 把它放回去,和别的kv进行竞争
heap.add(kvScanner);
} else {
// 它没东西了,关闭完事
kvScanner.close();
}
} else {
// 它没东西了,关闭完事
kvScanner.close();
}
kvScanner = heap.poll();
}
return kvScanner;
}
鉴于它每次都要比较的情况,如果一个列族下的HFile比较多的话,它的比较次数也会增大,会影响查询效率,查询时间和HFile的数量成线性关系。
另外补充点内容,是前面写Scan的时候拉下的:
由于写入同一个rowkey相关的KeyValue的时候时间戳在前的先写入,查询的时候又需要总是读该rowkey最新的KeyValue,所以在查询的时候会先seek到该rowkey的时间戳最大的位置,具体查的时候,不断的向前seekBefore,直到这个rowkey的KeyValue全部查完位置,然后再向前定位到一个rowkey的位置。
简而言之:
不同rowkey的向前查,从rowkey小的查到rowkey大的;查相同rowkey的向后查,从最新的时间戳到查到最久的时间戳。
总结:
这就把如何查询出来下一个KeyValue的过程讲完了,它的peek方法、next方法、比较的方法,希望对大家有帮助,这个系列的文章到此也就结束了,下个目标是跟随超哥学习Spark源码,感谢广大读者的支持,觉得我写得好的,可以关注一下我的博客,谢谢!
hbase源码系列(十五)终结篇&Scan续集-->如何查询出来下一个KeyValue的更多相关文章
- Vue.js 源码分析(十五) 指令篇 v-bind指令详解
指令是Vue.js模板中最常用的一项功能,它带有前缀v-,比如上面说的v-if.v-html.v-pre等.指令的主要职责就是当其表达式的值改变时,相应的将某些行为应用到DOM上,先介绍v-bind指 ...
- hbase源码系列(五)Trie单词查找树
在上一章中提到了编码压缩,讲了一个简单的DataBlockEncoding.PREFIX算法,它用的是前序编码压缩的算法,它搜索到时候,是全扫描的方式搜索的,如此一来,搜索效率实在是不敢恭维,所以在h ...
- hbase源码系列(十二)Get、Scan在服务端是如何处理
hbase源码系列(十二)Get.Scan在服务端是如何处理? 继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Del ...
- 10 hbase源码系列(十)HLog与日志恢复
hbase源码系列(十)HLog与日志恢复 HLog概述 hbase在写入数据之前会先写入MemStore,成功了再写入HLog,当MemStore的数据丢失的时候,还可以用HLog的数据来进行恢 ...
- 9 hbase源码系列(九)StoreFile存储格式
hbase源码系列(九)StoreFile存储格式 从这一章开始要讲Region Server这块的了,但是在讲Region Server这块之前得讲一下StoreFile,否则后面的不好讲下去 ...
- C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库
C# DateTime的11种构造函数 别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...
- 11 hbase源码系列(十一)Put、Delete在服务端是如何处理
hbase源码系列(十一)Put.Delete在服务端是如何处理? 在讲完之后HFile和HLog之后,今天我想分享是Put在Region Server经历些了什么?相信前面看了<HTab ...
- HBase源码系列之HFile
本文讨论0.98版本的hbase里v2版本.其实对于HFile能有一个大体的较深入理解是在我去查看"到底是不是一条记录不能垮block"的时候突然意识到的. 首先说一个对HFile ...
- hbase源码系列(十二)Get、Scan在服务端是如何处理?
继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Delete一样,上一篇我本来只打算写Put的,结果发现Delete也可以 ...
随机推荐
- jvm本地实战
前言 由于上次线上full gc,让我这个没有机会实战接触jvm的人,尝到了一定的甜头,同时也觉得自己还有很多东西需要去实战并总结.这是一篇记录jvm配置参数,使用jvisualvm工具来让人对j ...
- SpringMvc 文件下载 详解
最近SSM 需要用到文件下载,以前没用过,在百度上找了好久发现没有一篇博客,对于此段代码进行详细讲解, 这里是本人的个人总结,跟大家分享一下!!!不谢 /** * 文件下载 * ResponseEnt ...
- 12,EasyNetQ-自动订阅
EasyNetQ自v0.7.1.30附带一个简单的AutoSubscriber. 您可以使用它轻松扫描实现接口IConsume <T>或IConsumeAsync <T>的类的 ...
- 大数据量时 Mysql LIMIT如何正确对其进行优化(转载)
以下的文章主要是对Mysql LIMIT简单介绍,我们大家都知道LIMIT子句一般是用来限制SELECT语句返回的实际行数.LIMIT取1个或是2个数字参数,如果给定的是2个参数,第一个指定要返回的第 ...
- 洛谷P1525 关押罪犯
To 洛谷.1525 关押罪犯 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用 ...
- angular.js--demo2-----声明局部控制器controller
<!doctype html><html ng-app="HelloAngular"> <head> <meta charset=&quo ...
- android View的点击无效的原因
点击事件不生效,原来是因为我在里面的 ImageView中添加了 android:clickable="true". 解决办法:删掉ImageView中的android:click ...
- Centos 7 安装 Mysql 5.5 5.6 5.7
环境 [root@node1 ~]# cat /etc/redhat-release CentOS Linux release (Core) [root@node1 ~]# uname -a Linu ...
- cocos creator 入门理解点
简单解释, [来源:官方文档] Cocos是触控科技推出的游戏开发一站式解决方案,包含了从新建立项.游戏制作.到打包上线的全套流程.开发者可以通过cocos快速生成代码.编辑资源和动画,最终输出适合于 ...
- C#6.0特性与vs2015
C#6.0 中的那些新特性 1. 自动属性的初始化 public string Name { get; set; } = "zhangsan"; 2. 只读属性初始化 public ...