HBase查找一条数据的过程

HBase中的如何路由到正确的
在HBase中 , 大部分的操作都是在完成的 , 端想要插入,删除,查询数据都需要先找到相应的。什么叫相应的?就是管理你要操作的那个的 。本身并 不知道哪个管理哪个,那么它是如何找到相应的的?本文就是在研究源码的基础上揭秘这个过程 。
在前面的文章“HBase存储架构”中我们已经讨论了HBase基本的存储架构 。在此基础上我们引入两个特殊的概念:-ROOT-和.META. 。这是什么?它们是HBase的两张内置表,从存储结构和操作方法的角度来说,它们和其他HBase的表没有任何区别 , 你可以认为这就是两张普通的表 , 对于普通表 的操作对它们都适用 。它们与众不同的地方是HBase用它们来存贮一个重要的系统信息——的分布情况以及每个的详细信息 。
好了,既然我们前面说到-ROOT-和.META.可以被看作是两张普通的表,那么它们和其他表一样就应该有自己的表结构 。没错,它们有自己的表结构,并且这两张表的表结构是相同的,在分析源码之后我将这个表结构大致的画了出来:
我们来仔细分析一下这个结构,每条Row记录了一个的信息 。
首先是,由三部分组成:,和。存储的内容我们又称之为的Name 。哦 , 还记得吗?我们在前面的文章中提到的,用来存放的文件 夹的名字是的Hash值,因为可能包含某些非法字符 。现在你应该知道为什么会包含非法字符 了吧,因为是被允许包含任何值的 。将组成的三个部分用逗号连接就构成了整个,这里使用十进制 的数字字符串来表示的 。这里有一个的例子:
,,
然后是表中最主要的:info,info里面包含三个:,,。其中就是的详细信息,包括,以及每个的信息等等 。存储的就是管理这个的的地址 。
所以当被拆分、合并或者重新分配的时候 , 都需要来修改这张表的内容 。
到目前为止我们已经学习了必须的背景知识,下面我们要正式开始介绍端寻找的整个过程 。我打算用一个假想的例子来学习这个过程 , 因此我先构建了假想的-ROOT-表和.META.表 。
我们先来看.META.表,假设HBase中只有两张用户表:和 , 非常大,被划分成了很多 , 因此 在.META.表中有很多条Row用来记录这些 。而很?。皇潜换殖闪肆礁觯虼嗽?META.中只有两条Row 用来记录 。这个表的内容看上去是这个样子的:
.META.
现在假设我们要从里面插寻一条是的数据 。那么我们应该遵循以下步骤:
1. 从.META.表里面查询哪个包含这条数据 。
2. 获取管理这个的地址 。
3. 连接这个, 查到这条数据 。
好,我们先来第一步 。问题是.META.也是一张普通的表,我们需要先知道哪个管理了.META.表,怎么办?有一个方法,我们把管 理.META.表的的地址放到上面不久行了,这样大家都知道了谁在管理.META. 。
貌似问题解决了,但对于这个例子我们遇到了一个新问题 。因为实在太大了,它的实在太多了,.META.为了存储这些信 息,花费了大量的空间,自己也需要划分成多个 。这就意味着可能有多个在管理.META. 。怎么办?在 里面存储所有管理.META.的地址让自己去遍历?HBase并不是这么做的 。
【HBase查找一条数据的过程】HBase的做法是用另外一个表来记录.META.的信息,就和.META.记录用户表的信息一模一样 。这个表就是-ROOT-表 。这也解释了为什么-ROOT-和.META.拥有相同的表结构,因为他们的原理是一模一样的 。
假设.META.表被分成了两个,那么-ROOT-的内容看上去大概是这个样子的:
-ROOT-
这么一来端就需要先去访问-ROOT-表 。所以需要知道管理-ROOT-表的的地址 。这个地址被存在中 。默认的路径是:
/hbase/root--
等等,如果-ROOT-表太大了,要被分成多个怎么办?嘿嘿,HBase认为-ROOT-表不会大到那个程度,因此-ROOT-只会有一个,这个的信息也是被存在HBase内部的 。
现在让我们从头来过,我们要查询中是的数据 。整个路由过程的主要代码在
org.apache.hadoop.hbase.client.HConnectionManager.TableServers中:private HRegionLocation locateRegion(final byte [] tableName,final byte [] row, boolean useCache)throws IOException{if (tableName == null || tableName.length == 0) {throw new IllegalArgumentException(“table name cannot be null or zero length”);}if (Bytes.equals(tableName, ROOT_TABLE_NAME)) {synchronized (rootRegionLock) {// This block guards against two threads trying to find the root// region at the same time. One will go do the find while the// second waits. The second thread will not do find.if (!useCache || rootRegionLocation == null) {this.rootRegionLocation = locateRootRegion();}return this.rootRegionLocation;}} else if (Bytes.equals(tableName, META_TABLE_NAME)) {return locateRegionInMeta(ROOT_TABLE_NAME, tableName, row, useCache,metaRegionLock);} else {// Region not in the cache – have to go to the meta. RSreturn locateRegionInMeta(META_TABLE_NAME, tableName, row, useCache, userRegionLock);}}
这是一个递归调用的过程:
获取,为的
=>
获取.META.,为,, 的
=>
获取-ROOT- , 为.META.,,,,的
=>
获取-ROOT-的
=>
从得到-ROOT-的
=>
从-ROOT-表中查到最接近(小于)
.META.,,,,的一条Row,并得到.META.的
=>
从.META.表中查到最接近(小于),, 的一条Row,并得到的
=>
从中查到的Row
到此为止完成了路由的整个过程,在整个过程中使用了添加“”后缀并查找最接近(小于)的方法 。对于这个方法大家可以仔细揣摩一下 , 并不是很难理解 。
最后要提醒大家注意两件事情:
在整个路由过程中并没有涉及到,也就是说HBase日常的数据操作并不需要 , 不会造成的负担 。
端并不会每次数据操作都做这整个路由过程,很多数据都会被Cache起来 。至于如何Cache,则不在本文的讨论范围之内 。
引用: