欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Hbase架构剖析

程序员文章站 2022-06-23 22:42:18
HBase隶属于hadoop生态系统,它参考了谷歌的BigTable建模,实现的编程语言为 Java, 建立在hdfs之上,提供高可靠性、高性能、列存储、可伸缩、实时读写的数据库系统。它仅能通过主键(row key)和主键的range来检索数据,主要用来存储非结构化和半结构化的松散数据。与hadoo ......

  hbase隶属于hadoop生态系统,它参考了谷歌的bigtable建模,实现的编程语言为 java, 建立在hdfs之上,提供高可靠性、高性能、列存储、可伸缩、实时读写的数据库系统。它仅能通过主键(row key)和主键的range来检索数据,主要用来存储非结构化和半结构化的松散数据。与hadoop一样,hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加计算和存储能力。hbase数据库中的表一般有这样的特点:

  • 大: 一个表可以有上亿行,上百万列
  • 面向列:  面向列(族)的存储和权限控制,列(族)独立检索
  • 稀疏: 对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏

目录:

  • 系统架构
  • 数据模型
  • regionserver
  • namespace
  • hbase寻址
  • write
  • compaction
  • splite
  • read

系统架构:

hbase采用master/slave架构搭建集群,由hmaster节点、hregionserver节点、zookeeper集群组成,而在底层,它将数据存储于hdfs中,因而涉及到hdfs的nn、dn等,总体结构如下(注意:在hadoop(四): 本地 hbase 集群配置 azure blob storage 介绍过,也可以将底层的存储配置为 azure blob storage 或 amazon web services),图a较清楚表达各组件之间的访问及内部实现逻辑,图b更直观表达hbase 与 hadoop hdfs 部署结构及 hadoop nn 和 hmaster 的 spof 解决方案

Hbase架构剖析

架构图a

Hbase架构剖析

 

 

 架构图b

  • client的主要功能:
  1. 使用hbase的rpc机制与hmaster和hregionserver进行通信
  2. 对于管理类操作,client与hmaster进行rpc
  3. 对于数据读写类操作,client与hregionserver进行rpc
  • zookeeper功能:
  1. 通过选举,保证任何时候,集群中只有一个master,master与regionservers 启动时会向zookeeper注册
  2. 实时监控region server的上线和下线信息,并实时通知给master
  3. 存贮所有region的寻址入口和hbase的schema和table元数据
  4. zookeeper的引入实现hmaster主从节点的failover

详细工作原理如下图:

