HBase是谷歌BigTble的开源实现。谷歌的三篇论文拉开了大数据江湖的序幕,铸就了现在以Hadoop为主的大数据技术生态圈。而HBase是开源的大数据数据库,和传统的行式数据库不同的是,HBase是列式数据库。列式数据的特点是开源横向扩展,将一张表的数据存储在hadoop集群的不同datanode中,一张表的存储量可以达到T级别。这是行式关系型数据库无法实现的。本文主要讲解HBase的基本概念,只有概念清楚了才能更好的在我们的系统中使用HBase。
核心组件介绍
Table:可理解为传统数据库中的一个表,但因为SchemaLess的设计,它较之传统数据库的表而言,在设计上更加灵活。
Region:将表横向切割为一个个子表,子表在HBase中被称之为Region。
RegionServer:数据服务进程,Region必须部署在某一个RegionServer上才可以提供读写服务。
HFile:HBase数据库在底层分布式文件系统中的文件组织形式。
Column Family:一些列的集合。不同的Column Family数据被存储在不同的路径中。
MemStore:用来在内存中缓存一定大小的数据,达到设定的阈值后批量写入到底层文件系统中。数据是有序的。
下图清晰的展示了Table,Region,RegionServer,HFile,MemStore,Column Family在HBase的逻辑关系。
下图显示了HBase集群中的关键进程
Zookeeper:HBase集群的调度器,可以用于将HBase RegionServer信息注册到zookeeper中,查询HBase RegionServer状态信息,HMaster启动时会将HBase系统表-ROOT-加载到zookeeper集群中,通过zookeeper集群可以获取当前系统表.META.的存储所对应的RegionServer信息
。
Master,通过jps命令显示的进程名称是HMaster,在负责表管理操作,Region到各个RegionServer的分配以及RegionServer Failover的处理等。
RegionServer进程提供数据读写服务。
NameNode,Hadoop进程,处理来自Master的请求,H管理DFS文件系统的命名空间NameSpace。
DataNode,Hadoop数据节点进程,HBase的所有数据都存在Hadoop的DataNode中。
KeyValue数据存储结构
HBase所存储的数据是以KeyValue形式存放的,KeyValue有特定的数据结构,如下图所示,一个KeyValue可以理解成HBase表中的一个列,当一行存在多个列时,将包含多个KeyValue,同一行的KeyValue有可能存储在不同的文件中,但在读取时,会按需合并在一起返回给客户端。
用户写数据时,需要定义用户数据的RowKey,指定每一列所存放的Column Family,并且为其定义相应的Qualifier(列名),Value部分存放用户数据。Hbase中每一行可拥有不同的KeyValues,这就是HBase Schema-less的特点。
HBase中支持数据的多版本,通过带有不同时间戳的多个KeyValue版本来实现的,如下图所示。
HBase所保存的版本数据是可配置的,默认存放3个版本。在普通的读取流程中,旧版本的数据时不可见的,但通过制定版本数或者版本号的读取,可以获取旧版本数据。下图是普通读取刘恒与多版本读取流程的对比。
灵活的列定义
用户数据存入到HBase表中时,需要进行Qualifier(KeyValue/列)设计。一个最简单的设计是保持HBase的列与用户数据的列一致,如下图1的设计。这种设计,基本上与关系型数据库的设计是一致的,但这种设计会带来较大的数据冗余(KeyValue结构开销)。但HBase基于KeyValue的接口,决定了这种设计可以是非常灵活的,例如,我们也可以考虑为HBase的每一行只设置两个列,其中,Name为一个列,其他内容合并到一个列中,如下图2所示。
尽管我们在使用HBase表存放数据的时候,需要预先做好列的设计。但这个设计仅仅由应用层感知,HBase并没有存放任何的Schema信息来描述这个设计。也就是说,应用层需要知道为每一个表/每一行设计了什么样的列(KeyValue),然后在地区的时候做相应的解析。既然HBase中并没有Schema信息,name,每一行中的列,也可以是任意添加的。如下图所示,绿色背景的KeyValue为后续增加的。
Column Family
假设为表设置了两个列族,而且,定义了每一个列簇中要存放的列,如下图所示:
{Name} -> Column Family - A, {City,Phone,Gender} -> Column Familly-B。不同列簇的数据会被存储在不同的路径中。即,设置多个列簇时一行数据可能存在于两个路径中。整行读取的时候,需要将两个路径中的数据合并在一起蔡可以获取完整的一行记录。但如果仅仅读取Name一列的话,只需要读取Column Family-A即可。