HBase技术简介
一、HBase简介
HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。
HBase是Google BigTable的开源实现,类似Google BigTable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统;Google运行MapReduce来处理Bigtable中的海量数据,HBase同样利用Hadoop MapReduce来处理HBase中的海量数据;Google Bigtable利用 Chubby作为协同服务,HBase利用Zookeeper作为对应。
上图描述了Hadoop EcoSystem中的各层系统,其中HBase位于结构化存储层,Hadoop HDFS为HBase提供了高可靠性的底层存储支持,Hadoop MapReduce为HBase提供了高性能的计算能力,Zookeeper为HBase提供了稳定服务和failover机制。
此外,Pig和Hive还为HBase提供了高层语言支持,使得在HBase上进行数据统计处理变的非常简单。 Sqoop则为HBase提供了方便的RDBMS数据导入功能,使得传统数据库数据向HBase中迁移变的非常方便。
二、HBase访问接口
Native Java API,最常规和高效的访问方式,适合Hadoop MapReduce Job并行批处理HBase表数据
HBase Shell,HBase的命令行工具,最简单的接口,适合HBase管理使用
Thrift Gateway,利用Thrift序列化技术,支持C++,PHP,Python等多种语言,适合其他异构系统在线访问HBase表数据
REST Gateway,支持REST 风格的Http API访问HBase, 解除了语言限制
Pig,可以使用Pig Latin流式编程语言来操作HBase中的数据,和Hive类似,本质最终也是编译成MapReduce Job来处理HBase表数据,适合做数据统计
Hive,当前Hive的Release版本尚没有加入对HBase的支持,但在下一个版本Hive 0.7.0中将会支持HBase,可以使用类似SQL语言来访问HBase。
三、HBase数据模型
3.1 Table & Column Family
Row Key | Timestamp | Column Family | |
URI | Parser | ||
r1 | t3 | url=http://www.taobao.com | title=天天特价 |
t2 | host=taobao.com | ||
t1 | |||
r2 | t5 | url=http://www.alibaba.com | content=每天… |
t4 | host=alibaba.com |
Row Key:
行键,Table的主键,用来检索记录。访问HBase table中的行,只有三种方式:
- 通过单个row key访问
- 通过row key的range
- 全表扫描
row key可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes左右),在HBase内部,row key保存为字节数组。数据按照row key的字典序(byte order)排序存储,因此设计key时要充分排序存储这个特性,将经常一起读取的行存储放到一起。
HBase支持单行事务,行的一次读写是原子操作 (不论一次读写多少列),这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为。
Column Family:
列簇,Table在水平方向有一个或者多个Column Family组成,一个Column Family中可以由任意多个Column组成,即Column Family支持动态扩展,无需预先定义Column的数量以及类型,所有Column均以二进制格式存储,用户需要自行进行类型转换。
列名都以列族作为前缀。例如’courses:history’,’courses:math’,都属于courses 这个列族。
访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。
Timestamp:
时间戳,每次数据操作对应的时间戳,可以看作是数据的version number。时间戳的类型是 64位整型。时间戳可以由HBase在数据写入时自动赋值,此时时间戳是精确到毫秒的当前系统时间;时间戳也可以由客户显式赋值,如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。
为了避免数据存在过多版本造成的的管理 (包括存储和索引)负担,HBase提供了两种数据版本回收方式:一是保存数据的最后n个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。
Cell:
由 {row key
, column family
, column qualifier
, version
} 唯一确定的单元。cell中的数据是没有类型的,全部是字节码形式存贮。
3.2 Table & Region
当Table随着记录数不断增加而变大后,会逐渐分裂成多份splits,成为regions,一个region由[startkey,endkey)表示,不同的region会被Master分配给相应的RegionServer进行管理:
3.3 -ROOT- && .META. Table
HBase中有两张特殊的Table,-ROOT-和.META.
.META.:记录了用户表的Region信息,.META.可以有多个regoin
-ROOT-:记录了.META.表的Region信息,-ROOT-只有一个region
Zookeeper中记录了-ROOT-表的location
Client访问用户数据之前需要首先访问zookeeper,然后访问-ROOT-表,接着访问.META.表,最后才能找到User Table的位置去访问,中间需要多次网络操作,不过Client端会做cache缓存。
四、HBase系统架构
Client
HBase Client使用HBase的RPC机制与HMaster和HRegionServer进行通信,对于管理类操作,Client与HMaster进行RPC;对于数据读写类操作,Client与HRegionServer进行RPC
Zookeeper
Zookeeper Quorum中除了存储了-ROOT-表的地址和HMaster的地址,HRegionServer也会把自己以Ephemeral方式注册到Zookeeper中,使得HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的单点问题,见下文描述。
HMaster
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master运行,HMaster在功能上主要负责Table和Region的管理工作:
管理用户对Table的增、删、改、查操作
管理HRegionServer的负载均衡,调整Region分布
在Region Split后,负责新Region的分配
在HRegionServer停机后,负责失效HRegionServer 上的Regions迁移
HDFS的垃圾文件回收
注意:Client访问HBase上数据的过程并不需要HMaster参与(寻址访问Zookeeper和HRegionServer,数据读写访问HRegioneServer),HMaster仅仅维护者table和region的元数据信息,负载很低。
HRegionServer
HRegionServer主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。
注意,每个HRegsionServer维护一个HLog文件
HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个Region,HRegion中由多个HStore组成。每个HStore对应了Table中的一个Column Family的存储,可以看出每个Column Family其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个Column Family中,这样最高效。
HStore
HStore 是HBase存储的核心,其中由两部分组成:
- MemStore
- StoreFiles。
MemStore是Sorted Memory Buffer,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile)。
Compaction:当StoreFile文件数量增长到一定阈值,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进行版本合并和数据删除,因此可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的写操作只要进入内存中就可以立即返回,保证了HBase I/O的高性能。
Split:当StoreFiles Compact后,会逐步形成越来越大的StoreFile,当单个StoreFile大小超过一定阈值后,会触发Split操作,同时把当前Region Split成2个Region,父Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。下图描述了Compaction和Split的过程:
HLog
在理解了上述HStore的基本原理后,还必须了解一下HLog的功能,因为上述的HStore在系统正常工作的前提下是没有问题的,但是在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegionServer意外退出,MemStore中的内存数据将会丢失,这就需要引入HLog了。
每个HRegionServer中都有一个HLog对象,HLog是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中(HLog文件格式见后续),HLog文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的 HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取 到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。
五、HBase存储格式
HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,主要包括上述提出的两种文件类型:
HFile, HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile
HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File
HFile
下图是HFile V2的存储格式:
HFile的组成分成四部分,分别是Scanned Block(数据block)、Non-Scanned block(元数据block)、Load-on-open(在hbase运行时,HFile需要加载到内存中的索引、bloom filter和文件信息)以及trailer(文件尾)。
在HFile中根据一个key搜索一个data的过程:
先内存中对HFile的root index进行二分查找。如果支持多级索引的话,则定位到的是leaf/intermediate index,如果是单级索引,则定位到的是data block
如果支持多级索引,则会从缓存/hdfs(分布式文件系统)中读取leaf/intermediate index chunk,在leaf/intermediate chunk根据key值进行二分查找(leaf/intermediate index chunk支持二分查找),找到对应的data block。
从缓存/hdfs中读取data block。
在data block中遍历查找key。
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的 Block Cache 机制。每个Data块的大小可以在创建一个Table的时候通过参数指定(默认64K),大号的Block有利于顺序Scan,小号Block利于随机查询。在DataBlock中存储的是一系列KeyValue,在KeyValue后面跟一个timestamp,如下图所示:
HFile里面的每个KeyValue对就是一个简单的byte数组。但是这个byte数组里面包含了很多项,并且有固定的结构。KV结构图如下:
可以看出KeyValue格式分为四个部分:KeyLength、ValueLength、Key、Value。
其中KeyLength和ValueLength都是整型,表示长度。Key有固定的格式,KeyType有四种类型,分别是Put、Delete、 DeleteColumn和DeleteFamily。Value部分没有这么复杂的结构,就是纯粹的二进制数据了。
HLogFile
上图中示意了HLog文件的结构,其实HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。
HLog Sequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue,可参见上文描述。
六、关键流程
6.1 region定位
HBase如何找到某个row key (或者某个row key的range)所在的region?使用三层类似B+树的结构来保存region位置:
- 第一层:Zookeeper保存了-ROOT-表的位置。
- 第二层:-ROOT- 表保存了.META.表所有region的位置,通过-ROOT-表,可以访问.META.表的数据。
- 第三层:.META.是一个特殊的表,保存了HBase中所有数据表的region位置信息。
需要注意的是:
- -ROOT-表永远不会被split,保证了只需要三次跳转,就能定位到任意region
- META.表每行保存一个region的位置信息,row key采用表名+表的最后一行编码而成
- 为了加快访问,.META.表的全部region都保存在内存中
- Client会将查询过的位置信息保存缓存起来,缓存不会主动失效
6.2 region分配
任何时刻,一个region只能分配给一个HRegionServer。HMaster记录了当前有哪些可用的HRegionServer。以及当前哪些region分配给了哪些HRegionServer,哪些region还没有分配。当存在未分配的region,并且有一个HRegionServer上有可用空间时,HMaster就给这个HRegionServer发送一个装载请求,把region分配给这个HRegionServer。HRegionServer得到请求后,就开始对此region提供服务。
6.3 写请求处理
数据在更新时首先写入Log(WAL log)和内存(MemStore)中,MemStore中的数据是排序的,当MemStore累计到一定阈值时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。与此同时,系统会在Zookeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了。
当系统出现意外时,可能导致内存(MemStore)中的数据丢失,此时使用Log(WAL log)来恢复checkpoint之后的数据。
前面提到过StoreFile是只读的,一旦创建后就不可以再修改。因此HBase的更新其实是不断追加的操作,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的写操作只要 进入内存中就可以立即返回,保证了HBase I/O的高性能。
6.4 读请求处理
如果想快速读取数据,通用的原则是数据保持有序 并 尽可能保存在内存里。HBase实现了这两个目标。
- HBase提供三维有序存储,首先以rowkey字典序,然后以column family + column qualifier字典序,最后单元格是以timestamp时间戳降序排序。
- HBase读操作使用了LRU缓存技术,这种缓存叫做BlockCache,用来保存从HFile里读入内存的频繁访问的数据。
从HBase读数据,会依次访问三个地方:首先检查MemStore,然后检查BlockCache,最后才会访问硬盘上的对应HFile。
6.5 Compact/Split
上文已经讲到,当StoreFile文件数量达到一定阈值,会触发compaction;当单个StoreFile大小超过一定阈值后,会触发split。
Compaction分为两种:major compaction(大合并)和 minor compaction(小合并)
minor compaction把多个小HFile合并成一个大HFile,主要是为了提高读效率(因为一个row可能散布在多个HFile文件中)。minor compaction不做任何删除数据、过期数据的清理工作。
major compaction是把给定region的一个列族的所有HFile合并成一个文件,major compaction会丢弃有删除标记的或过期的内容,释放占用的空间。
minor合并是轻量级的,可以频繁发生;major合并相当耗资源,不要经常使用,且通常需要手工触发。
Split操作过程完成的非常快,因为原始的数据文件并不会被改变,系统只是简单的创建两个Reference文件指向原始的数据文件,每个Reference文件管理原始文件一半的数据。Reference文件名字是一个ID,它使用被参考的region的名字的hash作为前缀,例如:1278437856009925445.3323223323。Reference文件只含有非常少量的信息,这些信息包括被分割的原始region的key以及这个文件管理前半段还是后半段。只有当系统做compaction的时候原始数据文件才会被分割成两个独立的文件并放到相应的region目录下面,同时原始数据文件和那些Reference文件也会被清除。
6.6 HRegionServer上线和下线
HMaster使用Zookeeper来跟踪HRegionServer状态。当某个HRegionServer启动时,会首先在Zookeeper上的server目录下建立代表自己的文件,并获得该文件的独占锁。由于HMaster订阅了server目录上的变更消息,当server目录下的文件出现新增或删除操作时,HMaster可以得到来自Zookeeper的实时通知。因此一旦HRegionServer上线,HMaster能马上得到消息。
当HRegionServer下线时,它和Zookeeper的会话断开,Zookeeper自动释放代表这台server的文件上的独占锁。而HMaster不断轮询server目录下文件的锁状态,如果发现某个HRegionServer丢失了它自己的独占锁(或者HMaster连续几次和HRegionServer通信都无法成功),HMaster就会去尝试获取代表这个HRegionServer的读写锁,一旦获取成功,就可以确定:
- 可能性一:HRegionServer和Zookeeper之间的网络断开了;
- 可能性二:HRegionServer挂了;
的其中一种情况发生了,无论哪种情况,HRegionServer都无法继续为它的region提供服务了,此时HMaster会删除server目录下代表这台HRegionServer的文件,并将这台HRegionServer的region分配给其它还活着的HRegionServer。
如果网络短暂出现问题导致HRegionServer丢失了它的锁,那么HRegionServer重新连接到Zookeeper之后,只要代表它的文件还在,它就会不断尝试获取这个文件上的锁,一旦获取到了,就可以继续提供服务。
6.7 HMaster上线和下线
HMaster启动会依次进行以下步骤:
- 从Zookeeper上获取唯一一个master的锁,用来阻止其它HMaster成为工作HMaster。
- 扫描Zookeeper上的server目录,获得当前可用的HRegionServer列表。
- 和2中的每个HRegionServer通信,获得当前已分配的region和HRegionServer的对应关系。
- 扫描.META.中region的集合,计算得到当前还未分配的region,将他们放入待分配region列表。
由于HMaster只维护表和region的元数据,而不参与表数据IO的过程,因此HMaster下线仅导致所有元数据的修改被冻结(无法创建删除表,无法修改表的schema,无法进行region的负载均衡,无法处理region上下线,无法进行region的合并,唯一例外的是region的split可以正常进行,因为只有HRegionServer参与),表的数据读写还可以正常进行。因此HMaster下线短时间内对整个HBase集群没有影响。
从上线过程可以看到,HMaster保存的信息全是可以冗余信息(都可以从系统其它地方收集到或者计算出来),因此,一般HBase集群中总是有一个HMaster在提供服务,还有一个以上的HMaster在等待时机抢占它的位置。
七、结束
本文对HBase技术在功能和设计上进行了大致的介绍,由于篇幅有限,本文没有过多深入地描述HBase的一些细节技术。
参考:
[1] www.searchtb.com/2011/01/understanding-hbase.html
[2] blog.csdn.net/john_f_lau/article/category/1832241
[3] my.oschina.net/zhengyang841117/blog/188723