查看原文
其他

可能是最易懂的Hbase架构原理解析

channingbreeze 51CTO技术栈 2019-05-15

小史是一个非科班的程序员,虽然学的是电子专业,但是通过自己的努力成功通过了面试,现在要开始迎接新生活了。


今天,小史的姐姐和吕老师一起过来看小史,一进屋,就有一股难闻的气味。

可不,小史姐姐走进卫生间,发现地下一个盆子里全是没洗的袜子。

小史:当然不是,盆里的袜子满了,就先放到这个桶里,然后再继续装,等到桶里的袜子满了,然后才放到洗衣机里一次洗完,这样不仅效率高,而且节省水电费。

小史洋洋得意地介绍起自己洗袜子的流程。

小史一听就有点不高兴,全世界都黑程序员,没想到自己还没变成程序员就被自家姐姐黑了。

说完就进自己房间,把姐姐和吕老师晾在外面。小史姐姐也意识到不该拿程序员开玩笑,但现在也不知道该怎么办,就看着吕老师。

吕老师走进小史的房间。

HBase是啥


小史:别吹了,构建在 HDFS 上除了能存储海量数据之外,缺点一大堆,上次你给我介绍的 HDFS 缺点我可没忘啊,不支持小文件,不支持并发写,不支持文件随机修改,查询效率也低。


小史仔细回忆起 HDFS 来:

吕老师:HDFS 确实有很多缺点,但是 HBase 却是一个支持百万级别高并发写入,支持实时查询,适合存储稀疏数据的分布式数据库系统。

吕老师:HBase 主要用于大数据领域,在这方面,确实比 MySQL 要厉害得多啊,它和 MySQL 的存储方式就完全不一样。MySQL 是行式存储,HBase 是列式存储。

列式存储


吕老师:没错,这就是行式存储系统存储稀疏数据的问题,我们再来看看列式存储如何解决这个问题,它的存储结构是这样的:

小史:这个我看懂了,相当于把每一行的每一列拆开,然后通过 Rowkey 关联起来,Rowkey 相同的这些数据其实就是原来的一行。

吕老师:你这里只说到了一个好处,由于把一行数据变成了这样的 key-value 的形式。


所以 HBase 可以存储上百万列,又由于 HBase 基于 HDFS 来存储,所以 HBase 可以存储上亿行,是一个真正的海量数据库。

吕老师:这就是 HBase 的威力呀,还不只如此,其实很多时候,我们做 Select 查询的时候,只关注某几列,比如我现在只关心大家的工资,传统的按行存储,要选出所有人的工资是怎么办的呢?


小史:哦,我大概明白了,原来是这样,所以 HBase 的查询效率也很高,但是我有个问题啊,如果我就要查我的所有信息,这是一行数据,HBase 查询起来是不是反而更慢了呢?

列簇


吕老师:列簇,顾名思义,就是把一些列放在一起咯,在 HBase 中,会把列簇中的列存储在一起,比如我们把和工作相关的 Salary 和 Job 都放在 Work 这个列簇下。


那么大概是这样的:

小史:哦,我明白了,这样的话,一个列簇中的列会被一次就拿出来,如果我要查所有列的信息的话,把所有信息都放在一个列簇就好了。

注意:HBase 中,其实所有列都是在列簇中,定义表的时候就需要指定列簇。生产环境由于性能考虑和数据均衡考虑,一般只会用一个列簇,最多两个列簇。


Rowkey 设计


注:当然,有些中间件把 SQL 翻译成 HBase 的查询规则,从而支持了 SQL 查 HBase,不在本文讨论范围内。

小史:啊?这和我想象的不一样啊,如果我想查询工资比 20w 多的记录,在 MySQL 中,只要用一条很简单的 SQL 就行啊,这在 HBase 中怎么查呢?

吕老师:在 HBase 中,你需要把要查询的字段巧妙地设置在 Rowkey 中,一个 Rowkey 你可以理解为一个字符串,而 HBase 就是根据 Rowkey 来建立索引的。

不熟悉 B+ 树的同学可以看这篇文章HBase 的 HFile 底层也是一样的原理。

吕老师:假设员工工资 9999w 封顶,查询的时候可能根据员工工资查询,也可能根据名字查询一个特定的员工,那么 Rowkey 就可以这样设计:

