3.1.2 HBase简介

“小明哥,我大概了解了Hadoop这种分布式的工作模式。可是我平时一般都是通过数据库来编程的,仅仅有分布式的文件系统还不够啊。”

“这是一个好问题。下面我来介绍一下基于HDFS的数据库系统。”

与传统的文件系统相似,HDFS解决了数据存储的基本问题。可是,作为文件系统,HDFS同样面临一个问题:缺乏良好的数据组织和访问,对于开发大型应用而言,这实在是不太方便,因此需要一个类似传统关系型数据库的管理系统。在此大环境下,Apache HBase(http://hbase.apache.org/)应运而生了。HBase是Apache的Hadoop项目的子项目,当前最新版本是1.1.2。它是一个分布式的、面向列的开源数据库,该技术的开发同样是受Google的一篇论文所启发,即《Bigtable:一个结构化数据的分布式存储系统》(“Bigtable:A Distributed Storage System for Structured Data”)。Bigtable利用了Google文件系统GFS来提供分布式数据存储,类似地,HBase是在Hadoop的HDFS基础之上提供了Bigtable的能力。Hadoop和Database两个英文单词的叠加也是HBase英文名称的由来。HBase不同于一般的关系数据库,它是一个适合于非结构化数据的数据库,最大的特点是基于列而不是基于行的模式进行存储。那么,HBase为什么要选择这样的NoSQL的设计方式呢?如此的设计又能带来哪些好处呢?为了更好地理解这些,我们先来简单回顾一下关系型SQL数据库的背景。

在本章伊始,我们就已经介绍了关系型数据库和其核心元素ER图。在实际的系统实现中,关系模型都是通过二维表格来表示的,一个关系型数据库就是由若干个二维表格和它们之间的联系所组成的。借用图3-1的ER图,可以设计出如图3-3所示的二维表。

图3-3 员工和俱乐部的二维表设计

其中,俱乐部ID在现实场景中可能并不存在,但是由于处理的需要,一般数据库系统会自动添加这个属性。除此以外,每张表格的列都对应ER图中实体的一个属性,例如员工信息表中就有员工ID、姓名、年龄和性别共4个属性,而俱乐部表格中的列也包括了名称、项目、负责人、经费和活动日的属性。在数据库中这些列被称为“字段”。表的每一行则代表一个实体,例如员工信息表的第一行代表员工张三,而俱乐部信息表的第三行代表“如鱼得水”游泳俱乐部。在数据库中这些行被称为“记录”。而我们可以通过增加第三张表格(参加关系表)来体现员工和俱乐部之间的关系,从表中可以看出,001号员工张三参加了乒乓球和游泳俱乐部。如果需要使用SQL语言来针对这些表格进行查询,也是非常方便的,下面列出几个最基本的使用案例:

SELECT 姓名 FROM 员工信息表              含义:返回“员工信息表”中所有员工的姓名
INSERT INTO 俱乐部信息表 VALUES (“夸父追日”,“慢跑”,“吴九”,“ 3,000 ”,“每周六”)含义:建立新的“慢跑”俱乐部

不难发现,这种关系数据库有着非常明显的优势:

  • 理解容易:相信大家从图3-3中已经看出来了,二维表非常贴近人类的思维逻辑。
  • 性能良好:提供了强大的索引功能,能方便地查询各种数据集合。
  • 使用方便:SQL结构化查询语言,入门简单,同时也能实现比较复杂的逻辑,使用者无需考虑过多实现细节。
  • 维护便捷:提供了事务保证数据的一致性,大幅降低了数据的冗余和不一致概率。

正是因为有着诸多明显的优势,所以即使是在NoSQL概念炒得异常火热的今天,关系型SQL数据库仍然有着举足轻重和无法替代的地位。多数的银行系统、企业内部企业资源规划ERP(Enterprise Resource Planning)系统还在使用稳健的关系型数据库,其事务性保证了每笔交易的准确无误。当然,关系型数据库在互联网和大数据时代面临前所未有的巨大挑战,其某些方面的不足也凸现了出来。

  • 处理性能不足以应付海量数据:关系型数据库的数据准确性主要得益于事务性的保证,可是,事务一致性需要消耗更多的处理资源。互联网使得数据量疯狂地膨胀,某些热门网站每日的用户访问量和并发量都是惊人的。如果还要保持事务性,那么处理速度就明显跟不上数据的增长速度了。而且在互联网的应用中,数据的准确性要求没有银行、金融或电信等行业那么高。例如,当我们通过Flume收集用户访问网站的行为数据时,在某个时间点的点击记录发生了延迟甚至是丢失,对整体的分析并无大碍,也不会产生经济上的损失。这个时候关系型数据库的强事务性就失去了优势。
  • 数据库的表结构不够灵活:关系型数据库建立在ER和关系模型上,因此在表格的设计之初需要定义严格的模式(Schema)。一旦Schema确定,那么每行的记录就都需要严格遵守这个规定。万一需要修改,整个过程也是比较复杂的。修改完毕后,既有的所有记录都要根据这个新的Schema做出相应的调整。然而,互联网领域强调的就是“变化”。每时每刻创新的想法都在诞生,项目的进度走的都是敏捷迭代方式,那么数据的定义也不可能一成不变。频繁的改动会使得关系型数据库疲于应付。

由于不能很好地适应互联网时代的数据处理需求,人们开始设计NoSQL的方案以作为补充。针对上述两个主要的不足,HBase首先集成了Hadoop的HDFS文件系统HBase也可以不用建构在HDFS之上,不过这样就无法发挥HDFS的并行处理能力了。,用于提供可水平扩展的能力,为大规模数据量做好了准备。另外,HBase还提供了非常灵活的列式存储,这是有别于关系型数据库行式存储的方式。关于列式和行式存储的差异,可通过公司俱乐部的案例来做进一步说明。小王是某大公司的人力资源专员,她有一项重要的任务是负责员工的福利事宜,也包括员工的业余爱好俱乐部。一日,主管让小王将全公司员工所参加的俱乐部情况统计一遍,3天之内完成。非常遗憾的是,这家公司还没有将这些信息输入ERP系统,全公司10000多名员工的俱乐部资料,全部需要小王手工整理出来。即使加班加点,对她而言3天完成也是不可能的。咋办呢?小王只好向老板申请增加几名实习生,对于实习生,她是这么安排的:按照公司的部门来划分,4个实习生加上自己共同处理公司五大部门的事务,其内容大致如图3-4所示。

图3-4 小王给实习生们的分工方案A

小王将这种方式称为方案A。通过3天的艰苦奋斗,小王终于将材料按时提交给了主管。主管看过后非常满意,小王对自己工作安排的合理性也很是得意。可是,没过几天,主管又提出了新的需求:她想知道每位员工参加俱乐部活动的出席率如何。于是小王和每位实习生只能再次统计一遍。又过了几天,主管提出她还想知道每位员工参加俱乐部比赛后,获得名次的情况。新的需求不断增加,每次小王和实习生都非常辛苦。而且一旦员工参加了新的俱乐部,或者是出席率发生了变化,数据更新工作又将是无法避免的。小王开始思考,这样的分工真的是合理的吗?有没有可能换一种方式?她发现主要的变化大多是新增的统计项目,而且新增的项目也不一定适合所有员工,例如俱乐部比赛名次,有些俱乐部根本不组织比赛,也无所谓比赛名次了。那么,如果让每个实习生专职负责若干统计项目,工作效率是否会更高呢?例如根据图3-5的形式来分工。

这次,小王负责统计员工参加了哪些俱乐部,而其他4位实习生分别统计出席率、比赛名次、赞助经费和俱乐部内的职务。如果再有新的项目需要统计,小王也分配给某人专职来维护。小王将其称为方案B。相对于方案A,方案B的好处在于,如果只是新增或更新某一项数据,只需要一位人员来操作,而其他人完全可以不用理会。如果将来这些数据不再是人工操作,而小王和小伙伴们的工作也交由计算机来处理的话,那么A方案对应就是行式存储,而B方案对应的就是列式存储。两者孰优孰劣并无定论,而是要看具体的应用场合。刚刚提到小王的主管提出的新需求很多,经常需要增加统计项目,这就对应于二维表中列的维护,因此适合列式存储。再假设一下,主管没有那么多需求,但是经常有新员工入职,每位员工对应于二维表中的一行,那么这就会涉及行的维护,此时行存储就更适合。如果这个时候还采用列存储,就意味着每次新增员工,所有的小伙伴们都要修改手头的表格。

图3-5 小王新的分工方式方案B

在了解列式存储相对于行式存储的优势之后,我们就能明白为什么列式存储更适合互联网灵活多变的环境了,它并不要求开发者在起初就给出完美的数据Schema定义,而是允许在随后的进展过程中不断被优化,定义修改所导致的历史数据的更新成本也会更小。接下来看看HBase使用了怎样的数据模型来实现列式存储。下面首先列出几个关键的概念:

  • 表格(Table):HBase同样用二维表格来组织数据。
  • 行(Row):在表格里,每一行代表一条记录,这与关系型数据库一致。每行通过行键(Row Key)进行唯一标识。
  • 列族(Column Family):了解这点很关键,行里的字段按照列族进行分组,可以看作一堆属性或字段的集合。列族的定义决定了HBase数据的物理存放。因此,列族需要预先定义,而且不要轻易修改。每行都拥有相同的列族,不过HBase并不要求每个列族都存储数据。这也是为了满足灵活的数据定义需求。
  • 列限定符(Column Qualifier):列族里包括多个属性,限定符可以帮助定位列族里的数据。与列族不同,列限定符没有必要预先被定义,因此每行可以拥有不同数量和名称的限定符。图3-5中的表格存在很多“/”(空缺值),对于这样的表格,灵活的列限定符可以减少不必要的存储,提升处理稀疏矩阵的能力。
  • 单元(Cell):二维表里的单元格,通过行键、列族和列限定符来唯一确定。存储在其中的值称为单元值(Cell Value)。
  • 版本(Version):注意,这是HBase与很多数据库的不同之处。即使单元被确定了,里面的单元值仍然可以根据时间的不同拥有多个版本。版本用时间戳(Timestamp)来标识。读取的时候如果没有指定时间戳,那就默认获取最近的版本。

如果将行键和列限定符对应于关系型数据库的行和列,那么HBase主要就多了列族和版本。记住这6个主要概念,理解HBase读取数据的机制就并不困难了。从图3-6中可以看出HBase的坐标体系。其中最有趣的地方在于,HBase中可以不用提供全部坐标。如果只提供行键,那么就会返回某行的整行。如果提供行键、列族和列限定符,那么就返回某行某列的最新单元值。再进一步,如果同时提供了行键、列族、列限定符和时间戳,那么就返回某行某列单元值的某个版本。

图3-6 HBase的主要概念:行键、列族、列限定符、单元和版本

其中,如果给定行键00096,将返回如下信息:

{俱乐部ID:“001,005”,出席率:“25%”,比赛名次:“2”,赞助经费:“300”}

如果给定行键00096、列族“信息”和列限定符“出席率”,那么返回出席率:“25%”。

如果给定行键00098、列族“信息”、列限定符“俱乐部ID”和时间戳“2015年2月”,那么返回值是“008,010”。

了解完HBase的基本概念和数据模型,再来看看它的体系架构,如图3-7所示。为了保证良好的扩充性和并行处理能力,HBase是架构在Hadoop的HDFS上的。此外,它通过HRegion和HStore来实现列族的存储。具体来说,其中的主要元素如下。

图3-7 HBase的整体架构,以HDFS为基础,通过HRegion构成列式存储

  • HMaster:类似HDFS的命名节点,HBase使用HMaster主节点协调和管理多个HRegion服务器(HRegionServer)节点。HMaster本身并不存放具体的数据。HRegion服务器是通过ZooKeeper来协调的。ZooKeeperZooKeeper的名字来源是因为它管理的很多分布式系统喜欢使用动物来代言,“动物园的管理者”再形象不过了。是一个为分布式应用提供一致性服务的软件,可以提供配置维护、名字服务、分布式同步、组服务等。在后面介绍的其他Apache开源项目中,也时常会见到它的身影。
  • HRegion:HBase的表在逻辑上可以划分为多个Region。随着数据的不断增加,一张表会被拆分为多块。每一块就是一个HRegion,保存一段连续的数据。数据都是通过底层的HStore、HFile(StoreFile)和MemStore来实现的。每个HStore对应于一个列族,HFile和MemStore分别是文件和内存的存储。此外,HRegion中还包含HLog来记录日志以便于事故后的恢复。
  • HRegion服务器(HRegionServer):多个HRegion由HRegion服务器来管理。

HBase的列存储设计为灵活的表结构提供了基础,在实际应用中修改列族的定义是很常见的。对于新的业务需求,不断增加列族或限定符也是不错的选择,从二维表的视角上看,这种模式会导致表列越来越多,越来越宽,因此我们也可以形象地将其称为“宽表”。

“看来HBase的宽表模式,既可以节省关系型数据库中的连接操作或存储空间,同时还能将不同Schema模式的数据进行混合。是不是可以将其运用在异构数据源的统一和集成上?我们公司即将进行的线下业务,每个商铺都有自己的ERP系统,数据格式都不尽相同,我正头疼这个事情呢。”

“你的理解完全正确,可以通过HBase进行商铺历史数据的整合,而且还不用担心水平的扩展性”。