上篇博文中已经谈到, 有两个流程没有讲到. 一个是 MetaTableAccessor.getRegionLocations, 另外一个是 ConnectionImplementation.cacheLocation. 这一节, 就让我们单独来介绍这两个流程.
首先让我们来到 MetaTableAccessor.getRegionLocations.
1. 调用 MetaTableAccessor.getRegionInfo, 获取返回结果集中指定的列信息 (info:regioninfo) 的值. 在这个方法的调用过程中, 有一个知识点需要大家关注 --Result.binarySearch. 我将放在后面讲解.
2. 然后调用了 Result.getNoVersionMap. 在这里, 完成了对返回结果集的含 version 版本信息的封装与不含 version 版本信息的封装, 同样, 我将放在后面讲解.
首先让我们来到 Result.binarySearch. 大家可以看到这里使用的 kvs[0]的 rowKey, 然后使用了传入的 family(info)与 qualifier(regioninfo). 大家可能比较迷惑, 为什么这里的逻辑是这样的. 原因很简单, 因为这里传入的 Cell 数组的 rowKey 都是一样的, 要利用 Arrays.binarySearch 搜索指定 family:qualifier. 因此首先使用这些信息构造了一个封装了以上信息的 FirstOnRowColCell. 这里需要注意的是, 新建的 cell.getTimestamp 返回值为 HConstants.LATEST_TIMESTAMP = Long.MAX_VALUE. 这里, 大家可能会对 Arrays.binarySearch 的返回值比较新奇, 为什么结果是负值包括后面为什么有表达式 (pos = (pos+1) * -1). 大家感兴趣的可以追一下源码, 我只简单说一下结论. 在调用 Arrays.binarySearch 方法时, 如果所要搜索的数组中包含键, 则返回键在该数组的位置, 然而, 如果数组中不包含键, 那么就返回 -(insertion point) - 1. 这里的 insertion point 就是该数组中第一个元素大于键的索引位置(the index of the first element greater than the key). 如果大家还是不懂, 在网上搜一下就明白了, 我在这里就不详述了. 后面通过表达式(pos = (pos+1) * -1) 也就获取的 Arrays.binarySearch 后的 insertion point. 看到这里大家可能还有点迷惑, 不过, 相信我在介绍完 CellComparatorImpl 后, 大家可能就恍然大悟了.
接下来让我们来到 CellComparatorImpl.compare 方法. 这里主要调用了 compareRows 与 compareWithoutRow.compareRows 比较简单, 就是比较传入 Cell 的 rowKey. 真正重要的是 compareWithoutRow.
接下来让我们来到 CellComparatorImpl.compareWithoutRow 方法. 这里比较容易误会的是 compareTimestamps.
接下来让我们来到 CellComparatorImpl.compareTimestamps. 正如截图中注释所说, 交换顺序以实现将相同的 family:qualifier 按照时间戳的降序来排列(family 与 qualifier 都是按照升序来排列的). 看到这里, 相信大家就能够明白为什么构建的 Cell 时间戳为 Long.MAX_VALUE.
不过, 我还是在这里再简单介绍一下. 上面我已经提到 Arrays.binarySearch 中 insertion point 是该数组中第一个元素大于键的索引位置(the index of the first element greater than the key). 假如, 如果说这里的 CellComparatorImpl.compareTimestamps 为升序排列, 那么, 上面构造的 key 的 insertion point 为数组中相同 family:qualifier 的 index + 1. 而这里改为降序之后, 构造的 key 的 insertion point 为数组中相同 family:qualifier 的 index. 而这个结果正是我们需要的.
到这里, 大家可能就明白了 Result.getColumnLatestCell 方法的含义 -- 获取指定 family:qualifier 中时间戳最接近 Long.MAX_VALUE 的 cell.
接下来我插入一个知识点 --Result.getMap 与 Result.getNoVersionMap. 这里获取的是含 version 信息的列. 通过其中的 versionMap.put 方法我们就可以知道, 这里将不同 version 的 value 值保存在 map 中了.
然后来到 Result.getNoVersionMap. 在这里获取的是不含 version 的列. 由于上面在构造 versionMap 时传入的 Comparator 为倒序排序, 因此, 这里通过 qualifierEntry.getValue().firstKey()获得的是最新版本的 value.
接下来, 让我们来到本节中另外一个也是最后一个重要的方法 ConnectionImplementation.cacheLocation. 由于其主要调用了 MetaCache.getCachedLocation, 因此, 我在这里贴出 MetaCache.getCachedLocation 源码, 如下图所示. 其中比较重要的方法是 MetaCache.getTableLocations.
接下来让我们来到 MetaCache.getTableLocations, 如下图所示. 如果看过我的上篇博文《HBase 之 Table.put 客户端流程》, 大家可能知道, 我埋了一个伏笔, 也就是这里的最后一个入参. 上一篇中的与这里的入参类型不同, 但是方法的调用流程是一样的, 我就在这里详细讲解.
上图中最后一个入参是 java.util.function.Supplier. 如下图所示.
上图中的最后一个入参类型是 Runnable. 看到这里, 大家可能就明白了. 如果在 MetaCache.cachedRegionLocations 中并没有相应的 key,value 对, 那么就会调用 supplier.get 方法, 也就是 getTableLocations 的最后一个入参, 重新构建一个 CopyOnWriteArrayMap, 并且将内部的比较器设置为 Bytes.BYTES_COMPARATOR. 然后将其放到 MetaCache.cachedRegionLocations.
到此为止, 完整的《HBase 之 Table.put 客户端流程》就结束了. 大家如果有什么疑问或者大数据相关的问题可以发送至我的邮箱 15935152719@163. com.
从下一节起, 也就是本周末, 我将为大家带来 HBase 的第二章内容 --Hbase 之 Client 协议. 届时, Client 协议中的服务端与客户端的完整流程将为大家一一奉上. 如果比较关注其中的内容可以关注我, 或者成为我的粉丝, 都是就可以及时收到更新啦.
来源: https://www.cnblogs.com/letsfly/p/10067904.html