HBase详解
1.HBase简介
HBase是一个在HDFS上开发的面向列的分布式数据库,能够对大型数据提供随机、实施的读写访问。HBase的运行依赖于其他文件系统,他模仿并提供了基于Google文件系统(GFS)中大表(BigTable)数据库的所有功能。
虽然数据库存储和检索的实现可以选择很多不同的才策略,但是绝大数解决办法--—特别是关系数据库技术的变种----不是为了大规模可伸缩的分布式处理设计的。很多厂商提供复制和分区解决方案,让数据库能够从单节点扩展出去,但是这些技术大都属于“事后”的解决办法,而且非常难以安装和维护。
HBase从另一个方向来解决可伸缩性的问题。它自底向上进行构建,能够简单的通过增加节点来达到线性扩展。HBase并不是关系型数据库,它不支持SQL。它能实现在廉价硬件构成的集群上管理超大规模的稀疏表。
HBase存储的是松散型数据。具体来说,HBase存储的数据介于映射(key/value)和关系型数据之间。
如上图所示,HBase存储的数据可以理解为一种key和value的映射关系,但又不是简简单单的映射关系。除此之外它还具有许多其他的特性。
HBase存储的数据从逻辑上来看就像一张很大的表,并且它的数据行可以根据需要动态增加。除此之外,每个cell(有行和列所确定的位置)中的数据又可以具有多个版本(通过时间戳来区别)。从上图中还可以看出,HBase具有这样的特点:他向下提供了存储,向上提供了运算;另外,在HBase之上还可以使用hadoop的MapReduce计算模型来并行处理大规模数据,这也是它具有强大性能的核心所在,它将数据存储和并行计算完美的结合在一起。
HBase的一个典型应用是webtable,一个以网页URL为主键的表,其中包含爬取的页面和页面的属性。Webtable非常大,行数可以达十亿级。在webtable上连续、批处理的运用分析和解析的MapReduce作业,从而获取相关的统计信息,增加验证的MIME类型列,提供搜索引擎进行索引的解析后的文本内容。与此同时,“爬取器(crawler)”随机的以不同速度访问表中的不同行,更新它们的内容;在用户点击访问网站的缓存页面时,这些随机访问的页面实时提供给它们使用。
2. 概念
2.1. 数据模型简介
应用把数据存放在带标签的表中。表有行和列组成。表格“单元格”----由行和列的坐标交叉决定---是有版本的。默认情况下,版本号自动分配,是HBase插入单元格时的时间戳。单元格的内容是未解释的字节数组。
表中行的键也是字节数组。所以理论上,任何其他东西都可以表示成二进制形式,然后转化为长整型的字符串或直接对数据结构进行序列化,来作为键值。表中的行根据行的键值(也就是表的主键)进行排序。排序根据字节序进行。所有对表的访问都要通过表的主键。
行中的列分为“列族(column family)”。所有的列族成员都有相同的前缀。因此,像列temperature:air和temperature:dew_point都是列族temperature的成员,而station:identifier则属于station族。列族的前缀必须由“可打印的”字符组成。而修饰的结尾字符,即列族修饰符,可以为任意字节。
一个表的列族必须作为表模式定义的一部分预先给出。但是新的列族成员可以随后按需要加入。如:只要目标表中已经有了列族station,那么客户端就在更新时提供新的列station:address,并存储它的值。
物理上,所有的列族成员都一起存放在文件系统中。所以,虽然前面把HBase描述为一个面向列的存储器,但更准确的说法实际上是它是一个面向列族的存储器。由于调优和存储都是在列族这个层次上进行的,所以最好使用所有列族成员都有相同的“访问模式”和大小特征。总之,HBase和RDBMS中的表类似,单元格有版本,行是排序的,而只要列族预先存在,客户端随时可以把列添加都列族中去。
2.2. 区域
HBase自动把表水平划分成“区域”。每个区域由表中行的子集构成。每个区域由它所属于的表、它所包含的第一行及其最后一行来表示。一开始,一个表只有一个区域。但是随着区域开始变大,等到它超出设定的大小阀值,便会在某行的边界上把表分成两个大小基本相同的新分区。在第一次划分之前,所有加载的数据都放在原始区域所在的那台服务器上。随着表变大,区域的个数也会增加。区域是在hbase集群分布数据的最小单位。用这种方式,一个因为太大而无法在单台服务器上的表会被放到服务器集群上,其中每个节点都负责管理表所有区域的一个子集。表的加载也是使用这种方法把数据分布到各个节点去的。在线的所有区域按次序排列就构成了表的所有内容。
2.3. 加锁
无论对行进行访问的事物牵涉到多少列,对行的更新都是“原子的”。这使得“加锁模型”能够保持简单。
2.4.HBase数据模型
2.4.1.Table&column Family
组成部件说明:
RowKey:Table主键 行健 Table中记录按照Row key排序
Column Family: 列族,一个table在水平方向有一个或者多个列族,列族可由任意多个column组成,列族支持动态扩展,无需预定义数据及类型,二进制存储,用户需自进行类型转换。
Column:属于某一个columnfamily,familyName:columnName,每条记录可以动态添加。
Version number:类型为long,模式值是系统时间戳(timestamp),用户可以动态添加
Value(cell):byte array
2.4.2.Table&Regin
1、Table随着记录增多不断变大,会自动分裂成多份splits,成为Rgions。
2、一个 region由【startkey、endkey】表示
3、不同region会被master分配给相应的regionserver进行管理。
2.5.HBase物理模型
- 每个column family存储在hdfs上的单个文件中,并且空值不会被保存;
- rowKey和version number在每个column family中均有一份;
- HBase为了每一个值维护了多级索引,即:<rowkey, timestamp,column family,column name >
2.5.1.物理存储
1、Table中所有行都会按照rowkey的字典排序列;
2、Table在行的方向上分割为多个region;
3、Region按大小分割的,每个表开始只有一个region,随着数据增加,region不断增大,当增大到一个阀值的时候,region会等分分割为列个新的region,之后会越来越多;
4、Region是HBase中分布式存储和负载均衡的最小单位,不同region分布到不同Regionserver上。
5、Region虽然是分布式存储的最小单元,但并不是存储的最小单元。Region由一个或者多个store组成,每个store又由一个memStore和0至多个storefile组成,storefile包含HFile;memstore存储在内存中,storefile存储在hdfs上。
3. 实现
正如HDFS和MapReduce由客户端、从属机和协调主控机组成,Hbase也采用相同的模型,他用一个Master节点协调管理一个或多个Regionserver从属机。Hbase主控机负责启动和全新的安装、把区域分配给注册的Regionserver,恢复Regionserver的故障。Master的负载很轻。Regionserver负责零个或多个区域的管理以及响应客户端的去写请求。Regionserver还负责区域的划分,并通知Hbase Master有了新的子区域,这样主控机就可以把父区域设为离线,并用子区域替换父区域。
Hbase依赖于zookeeper。默认情况下,它管理一个zookeeper实例,作为集群的权威。Hbase负责根目录表的位置,当前集群主控机地址类似重要信息的管理。如果区域的分配过程中有服务器崩溃,就通过zookeeper来协调分配。在zookeeper上管理分配事物的状态有助于恢复时可以从崩溃服务器遗留的状态开始继续分配。在启动一个客户端到Hbase集群的连接时,客户端必须至少拿到集群所传递的zookeeper整体的位置。这样,客户端才能访问zookeeper层次,了解集群的属性,如服务器的位置。
类似于在hadoop中可以通过conf/slaves文件查看datanode和tasktracker,Regionserver从属机节点列在Hbase的conf/regionserver文件中。启动和结束服务的脚本也使用hadoop一样基于ssh的远程命令机制来运行。集群的站点配置在HBase的conf/hbase-site.xml和conf/hbase-env.sh文件中。它们的格式和hadoop项目中对应的格式相同。
HBase通过hadoop文件系统API来持久化存储数据。有多种文件系统接口的实现。如:本地接口、kfs文件系统、HDFS等。
3.1. 运行中的HBase
HBase内部保留名称-root-和.meta.的特殊目录表(catalog table)。它们维护着当前集群上所有区域的列表、状态和位置。-root-表包含.meta.表的区域列表。.meta.表包含所有空间区域的列表。表中的项使用区域名称作为键。区域名有所属的表名、区域的起始行、区域的创建时间以及对整体进行的MD5组成。
如前所述,表的键是排序的。因此,要查找一个特定行所在的区域只要在目录表中找到一个键大于或等于给行键即可。区域变化时—即分裂、禁用/启用、删除、为负载均衡新部署区域或由于regionserver崩溃而重新部署区域时—目录表会进行相应的更新。这样,集群上所有区域的状态信息就能保持是最新的。
连接到zookeeper集群上的客户端首先查找-ROOT-的位置。然后客户端通过-root-获取所请求行所在范围所属.meta.区域的位置。客户端接着查找.meta.区域来获取用户空间区域所在节点以及其位置。接着,客户端就可以直接和管理那个区域的Regionserver进行交互。
每个行操作可能要访问三次远程节点。为了节省这些代价,客户端会缓存他们遍历-root-时所获取的信息和.meta.位置以及空间区域的开始行和结束行。这样,他们以后不需要访问.meta.表也能得知区域存放的位置。客户端在碰到错误之前会一直使用所缓存的想。当发生错误时—即区域被移动了—客户端会再次查看.meta.获取区域的新位置。如果.meta.区域也被移动了,客户端会再去查看-root-。
到达Regionserver的写操作首先追加到“提交日志”,然后在加入内存中的memstore。如果memstore满,它的内容会被“刷入”文件系统。
提交日志存放在HDFS中,因此即使一个Regionserver崩溃,提交日志仍然可用。如果发现一个Regionserver不能访问—通常因为服务器的znode在zookeeper中过期了—主控机会根据区域对死掉的Regionserver的提交日志进行分割。在重新分配后,打开并使用死掉的regionserver上的区域之前,这些区域会找到刚分割得到的文件,其中包括还没有持久化存储的更新。这些更新会被“重做”使区域恢复到服务器失败前夕的状态。
在读的时候首先查看区域的memstore。如果在memstore中找到了需要的版本,直接返回即可。否则,需要按照次序从新到旧检查“刷新文件”,直到找到满足查询的版本,或所有刷新文件都处理完为止。
有一个后台进程负责在刷新文件个数到达一个阀值是压缩它们。它把多个文件重新写入一个文件,因为读操作检查的文件越少,他的执行效率越高。在压缩时,超出模式所设最大值的版本以及被删除或过期的单元格会被清理掉。在regionserver上,另外有一个独立的进程监控者刷新文件的大小,一旦文件大小超出设定的最大值,便会对区域进行分割。
3.2. HBase体系结构
HBase的服务器体系结构遵循了简单的主从服务器架构,它由ReginServer群和Master服务器构成。HBase Master服务器负责管理所有的ReginServer服务器,而HBase中所有的服务器都是通过zookeeper来进行协调,并处理HBase服务器运行期间可能遇到的错误。
3.3. HBase系统架构图
组成部件说明:
3.3.1.Client
使用HBase RPC机制与HMaster和HRegionServer进行通讯,并维护cache来加速对HBase的访问,比如region的位置信息。
- Client与HMaster进行通讯进行管理类操作
- Client与HRegionServer进行数据读写类操作
3.3.2.Master
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master在运行,主要负责Table和Region的管理工作:
1 、管理用户对表的增删改查操作
2 、管理HRegionServer的负载均衡,调整Region分布
3、 Region Split后,负责新Region的分布
4、 在HRegionServer停机后,负责失效HRegionServer上Region迁移
3.3.3.Region Server
HRegionServer管理一些列HRegion对象;
每个HRegion对应Table中一个Region,HRegion由多个HStore组成;
每个HStore对应Table中一个Column Family的存储;
Column Family就是一个集中的存储单元,故将具有相同IO特性的Column放在一个Column Family会更高效
3.3.4.Zookeeper
Zookeeper Quorum存储-ROOT-表地址、HMaster地址;HRegionServer把自己以Ephedral方式注册到Zookeeper中,HMaster随时感知各个HRegionServer的健康状况。Zookeeper避免HMaster单点问题。
- 通过选举,保证任何时候,集群中只有一个Master,Master与RegionServer启动会向zookeeper注册;
- 贮存所有Region的寻址入口;
- 实时监控Region Server的上线和下线信息,并实时通知给Master;
- 存储Hbase的schema和Table元数据;
- 默认情况下,Hbase管理zookeeper实例,比如:启动或者停止zookeeper;
- Zookeeper的引入使得Master不再是单点故障;
3.3.5.HLog
1、引入HLog原因:在分布式系统环境中,无法避免系统出错或者宕机,一旦HRegionServer以外退出,MemStore中的内存数据就会丢失,引入HLog就是防止这种情况。
2、工作机制:每个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件,HLog文件定期会滚动出新,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的HLog文件,将不同region的log数据拆分,分别放到相应region目录下,然后再将失效的region重新分配,领取到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。
3、HLog File:
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。
3.3.6.HFile
HFile文件不定长,长度固定的块只有两个:Trailer和FileInfo。Trailer中指针指向其他数据块的起始点;FileInfo中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。
Data Index和Meta Index块记录了每个Data块和Meta块的起始点。
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制
每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询;每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏
HFile里面的每个KeyValue对就是一个简单的byte数组。这个byte数组里面包含了很多项,并且有固定的结构。
总结:HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,格式主要有两种--1 HFile HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile;2 HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File;
3.3.7.Table与HFILE的关系
在上图中所示table表中存在两个CF,分别是info和pwd。它们在hbase中的物理存储方式如下:
从上图可以看到row1和row5的数据分布在两个cf中,并且每个cf对应一个Hfile。并且逻辑上每一行的一个单元格数据,对应于HFile中的一行,然后用户按照row-key查询数据的时候,Hbase会遍历两个hfile,通过相同的row-key标识,将相关的单元格组织成行返回,这样便有了逻辑上的行数据。