HBase 架构组件
Hbase 是由三种类型的 server 组成的主从式(master-slave)架构:
- Zookeeper,分布式协调节点,负责维护集群状态。
- HBase Master 管理节点,负责 Region 的分配、上下线,DDL(创建,删除 table)等操作。
- Region Server 存储节点,负责处理数据的读写请求,客户端请求数据时直接和 Region Server 交互。
Zookeeper
HBase 使用 Zookeeper 做分布式管理服务,来维护集群中所有服务的状态。Zookeeper 维护了哪些 servers 是健康可用的,并且在 server 故障时做出通知。主要监听Region Server状态并且上报给HMaster,同时负责HMaster ha切换。
HBase Master
也叫 HMaster,管理节点,负责 Region 的分配、上下线,DDL(创建,删除表)等操作:
- 启动时分配 region,在故障恢复和负载均衡时重分配 region
- 监控集群中所有 Region Server 实例
- 提供创建,删除和更新 HBase Table 的接口
Region Server
Region Server负责region存储,接收客户端读写操作,由以下组件组成:
- WAL:Write Ahead Log , 预写日志, 记录了数据写入、更新日志,它被用来做故障恢复。与MySQL中redo-log 相对应。
- HFile 持久化在HDFS上的数据,以有序 KeyValue 的形式。
- MemStore:写缓存,数据首先会被写入到MemStore中。每个 Region 的每个 Column Family 都会有一个 MemStore。
- BlockCache:读缓存,在内存中存储了最常访问的数据,是 LRU(Least Recently Used)缓存。
底层的存储基于分布式文件系统,常见基于 Hadoop HDFS :
- Hadoop NameNode 管理节点,维护了所有 HDFS 物理 data block 的元信息与DataNode 节点信息。
- Hadoop DataNode 存储节点,负责存储 Region Server 所管理的数据。所有的 HBase 数据都存储在 HDFS 文件中。Region Server 和 HDFS DataNode 往往是分布在一起的,也就是部署在一起, 这样 Region Server 就能够实现数据本地化(data locality,即将数据放在离需要者尽可能近的地方)。HBase 的数据在写的时候是本地的,但是当 region 被迁移的时候,迁移往往是为了负载均衡或者是regionServer重新被拉起, 数据就可能不再满足本地性了,直到完成 compaction,才能又恢复到本地。
Region
Region是HBase数据管理的基本单元,表(Table)根据 rowkey 的范围被水平拆分成若干个 region。Region被分配给集群中的Region Server来管理,由Region Server来负责处理数据的读写请求。每个 Region Server 大约可以管理 1000 个 region。
组件之前协同工作
Zookeeper 用来协调分布式系统中集群状态信息的共享。Region Server 和 在线 HMaster(active HMaster)和 Zookeeper 保持会话(session)。Zookeeper 通过心跳检测来维护所有临时节点(ephemeral nodes)。
每个 Region Server 都会创建一个 ephemeral临时节点。HMaster 会监控这些节点来发现可用的 Region Server,同样它也会监控这些节点是否出现故障以及其负载情况。
HMaster 在做HA选举时,同样会创建 ephemeral临时节点,但是只有一个master节点可以创建成功,保证线上只有一个 HMaster。在线 HMaster会给 Zookeeper 发送心跳,不在线的待机 HMaster会监听 active HMaster 可能出现的故障并随时准备上位。
如果有一个 Region Server 或者 HMaster 出现故障或各种原因导致发送心跳失败,它们与 Zookeeper 的 session 就会过期,这个 ephemeral临时节点就会被删除,监听者们就会收到这个消息。Active HMaster 监听的是 region server下线的消息,然后会恢复故障的 region server 以及它所负责的 region 数据。而 Inactive HMaster 关心的则是 active HMaster 下线的消息,然后竞争上线变成 active HMaster。
Meta Table
Meta table 是一个特殊的 HBase table,它保存了系统中所有的 region 元数据信息,在Zookeeper中保留这张表的储存位置。这张 table 类似一个 b-tree,结构大致如下:
- Key:table, region start key, region id
- Value:region server
第一次读和写操作
当 HBase 第一次读或者写操作到来时:
- 客户端从 Zookeeper 那里获取是哪一台 Region Server 负责管理 Meta table。
- 客户端会查询被定位的Region Server上的Meta,进而获知是哪一台 Region Server 负责管理本次数据请求所需要的 rowkey。客户端会缓存这个信息,以及 Meta table 的位置信息本身。
- 然后客户端回去访问那台 Region Server,获取数据。
之后的请求,客户端从可以缓存中直接获取 Meta table 的位置信息(在哪一台 Region Server 上),以及之前访问过的 rowkey 的位置信息(哪一台 Region Server 上),除非因为 Region 被迁移了导致缓存失效(租约机制)。这时客户端会重复上面的步骤,重新获取相关位置信息并更新缓存。
(点评:客户端读写数据,实际上分了两步:第一步是定位,从 Meta table 获取 rowkey 属于哪个 Region Server 管理;第二步再去相应的 Region Server 读写数据。这里涉及到了两个 Region Server,要理解它们各自的角色功能。)
写数据
为了保证写入性能通常的操作就是先写内存,同时为了保证数据可靠性,会记录写入日志,也就是常说的WAL。当客户端发起一个写数据请求(Put 操作),第一步首先是将数据写入到 WAL 中:
- 新数据会被追加到 WAL 文件尾部。
- WAL 用来在故障恢复时恢复还未被持久化的数据。
数据被写入 WAL 后,会被加入到 MemStore 即写缓存。然后服务端就可以向客户端返回 ack 表示写数据完成。
(点评:注意数据写入时 WAL 和 MemStore 更新的顺序,不能调换,必须先 WAL 再 MemStore。如果反过来,先更新完 MemStore,此时 Region Server 发生 crash,内存中的更新就丢失了,而此时数据还未被持久化到 WAL,就无法恢复了。理论上 WAL 就是 MemStore 中数据的一个镜像,应该保持一致,除非发生系统 crash。另外注意更新 WAL 是在文件尾部追加的方式,即顺序写入,这种磁盘操作性能很高,不会太影响请求的整体响应时间。)
MemStore
MemStore 在内存中缓存 HBase 的更新数据,以有序 KeyValues 的形式,跳表数据结构即Skip-List, 这和 HFile 中的存储形式一样。每个 Column Family 都有一个 MemStore,所有的更新都以 Column Family 为单位进行排序。
HFile
数据持久化存储在 HFile 中,当 MemStore 累积了足够多的数据后,整个有序数据集就会被写入一个新的 HFile 文件到 HDFS 上。整个过程是一个顺序写的操作,速度非常快,因为它不需要移动磁盘头。
HFile 文件结构
HFile文件主要包含三部分内容:数据、索引、元数据。
HFile 使用多层索引来查询数据而不必读取整个文件,这种多层索引类似于一个 B+ tree:
- KeyValues 有序存储。
- rowkey 指向 index,而 index 则指向了具体的 data block,以 64 KB 为单位。
- 每个 block 都有它的叶索引。
- 每个 block 的最后一个 key 都被存储在中间层索引。
- 索引根节点指向中间层索引。
trailer 指向原信息数据块,它是在数据持久化为 HFile 时被写在 HFile 文件尾部。trailer 还包含例如布隆过滤器和时间范围等信息。布隆过滤器用来跳过那些不包含指定 rowkey 的文件,时间范围信息则是根据时间来过滤,跳过那些不在请求的时间范围之内的文件。
Compaction
由于不断写数据写入,可能会产生很多文件,导致读取数据时IO放大,因此会定期执行compaction操作即文件合并操作,以优化io,同时也会清理掉过期、删除的数据。
Major Compaction
合并重写每个 Column Family 下的所有的 HFiles,成为一个单独的大 HFile,在这个过程中,会执行数据的物理删除与过期数据清理。但是因为 major compaction 会重写所有的 HFile,会产生大量的硬盘 I/O 和网络开销。这被称为写放大(Write Amplification)。
Major compaction 可以被设定为自动调度。因为存在写放大的问题,major compaction 一般都安排在流量低峰期去执行。Major compaction 还能将因为服务器 crash 或者负载均衡导致的数据迁移重新移回到离 Region Server 的地方,这样就能恢复 data locality。
Minor Compaction
HBase 会自动合并一些小的 HFile,重写成少量更大的 HFiles。这个过程被称为 minor compaction。它使用归并排序算法,将小文件合并成大文件,有效减少 HFile 的数量。
MemStore Flush
MemStore 中累积了足够多的的数据后,默认128M, 或者是整个RegionServer 内存达到一定比率,MemStore 就会被flush 生成一个新的HFile文件。HBase 为每个 Column Family 都创建一个 HFile,里面存储了具体的 Cell,也即 KeyValue 数据。随着时间推移,HFile 会不断产生,因为 KeyValue 会不断地从 MemStore 中被刷写到硬盘上。
(点评:这里有个序列号的概念,每次 HBase 数据更新都会绑定一个新的自增序列号。而每个 HFile 则会存储它所保存的数据的最大序列号,这个元信息非常重要,它相当于一个 commit point,告诉我们在这个序列号之前的数据已经被持久化到硬盘了。它不仅在 region 启动时会被用到,在故障恢复时,也能告诉我们应该从 WAL 的什么位置开始回放数据的历史更新记录。)
Region 分裂
刚开始新建的table 默认只有一个 region。随着数据的不断写入,这个 region 逐渐变得很大时,它会执行分裂(split)操作,分成两个子 region,每个子 region 都包含了原来 region 一半的数据,这两个子 region 并行地在原来这个 region server 上创建,这个分裂动作会被报告给 HMaster。处于负载均衡的目的,HMaster 可能会将新的 region 迁移给其它 region server。
负载均衡
Splitting 一开始是发生在同一台 region server 上的,但是出于负载均衡的原因,HMaster 可能会将新的 region 迁移给其它 region server,这会导致那些 region server 需要访问离它比较远的 HDFS 数据,直到 major compaction 的到来,它会将那些远方的数据重新移回到离 region server 节点附近的地方。
(点评:注意这里的迁移的概念,只是逻辑上的迁移,即将某个 region 交给另一个 region server 管理。)
预分区 即新建表的时候预估数据量提前创建表region的个数,使其分布在不同的server上,以达到读写负载到不同server上。
数据备份
由于HBase 底层存储使用的是分布式文件系统,常见HDFS,HDFS 会自动备份 WAL 和 HFile 的文件 blocks,默认使用3副本。也就是HBase 依赖于 HDFS 来保证数据完整安全。当数据被写入 HDFS 时,一份会写入本地节点,另外两个备份会被写入其它节点。
WAL 和 HFiles 都会持久化到硬盘并备份。那么 HBase 是怎么恢复 MemStore 中还未被持久化到 HFile 的数据呢?下面的章节会讨论这个问题。
故障恢复
故障恢复分为两个方面:一个是HMaster 故障恢复、一个是Region Server故障恢复。
HMaster 故障恢复依赖于双Master模式,也就是常说的HA模式,一个active 、一个standby, standby master通过zookeeper监听active master, 当active master出现故障,standby master可快速执行选举操作,生成新的active master, 以提供服务。
当某个 Region Server 发生故障时,Zookeeper 依靠心跳检测机制发现节点故障,即HMaster会通过Zookeeper收到 region server 故障的通知。当 HMaster 发现某个 region server 故障,HMaster 会将这个 region server 所管理的 regions 分配给其它健康的 region servers。为了恢复故障region server 上还未被持久化到 HFile 的 MemStore 中数据,HMaster 会将 WAL 分割成几个文件,将它们保存在新的 region server 上。每个 region server 然后回放各自拿到的 WAL 碎片中的数据,来为它所分配到的新 region 建立 MemStore。
Apache HBase 架构的优点
- 强一致性
当 write 返回时,所有的 reader 都会读到同样的值。
- 自动扩展性
数据变大时 region 会分裂;使用 HDFS 存储备份数据。
- 内置恢复功能
使用 Write Ahead Log (类似于文件系统中的日志)
- 与 Hadoop 结合
使用 MapReduce 处理 HBase 数据会非常直观。
Apache HBase 也有问题
WAL 回放很慢;故障恢复很慢;Major Compaction 时候 I/O 会飙升。