Hbase架构剖析

 

  1. 在hmaster和hregionserver连接到zookeeper后创建ephemeral节点,并使用heartbeat机制维持这个节点的存活状态,如果某个ephemeral节点失效,则hmaster会收到通知,并做相应的处理
  2. hmaster通过监听zookeeper中的ephemeral节点(默认:/hbase/rs/*)来监控hregionserver的加入和宕机
  3. 在第一个hmaster连接到zookeeper时会创建ephemeral节点(默认:/hbasae/master)来表示active的hmaster,其后加进来的hmaster则监听该ephemeral节点,如果当前active的hmaster宕机,则该节点消失,因而其他hmaster得到通知,而将自身转换成active的hmaster,在变为active的hmaster之前,它会创建在/hbase/back-masters/下创建自己的ephemeral节点

 

  • hmaster功能:
    1. 管理hregionserver,实现其负载均衡
    2. 管理和分配hregion,比如在hregion split时分配新的hregion;在hregionserver退出时迁移其内的hregion到其他hregionserver上
    3. 监控集群中所有hregionserver的状态(通过heartbeat和监听zookeeper中的状态)
    4. 处理schema更新请求 (创建、删除、修改table的定义), 如下图:

Hbase架构剖析

 

 

 

  • hregionserver功能:
  1. region server维护master分配给它的region,处理对这些region的io请求
  2. region server负责切分在运行过程中变得过大的region
  • 小结:
  1. client访问hbase上数据的过程并不需要master参与(寻址访问zookeeper,数据读写访问regione server),master仅仅维护者table和region的元数据信息,负载很低
  2. hregion所处理的数据尽量和数据所在的datanode在一起,实现数据的本地化

数据模型:


  • table: 与传统关系型数据库类似,hbase以表(table)的方式组织数据,应用程序将数据存入hbase表中
  • row: hbase表中的行通过 rowkey 进行唯一标识,不论是数字还是字符串,最终都会转换成字段数据进行存储;hbase表中的行是按rowkey字典顺序排列
  • column family: hbase表由行和列共同组织,同时引入列族的概念,它将一列或多列组织在一起,hbase的列必须属于某一个列族,在创建表时只需指定表名和至少一个列族
  • cell: 行和列的交叉点称为单元格,单元格的内容就是列的值,以二进制形式存储,同时它是版本化的
  • version: 每个cell的值可保存数据的多个版本(到底支持几个版本可在建表时指定),按时间顺序倒序排列,时间戳是64位的整数,可在写入数据时赋值,也可由regionserver自动赋值
  • 注意:
  1. hbase没有数据类型,任何列值都被转换成字符串进行存储
  2. 与关系型数据库在创建表时需明确包含的列及类型不同,hbase表的每一行可以有不同的列
  3. 相同rowkey的插入操作被认为是同一行的操作。即相同rowkey的二次写入操作,第二次可被可为是对该行某些列的更新操作
  4. 列由列族和列名连接而成, 分隔符是冒号,如  d:name  (d: 列族名, name: 列名)
  • 以一个示例来说明关系型数据表和hbase表各自的解决方案(示例:博文及作者),关系型数据库表结构设计及数据如下图:

Hbase架构剖析

 

 

 表结构设计

Hbase架构剖析

 

 

 示例数据

 

用hbase设计表结构如下图:

 Hbase架构剖析

 

 

 存储示例数据如下:

Hbase架构剖析

 

 

 

  • 小结:
    1. hbase不支持条件查询和order by等查询,读取记录只能按row key(及其range)或全表扫描
    2. 在表创建时只需声明表名和至少一个列族名,每个column family为一个存储单元,在下节物理模型会详细介绍
    3. 在上例中设计了一个hbase表blog,该表有两个列族:article和author,但在实际应用中强烈建议使用单列族
    4. column不用创建表时定义即可以动态新增,同一column family的columns会群聚在一个存储单元上,并依column key排序,因此设计时应将具有相同i/o特性的column设计在一个column family上以提高性能。注意:这个列是可以增加和删除的,这和我们的传统数据库很大的区别。所以他适合非结构化数据
    5. hbase通过row和column确定一份数据,这份数据的值可能有多个版本,不同版本的值按照时间倒序排序,即最新的数据排在最前面,查询时默认返回最新版本。如上例中row key=1的author:nickname值有两个版本,分别为1317180070811对应的“一叶渡江”和1317180718830对应的“yedu”(对应到实际业务可以理解为在某时刻修改了nickname为yedu,但旧值仍然存在)。timestamp默认为系统当前时间(精确到毫秒),也可以在写入数据时指定该值
    6. 每个单元格值通过4个键唯一索引,tablename+rowkey+columnkey+timestamp=>value, 例如上例中{tablename=’blog’,rowkey=’1’,columnname=’author:nickname’,timestamp=’ 1317180718830’}索引到的唯一值是“yedu”
    7. 存储类型:
    • tablename 是字符串
    • rowkey 和 columnname 是二进制值(java 类型 byte[])
    • timestamp 是一个 64 位整数(java 类型 long)
    • value 是一个字节数组(java类型 byte[])

regionserver:


  • hregionserver一般和dn在同一台机器上运行,实现数据的本地性,如图b。hregionserver包含多个hregion,由wal(hlog)、blockcache、memstore、hfile组成,如图a,其中图a是0.94-的架构图,图b是0.96+的新架构图

Hbase架构剖析

 

图a

Hbase架构剖析

 

 

 

 图b

  • wal(write ahead log):它是hdfs上的一个文件,所有写操作都会先保证将数据写入这个log文件后,才会真正更新memstore,最后写入hfile中
  • 采用这种模式,可以保证hregionserver宕机后,依然可以从该log文件中读取数据,replay所有的操作,来保证数据的一致性
  • 一个hregionserver只有一个wal实例,即一个hregionserver的所有wal写都是串行,这当然会引起性能问题,在hbase 1.0之后,通过hbase-5699实现了多个wal并行写(multiwal),该实现采用hdfs的多个管道写,以单个hregion为单位
  • log文件会定期roll出新的文件而删除旧的文件(那些已持久化到hfile中的log可以删除)。wal文件存储在/hbase/wals/${hregionserver_name}的目录中
  • blockcache(图b):是一个读缓存,将数据预读取到内存中,以提升读的性能
  • hbase中提供两种blockcache的实现:默认on-heap lrublockcache和bucketcache(通常是off-heap)。通常bucketcache的性能要差于lrublockcache,然而由于gc的影响,lrublockcache的延迟会变的不稳定,而bucketcache由于是自己管理blockcache,而不需要gc,因而它的延迟通常比较稳定,这也是有些时候需要选用bucketcache的原因
  • hregion:是一个table中的一个region在一个hregionserver中的表达,是hbase中分布式存储和负载均衡的最小单元
  • 一个table拥有一个或多个region,分布在一台或多台hregionserver上
  • 一台hregionserver包含多个hregion,可以属于不同的table
  • 见图a,hregion由多个store(hstore)构成,每个hstore对应了一个table在这个hregion中的一个column family,即每个column family就是一个集中的存储单元
  • hstore是hbase中存储的核心,它实现了读写hdfs功能,一个hstore由一个memstore 和0个或多个storefile组成
  • memstore:是一个写缓存(in memory sorted buffer),所有数据的写在完成wal日志写后,会 写入memstore中,由memstore根据一定的算法将数据flush到底层hdfs文件中(hfile),通常每个hregion中的每个 column family有一个自己的memstore
  • hfile(storefile): 用于存储hbase的数据(cell/keyvalue)。在hfile中的数据是按rowkey、column family、column排序,对相同的cell(即这三个值都一样),则按timestamp倒序排列
  • 小结:
  1. table中的所有行都按照row key的字典序排列,table 在行的方向上分割为多个hregion,如下图1
  2. region按大小分割的,每个表一开始只有一个region,随着数据不断插入表,region不断增大,当增大到一个阀值的时候,hregion就会等分会两个新的hregion,如下图2

Hbase架构剖析

 

图1

 

 Hbase架构剖析

 

图2

        3、hregion是hbase中分布式存储和负载均衡的最小单元。最小单元就表示不同的hregion可以分布在不同的hregion server上。但一个hregion是不会拆分到多个server上的,如下图

Hbase架构剖析

 

 

        4、hregion虽然是分布式存储的最小单元,但并不是存储的最小单元。事实上,hregion由一个或者多个store组成,每个store保存一个columns family,每个strore又由一个memstore和0至多个storefile组成,如下图,说明:storefile以hfile格式保存在hdfs上

Hbase架构剖析

 

namespace:


  • 在hbase中,namespace命名空间指对一组表的逻辑分组,类似rdbms中的database,方便对表在业务上划分。
  • apache hbase从0.98.0, 0.95.2两个版本开始支持namespace级别的授权操作,hbase全局管理员可以创建、修改和回收namespace的授权
  • hbase系统默认定义了两个缺省的namespace,见如下图的目录结构:
  1. hbase:系统内建表,包括namespace和meta表
  2. default:用户建表时未指定namespace的表都创建在此

Hbase架构剖析 Hbase架构剖析

 

 

 

hbase寻址:


  • 本节主要讨论的问题:client访问用户数据时如何找到某个row key所在的region?
  • 0.94- 版本 client访问用户数据之前需要首先访问zookeeper,然后访问-root-表,接着访问.meta.表,最后才能找到用户数据的位置去访问,中间需要多次网络操作,如下图:

 Hbase架构剖析

 

  •  0.96+ 删除了root 表,改为zookeeper里面的文件,如下图 a, 以读为例,寻址示意图如b

Hbase架构剖析

图a

 

Hbase架构剖析

 

 

 

 图b

write:


  • 当客户端发起一个put请求时,首先根据rowkey寻址,从hbase:meta表中查出该put数据最终需要去的hregionserver
  • 客户端将put请求发送给相应的hregionserver,在hregionserver中它首先会将该put操作写入wal日志文件中(flush到磁盘中),如下图:

Hbase架构剖析

 

 

  • 完wal日志文件后,hregionserver根据put中的tablename和rowkey找到对应的hregion,并根据column family找到对应的hstore
  • 将put数据写入到该hstore的memstore中。此时写成功,并返回通知客户端

Hbase架构剖析

 

 

  • 上一节介绍过,memstore是一个in memory sorted buffer,在每个hstore中都有一个memstore,即它是一个hregion的一个column family对应一个实例。
  • 它的排列顺序以rowkey、column family、column的顺序以及timestamp的倒序,如下示意图:

Hbase架构剖析

 

 

  • 每一次put请求都是先写入到memstore中,当memstore满后会flush成一个新的storefile(底层实现是hfile),即一个hstore(column family)可以有0个或多个storefile(hfile)
  • 注意:memstore的最小flush单元是hregion而不是单个memstore, 这就是建议使用单列族的原因,太多的column family一起flush会引起性能问题
  • memstore触发flush动作的时机:
  1. 当一个memstore的大小超过了hbase.hregion.memstore.flush.size的大小,此时当前的hregion中所有的memstore会flush到hdfs中
  2. 当全局memstore的大小超过了hbase.regionserver.global.memstore.upperlimit的大小,默认40%的内存使用量。此时当前hregionserver中所有hregion中的memstore都会flush到hdfs中,flush顺序是memstore大小的倒序,直到总体的memstore使用量低于hbase.regionserver.global.memstore.lowerlimit,默认38%的内存使用量
  3. 待确认:一个hregion中所有memstore总和作为该hregion的memstore的大小还是选取最大的memstore作为参考?
  4. 当前hregionserver中wal的大小超过了hbase.regionserver.hlog.blocksize * hbase.regionserver.max.logs的数量,当前hregionserver中所有hregion中的memstore都会flush到hdfs中,flush使用时间顺序,最早的memstore先flush直到wal的数量少于hbase.regionserver.hlog.blocksize * hbase.regionserver.max.logs
  5. 注意:因为这个大小超过限制引起的flush不是一件好事,可能引起长时间的延迟
  • 在memstore flush过程中,还会在尾部追加一些meta数据,其中就包括flush时最大的wal sequence值,以告诉hbase这个storefile写入的最新数据的序列,那么在recover时就直到从哪里开始。在hregion启动时,这个sequence会被读取,并取最大的作为下一次更新时的起始sequence,如下图:

Hbase架构剖析

 

 

compaction:


  • memstore每次flush会创建新的hfile,而过多的hfile会引起读的性能问题,hbase采用compaction机制来解决这个问题
  • hbase中compaction分为两种:minor compaction和major compaction
  1. minor compaction: 是指选取一些小的、相邻的storefile将他们合并成一个更大的storefile,在这个过程中不会处理已经deleted或expired的cell。一次minor compaction的结果是更少并且更大的storefile, 如下图:

Hbase架构剖析

 

 

  1. major compaction: 是指将所有的storefile合并成一个storefile,在这个过程中,标记为deleted的cell会被删除,而那些已经expired的cell会被丢弃,那些已经超过最多版本数的cell会被丢弃。一次major compaction的结果是一个hstore只有一个storefile存在
  2. major compaction可以手动或自动触发,然而由于它会引起很多的io操作而引起性能问题,因而它一般会被安排在周末、凌晨等集群比较闲的时间, 如下示意图:

Hbase架构剖析

 

 

  • 修改hbase配置文件可以控制compaction行为
    1. hbase.hstore.compaction.min :默认值为 3,(老版本是:hbase.hstore.compactionthreshold),即store下面的storefiles数量 减去 正在compaction的数量 >=3是,需要做compaction
    2. hbase.hstore.compaction.max 默认值为10,表示一次minor compaction中最多选取10个store file
    3. hbase.hstore.compaction.min.size 表示文件大小小于该值的store file 一定会加入到minor compaction的store file中
    4. hbase.hstore.compaction.max.size 表示文件大小大于该值的store file 一定会被minor compaction排除

splite:


  • 最初,一个table只有一个hregion,随着数据写入增加,如果一个hregion到达一定的大小,就需要split成两个hregion,这个大小由hbase.hregion.max.filesize指定
  • split时,两个新的hregion会在同一个hregionserver中创建,它们各自包含父hregion一半的数据,当split完成后,父hregion会下线,而新的两个子hregion会向hmaster注册上线
  • 处于负载均衡的考虑,这两个新的hregion可能会被hmaster分配到其他的hregionserver,示意图如下:

Hbase架构剖析

 

 

  1. 在zookeeper上创建ephemeral的znode指示parent region正在splitting
  2. hmaster监控父regerion的region-in-transition znode
  3. 在parent region的文件夹中创建临时split目录
  4. 关闭parent region(会flush 所有memory store(memory file),等待active compaction结束),从现在开始parent region 不可服务。同时从本地server上offline parent region,每个region server都维护了一个valid region的list,该步将parent region从该list中移除
  5. split所有的store file,这一步为每个文件做一个reference file,reference file由两部分组成
    1. 第一部分是源文件的路径,第二部分是新的reference file引用源文件split key以及引用上半截还是下半截
    2. 举个例子:源文件是table1/storefile.11,split point 是key1, 则split 成两个子文件可能可能是table1/storefile.11.bottom.key1,table1/storefile.11.up.key1,表示从key1切开storefile.11后,两个引用文件分别引用源文件的下半部分和上半部分
  6. 创建child region
    1. 设置各种属性,比如将parent region的访问指标平分给child region,每人一半
    2. 将上面在parent 文件夹中生成的临时文件夹(里面包含对parent region的文件reference)move到表目录下,现在在目录层次上,child region已经跟parent region平起平坐了
  7. 向系统meta server中写入parent region split完毕的信息,并将child region的名字一并写入(split状态在meta层面持久化)
  8. 分别open 两个child region,主要包含以下几个步骤:
    1. 将child region信息写入meta server
    2. load 所有store file,并replay log等
    3. 如果包含reference文件,则做一次compaction(类似merge),直到将所有的reference文件compact完毕,这里可以看到parent region的文件是会被拆开写入各个child regions的
  9. 将parent region的状态由splitting转为split,zookeeper会负责通知master开始处理split事件,master开始offline parent region,并online child regions
  10. worker等待master处理完毕之后,确认child regions都已经online,split结束

 read:


  • 根据rowkey寻址(详情见上一节寻址部分),如下图:

Hbase架构剖析

 

 

获取数据顺序规则,如下图:

Hbase架构剖析

 

 参考和来源:

参考资料: