Hbase架构
最近项目中用到了Hbase,其作为一款NoSQL产品,采用列式存储,适合存储非结构化/半结构数据,为了支持大规模数据集及扩展性,弱化了ACID,牺牲了事务的部分功能。Hbase是Hadoop生态体系中的一部分,底层存储是分布式文件系统HDFS。HBase and MapR-DB: Designed for Distribution, Scale, and Speed和An In-Depth Look at the HBase Architecture这两篇文章写的很好,把hbase的适用场景、架构设计及关键实现清晰易懂的描述出来,推荐读下这两篇博客,当然,时间充裕的话最好还是去看官方文档,写的非常详细。本文的目的是提炼一下其中的关键点。
Hbase和RDBMS
传统的RDBMS(MySQL),其特点和其名字一样,是『关系型』的,意味着存储的数据模型需要严格符合schema,表设计需要符合范式,支持完整的事务功能(ACID)来确保数据一致性,支持复杂的联合查询。但是其设计初衷是在单节点,尽管可以对数据进行sharding水平拆分来构建集群模式,这种模式下的扩展性和读写性能有时候并不能满足海量数据的读写,而且一些join查询往往会成为瓶颈。
Hbase就是列式存储,将相关联的数据都存储在一块,方便读写,而不是像RDBMS一样将数据分散存储,再通过join查询进行聚合,另一方面Hbase牺牲一定的事务性,只支持单行,这样可以增大吞吐量,提高读写性能。
Hbase不是用来替代传统的RDBMS,两种数据库都有其各自的适用场景。如果需要完整的事务功能、复杂查询(SQL)、二级索引,则选择RDBMS。如果是存储大规模非结构/半结构数据,则推荐使用Hbase。
Hbase数据模型
Hbase是列式存储,核心的是其唯一主键RowKey,数据的分布和高性能读写都靠RowKey来完成的,内部都是按RowKey进行有序存储,Hbase将连续的RowKey进行拆分,分散到不同的服务器(Region Server)中,所以RowKey的合理设计关系到hbase的读写性能。而真正的数据则是存储在一个个Column之中,不同的Column可以组成ColumnFamily。 Hbase是稀疏存储,所以如果有的列没有值则不存储。Hbase中最小的存储单元是Cell:
Table:RowKey:ColumnFamily:Column:Timestamp -> Value
Hbase中的Table可以通过RowKey拆分到不同的server上,同一个RowKey又可以根据ColumnFamily存储在不同的文件中,最终通过5要素定位到value。Cell中timestamp是版本号,数据的增加和修改都是一条新纪录,只不过版本号不同,数据的删除则通过标记实现,查询的时候进行过滤。
Hbase架构
Hbase架构中的角色主要包括Client、Zookeeper、Hmaster、Region Server。
-
Client,主要从zookeeper那里获取meta表位置,然后获取meta信息并缓存,meta表中是Region与Region server的映射信息,最后从Region所在server获取具体数据
-
Zookeeper,提供分布式一致服务,监测Regeion Server和Hmaster的状态,并提供服务器宕机通知,还存储了meta表的位置。
-
Hmaster,主要负责Region分配和table的DDL操作。
-
Region Server,核心组件,负责数据读写,由于table是根据RowKey拆分到每一台Region Server的,所以一台Region Server负责table的[StartRowKey, EndRowKey)的一段Region,一个Region可以配置1G的存储,每一台Region Server可以负责1000个Region。由于所有数据都是存储在HDFS上的,为了减少网络IO,一般将Region Server与HDFS中的Data Node部署在同一台服务器上。Region Server的核心模块如下:
- WAL(Write Head Log),预写式日志,所有的操作都要写入这个日志,当服务器异常时,可以通过这个日志文件进行恢复
- BlockCache,读缓存,采用LRU机制进行替换
- MemStore,写缓存,缓存还未持久化的数据,在写入磁盘前会进行排序,每一个region的每一个ColumnFamily对应一个MemStore
- HFile,存储有序的KeyValue记录至HDFS
Hbase读写
写过程
Hbase写过程包含两步,首先将数据写入WAL,其次将数据写入MemStore,成功后可以返回ack至Client。
Region Flush
当MemStore写满后,需要进行刷盘操作,将MemStore flush到HFile。由于每一个ColumnFamily对应一个MemStore,而当一个MemStore满时,会flush当前Region中所有的MemStore,所以需要限制ColumnFamily的个数。在MemStore Flush时,会在HFile的meta字段存储最大的WAL序列值,这样系统就知道目前持久化到哪里了,系统恢复时可以从这个sequence开始。HFile是顺序写的,避免了频繁的磁盘查找。
HFile索引
HFile建立了类似于B+数的多级索引,可以让避免查找数据时扫描整个HFile,而且每个HFile也存了Bloom Filter和Time Range信息,可以快速筛选不在HFile中的RowKey。
读过程
由于归属于同一个RowKey的Cell可能会在多个地方(已经持久化的HFile,最近更新的MemStore,最近读过的BlockCache),所以在进行读操作时需要进行合并操作,过程如下:
- 从BlockCache中查看是否有读缓存
- 读缓存没有读到,则从写缓存MemStore查找
- 如果缓存都没有找到,则通过BlockCache中的索引和BloomFilter加载对应的HFile到内存中,找到对应的RowKey
Hbase压缩
由于每次MemStore容量满时都会flush生成新的HFile,所以HFile会越来越多,对读性能造成影响。Hbase的策略是进行压缩,将小文件合并成大文件,分为Minor Compaction和Major Compaction。Minor Compaction是轻量级的压缩工作,把HFile通过归并排序合并成稍微大点的HFile。而Major Compaction则是重量级的,将Region中所有HFile都合并成一个,并删除过期和废弃的Cell。但是Major Compaction会造成大量的网络IO,所以可以放在系统空闲的时候进行。
Region拆分和负载均衡
当一个Region增长到一定规模时,出于负载均衡的考虑,会将其拆分成两个ChildRegion,ChildRegion会向HMaster注册,如果一个RegionServer负载过高,HMaster会将Region分配到其他的RegionServer中。这样会导致RegionServer和本地数据(HFile)不在一起,会在下一次Major Compaction时将HFile移到当前的RegionServer。
- 上一篇 围城摘录
- 下一篇 ketama一致性哈希