世界上最伟大的互联网公司Google在2006年发布了其著名的论文BigTable,根据BigTable的中心设计理念,HBase项目于2007年初在Hadoop中成立,并在同一年的10月发布了第一个“可用”版本。
随着2008年初,Hadoop成为Apache基金会的顶级项目,HBase也顺理成章成为了Hadoop中一个重要子项目,并得到越来越多的关注,终于在2010年中期,HBase顺利成为Apache的顶级项目。
纵观HBase整个发展过程,一直跟Hadoop有着千丝万缕的联系,虽然发展至今,HBase已经是一个相当成熟的项目,standalone(单机模式)的部署方式可以直接把数据写入操作系统的文件系统中而不依赖于Hadoop,但离开了Hadoop的HBase,基本上也失去了存在的意义,所以后面如果没有特殊的说明,HBase底层都默认基于Hadoop的HDFS进行存储。
上面已经提到,HBase的存储依赖于Hadoop的HDFS,完整的HBase环境,需要包括Hadoop和HBase,外加一个高可用协调器Zookeeper。
HBase并不依赖于MapReduce(或者yarn),对于HBase来说,Hadoop仅仅用作数据存储,所以只需HDFS部分即可,包括:NameNode,DataNode, Hadoop 2.0以上版本支持NameNode的高可用,所以还包括JournalNode,ZKFC大致结构如下:
HDFS高可用部署
这里再简单介绍了几个模块的功能:
1. NameNode
记录HDFS中所有block的信息,即保存HDFS的所有元数据
2. DataNode
具体数据保存的位置
3. JournalNode
保存NameNode的状态信息
4. ZKFC
在active NameNode出现故障时,进行NameNode的切换
HBase本身由Master和RegionServer两部分组成,类似于HDFS中的NameNode与DataNode的角色,区别在于HBase的Master一般情况下只用于管理RegionServer,由于RegionServer一般情况下不怎么会改变,所以在HBase中,Master的压力并不大;这不像NameNode之于HDFS,HDFS中的NameNode需要记录DataNode中每个block的位置,所以对DataNode中数据的写入,都需要更新到NameNode。HBase大致结构如下图:
HBase结构图
同样,先简要介绍下个模块功能:
1. Zookeeper
HBase与HDFS的Zookeeper可以共享采用同一套Zookeeper系统,在HBase中,Zookeeper用于保存HBase的-ROOT-表的入口地址,通过-ROOT-表可以查找到具体Region所在RegionServer信息。
HBase中的Zookeeper还会保存一些临时信息。
2. Master
Master用于管理RegionServer状态,为RegionServer分配具体Region等等,在RegionServer出现意外宕机时,Master会负责该RegionServer上的Region进行迁移,这个过程是极其短暂的,也正因为此,HBase保障了服务的高可用性。
3. RegionServer
RegionServer是外部访问的最终目的地,对HBase数据的读取或者写入操作,最终都会落到RegionServer上。
通过上面对HDFS和HBase两部分组成结构的了解,无论是HDFS还是HBase,其各个组成部分在设计的时候都考虑到了单点故障问题,从而通过高可用的方式来尽量避免单点故障的出现,所以,从结构上来讲,HBase(包括HDFS)是一个没有单点故障的系统,服务可用性非常高。
也正是由于HBase、HDFS这种高可用设计,使HBase、HDFS的横向扩展变得相对简单,如果在运行一段时间后,发现存储容量不足,只需要增加HDFS的DataNode即可,如果是HBase的性能不足,可以通过增加HBase的RegionServer数量来提高性能,值得注意的是,无论是增加DataNode还是RegionServer,对外部应用来说都是透明的,不会被外部应用感知。
上一节从HBase的组成结构上分析了HBase本身不存在单点故障,从而保证了系统的高可用性。本节开始从HBase的设计原理方面入手来分析下HBase除了高可用外,在高性能方面也有不错的表现。
HBase通过Master管理多个RegionServer,每个RegionServer又负责多个Region,这种1(RegionServer)对多(Region)的形式,很好的把外部请求分散到多个服务上面,HBase通过rowkey来划分RegionServer,rowkey是一个简单字符串,根据字节类型进行比较后划分不同的范围,每个RegionServer对应一个rowkey范围,这个跟MongoDB中基于id范围的sharding方式比较类似,如下图:
RegionServer请求访问
一般地,一个物理节点部署一个RegionServer服务,每个RegionServer下面有许多Region,客户端的请求根据rowkey的不同,会被发送到不同的RegionServer上,RegionServer在根据rowkey的值,交由不同的Region进行处理。
客户端数据写入请求被RegionServer转发到具体的Region,每个Region内部有一块内存空间用于缓存写入的数据,称之为memstore。外部写入的数据在memstore中会根据rowkey进行排序,每个memstore有其固定的大小,当写入的数据达到memstore大小,或者整个RegionServer下所有的memstore占用的内存达到一定的阈值后,memstore的数据会被刷写到磁盘,以便可以腾出更多的内存空间用于新数据的缓存。由于memstore中的数据都是经过排序的,数据刷盘的速度也会非常快。
对于客户端来说,数据被写入memstore后,请求就已经被返回,所以,反应给客户端整个HBase系统的性能是非常高效的。
HBase数据写入过程
在HBase中,所有涉及到数据的更改操作,都是以追加写入的方式进行,比如,对于某条数据的更新操作,HBase会把该操作转化为写入操作,追加写入的好处是不会带来大量不必要的随机I/O,使整个系统保持高写入性能。
HBase并不像关系型数据库那样,支持很多复杂的查询。相反,在中HBase,只有一种索引,即基于rowkey的主键索引,所以,针对HBase的所有读操作,都需要rowkey进行参与,目前HBase只支持基于rowkey的单条记录查询或者范围查询。
客户端发起读请求,HBase根据查询的rowkey信息,把请求转发到指定RegionServer上,首先RegionServer会根据查询的rowkey不同,去指定的region的memstore中查询数据,如果改数据在memstore中正好存在,则返回改数据,如果memstore中没有该数据,每个RegionServer中会有一块单独的内存称之为block cache,该内存就是用来针对于读操作的,如果block cache中有需要的数据,则返回,如果没有,需要从底层HDFS上读取相关数据到block cache后再返回。
HBase数据读取流程
HBase客户端的写入请求在数据写入到memstore的时候就已经返回,我们知道,内存中的数据是不安全的,在服务出现故障的时候,内存中的数据很容易被丢失,HBase怎样保障数据的可靠性?
其实,和大多数数据库一样,HBase通过WAL(write ahead log)的方式保障数据的可靠性,HBase有自身的HLog用来保障数据在写入内存之前,已经被预先写入HLog,HLog每个RegionServer都有一份,在请求被转发到具体的Region之前,RegionServer就已经把数据写入了HLog,如果后续发生故障导致内存中的数据丢失,HBase还可以从RegionServer的HLog中恢复丢失的数据。
HBase从结构上保障了系统的高可用、高可靠性,内部实现的分布式、全内存异步写入保证了系统的高性能,那么这样一个具备了高性能和高可靠性的系统,哪些地方在使用过程中需要注意?
1. 系统部署复杂
从HBase的组成部分我们可以看到,为了保障HBase的高可用性,无论是HBase还是HDFS都引入了许多其他的模块,比如Zookeeper,JournalNode等等,这样无疑增加了系统的复杂度,对普通开发者来说,要构建这样一套系统,前期工作巨大。
2. 查询条件单一
HBase只支持基于rowkey的索引,所以对于HBase上数据查询都只能根据rowkey进行查询,不支持像关系型数据库那样复杂的SQL查询,HBase也不支持二级索引,单主键的限制,使得HBase的查询变得简单,也就失去了查询的多样性。
3. 尽量避免大范围查询
HBase采用的是根据rowkey字节范围进行分区,大范围的数据查询会使查询落到多个不同的RegionServer上,这样查询的效率相对会非常慢。
4. 表设计要求较高
HBase是一个基于rowkey的分布式系统,所以在表的设计上,rowkey的选择比较重要,一个好的rowkey需要避免产生热点数据,比如,部分应用喜欢用时间戳相关的字段作为rowkey的值,这样设计的坏处就在于某段时间内的访问都将被集中到某台RegionServer上,失去了分布式的意义。
HBase支持多种不同方式的replication模式,包括:1对1,1对多,多对1等多种不同的模式:
HBase 3种replication结构
1..N模式适合把一个HBase集群中不同的column Family拆分到不同的集群中
N..1模式适合备份数据,把多个HBase集群的数据备份到同一个集群中
1..1模式适合当做高可用部署,虽然HBase本身的可用性非常高,但在某些情况下,比如异地数据备份,异地双活等特殊要求下,异地数据备份将是一个不错的选择。
HBase本身不支持二级索引,但是可以通过一些辅助手段,可以在HBase上支持二级索引方案,第一是通过HBase的Coprocessor,第二个是通过引入外部支持,实现HBase二级索引方案。
HBase的coprocessor可以实现类似于关系型数据的触发器功能,可以在数据写入的put操作前定义pre-put操作,这样,如果要实现二级索引,只需要将需要索引的字段与rowkey做一个反向索引即可,即在插入一条数据的同时,同时插入一条反向索引,这样通过反向索引二次查询来实现二级索引。
另外一种实现二级索引的方法原理与coprocessor类似,只是通过一个全文索引引擎solr来代替coprocessor的功能,把需要建立二级索引的字段与rowkey字段在solr中建立索引,这样通过字段可以查询到rowkey的值,然后通过rowkey查询指定值
HBase从诞生至今将近10年,在apache基金会的孵化下,已经变成一个非常成熟的项目,也有许多不同的公司支持着许多不同的分支版本,如cloudra等等。下面我们来看下,HBase的一些具体应用。
Facebook用HBase存储在线消息,每天数据量近百亿,每月数据量250 ~ 300T, HBase读写比基本在1:1,吞吐量150w qps
10+在线HBase集群,好几百台服务器,米聊历史数据,消息push系统等多个重要应用系统都建立在HBase基础之上
哨兵监控系统,云信历史数据,日志归档数据等一系列重要应用底层都由HBase提供服务。
1. 写密集型应用,每天写入量巨大,而相对读数量较小的应用,比如IM的历史消息,游戏的日志等等
2. 不需要复杂查询条件来查询数据的应用,HBase只支持基于rowkey的查询,对于HBase来说,单条记录或者小范围的查询是可以接受的,大范围的查询由于分布式的原因,可能在性能上有点影响,而对于像SQL的join等查询,HBase无法支持。
3. 对性能和可靠性要求非常高的应用,由于HBase本身没有单点故障,可用性非常高
数据量较大,而且增长量无法预估的应用,HBase支持在线扩展,即使在一段时间内数据量呈井喷式增长,也可以通过HBase横向扩展来满足功能。
本文来自网易实践者社区,经作者蒋鸿翔授权发布。