注意:以上 Rowkey 是简化版设计,只是为了讲清楚范围查询。实际使用中由于 Rowkey 需要考虑散列性,所以可能不会这么用。后文会具体探讨散列性。

吕老师:HBase 提供了三种查询方式:

  • 全表扫描,Scan。

  • 根据一个 Rowkey 进行查询。

  • 根据 Rowkey 过滤的范围查询。


比如你要查工资不少于 20w 的记录,就可以用范围查询,查出从 startRow=0020 到 stopRow=9999 的所有记录,这是 HBase 直接支持的一种查询方式哦。

吕老师:这里要注意几点,首先,Rowkey 是按照字符串字典序来组织成 B+ 树的,所以数字的话需要补齐,不然的话会出现 123w 小于 20w 的情况,但是补齐的话,你就会发现 020w 小于 123w。

小史:哦,明白了,这都很好理解,因为 Rowkey 是字符串形式,所以肯定是按照字符串顺序排序咯。


而且 Rowkey 有点类似于 MySQL 中的主键吧,所以保证其唯一性也是可以理解的。


还有就是因为每个 key-value 都包含 Rowkey,所以 Rowkey 越短,越能节省存储空间。

注意:如果 Rowkey 复杂且查询条件复杂,HBase 还针对 Rowkey 提供了自定义 Filter,所以只要数据在 Rowkey 中有体现,能解析,就能根据自己的条件进行查询。

小史:但是吕老师,我有一个问题啊,之前说过 HDFS 不适合存储小文件,而 HBase 中的一条记录只有一点点数据,记录条数却很多,属于海量小文件,存在 HDFS 中不是内存爆炸了吗?

LSM 三层存储模型


小史:哦,这就像把盆里的袜子放到桶里一样。但是吕老师,如果数据量大的话,时间一长,就会有很多次刷写,不就形成了很多个小文件吗?这岂不又是海量小文件了?

不记得 HDFS 原理的同学可以温习一下:

吕老师:对了,HBase 也是使用同样的思想,其实这就是 WAL 预写日志的思想,HBase 也会将数据的操作先写日志,然后存到内存,哪天机器挂了,内存丢了,还能从 WAL 日志中将数据恢复。

数据修改


小史:不过吕老师,我还有问题啊,我记得 HDFS 是不能随机修改文件的,只能追加,那么 HBase 里的数据是不是写了之后就不能改也不能删除呢?

吕老师:删除同样是追加一条版本最新的记录,只不过标记这个数据被删除而已,查询的时候,看到版本最新的记录是数据删除,就知道这个数据被删了。

吕老师:哈,小史,你思考得非常深入,还记得 LSM 的第三层吗,HBase 会在合并的时候,将这些用不到的记录删除掉,节省存储空间。

吕老师:不全对,其实 HBase 把合并分为两种:

  • 小合并 Minor Compact,这种方式只会将少数文件进行简单合并,不会进行数据的清理。

  • 大合并 Major Compact,这种方式会将大部分文件进行合并,并且清理数据。

吕老师:基本正确,但是你要知道,如果数据量大,这个过程是非常耗性能的,一般在生产环境都禁止大合并,否则在正常服务的时候突然来个大合并,整个集群可能资源被耗光,没法正常服务。

HBase 架构



小史:HBase 的架构似乎也是 master-slave 架构,和 HDFS 有点像,HMaster 是用来管理集群,HRegionServer 是真正存储数据的地方吧?

吕老师:啊,这块不太对,HBase 在数据查询和写入的时候,其实并不是像 HDFS 那样询问 HMaster。


在 HBase 中,每一张表都会有元信息,这些信息也是被存储为 HBase 表,称为元信息表,也叫 Meta 表,这是一种系统表。

小史:但是这又有个问题,既然 Meta 表也是存储在 HBase 上,那么 HBase 又如何知道 Meta 表存在哪个 HRegionServer 上呢?这岂不是一个鸡生蛋蛋生鸡的问题?

吕老师:小史啊,我说 Meta 表是 HBase 表,是指 Meta 表也是用 Rowkey 和 Value 的键值存储,但是我并没有说 Meta 表在 HBase 上啊。


其实 Meta 表不是存储在 HRegionServer 上,而是存储在那个分布式协调服务 Zookeeper 上面。

小史:哦,原来如此,所以 Meta 表其实是在一个固定地方读取,然后根据 Meta 表就知道数据在哪个 HRegionServer 上。但是 Zookeeper 又是啥呢?

吕老师:其实 HMaster 的任务相对不繁重,但是却比较重要,它主要是通过调整和管理 Region 分布来实现 HRegionServer 的负载均衡。

HRegionServer 架构


吕老师:其实 Region 是 HBase 在 Rowkey 上的切分,每个 Region 都可以通过 StartKey 和 EndKey 来确定 Rowkey 的范围,一个 HRegionServer 上可能会有多个 Region。

小史:所以说数据是根据 Rowkey 和一定的哈希规则,分散到不同的 Region 上面,而 Region 又是属于某一个 HRegionServer 上的,这个关系没错吧?

吕老师:没错,通过这里其实可以得出 Rowkey 设计的另一个原则,就是散列性。


Rowkey 的头几个字母,最好不要是一样的,不然会分布在同一个 HRegionServer 上面,导致这个 HRegionServer 的负载非常高,累死累活,其他 HRegionServer 却没事干。


一般可以根据一定规则算一个数据的摘要,比如 MD5,把 MD5 的头几位拼在 Rowkey 的前面。

吕老师:哈哈,名词没讲过,原理可都是讲过的哟。比如这个 Store,我们之前说过,一个列簇中的列是存储在一起的,对应到这里,一个列簇中的数据就是存到一个 Store 中。

吕老师:没错,这里 StoreFile 只是一个名字,它是以 HFile 的格式存储在 HDFS 上,HFile 是一个存储格式,在新版本的 HFile 存储格式中,它就是一个类似 B+ 树的索引形式。

读取和写入流程


HBase Client 要写输入了,先从 Zookeeper 中拿到 Meta 表信息,根据数据的 Rowkey 找到应该往哪个 RegionServer 写。


然后 HBase 会将数据写入对应 RegionServer 的内存 MemStore 中,同时记录操作日志 WAL。


当 MemStore 超过一定阈值,就会将内存 MemStore 中的数据刷写到硬盘上,形成 StoreFile。


在触发了一定条件的时候,小的 StoreFile 会进行合并,变成大的 StoreFile,有利于 HDFS 存储。

吕老师:当大量 Rowkey 相近的数据都被分配到一个 Region 中,导致这个 Region 数据过大的时候,Region 会进行拆分,HMaster 会对拆分后的 Region 重新分配 RegionServer,这是 HMaster 的负载均衡策略。

HBase Client 要读数据了,先从 Zookeeper 中拿到 Meta 表信息,根据要查的 Rowkey 找到对应的数据在哪些 RegionServer 上。


分别在这些 RegionServer 上根据列簇进行 StoreFile 和 MemStore 的查找,得到很多 key-value 结构的数据。


根据数据的版本找到最新数据进行返回。

OLTP 和 OLAP


吕老师:OLTP 应用叫联机事务处理应用,就是类似银行转账等业务的,这类应用对事务要求比较高。


而 OLAP 应用叫联机分析处理应用,比如推荐系统,是在收集了大量用户行为后进行分析,再得出结论的应用,主要侧重分析,对事务要求非常低。

总结


小史把这次学习到的 HBase 的知识记了下来:

  • HBase 是列式存储,和 MySQL 的行式存储不一样。

  • HBase 中有列簇概念,同一个列簇下的列存储在一起,在 Region 的一个 StoreFile 中。

  • HBase 是按照 Rowkey 进行查找,要查询的字段要想办法放到 Rowkey 中。

  • HBase 内部使用 LSM 三层模型进行存储,数据先写到内存的 MemStore 中,内存达到一定阈值再刷写到硬盘 StoreFile 中,再满足一定条件时,小的 StoreFile 会合并为大的 StoreFile。

  • HBase 适合 OLAP 类的应用。


学完 HBase,记完笔记,小史开开心心地洗袜子去了。


作者:channingbreeze

编辑:陶家龙、孙淑娟

出处:转载自微信公众号:互联网侦察

精彩文章推荐:

中台是个什么鬼?你想知道的都在这里!

开个脑洞:如果让复联来响应安全事故

终于有人把Elasticsearch原理讲透了!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存