论文:https://mapr.com/blog/in-depth-look-hbase-architecture/
split/merge :https://blog.cloudera.com/apache-hbase-region-splitting-and-merging/
半翻译版:
https://blog.csdn.net/joeyon/article/details/78355650
https://blog.csdn.net/joeyon/article/details/78356166
翻译版:https://segmentfault.com/a/1190000019959411
https://www.ctolib.com/topics-142149.html
架构
HBase的主从(master slave)架构由三种servers组成:
Region Servers主要负责数据读写。在访问数据时,client直接和RegionServers进行沟通。
HBase Master用于Region assignment 和 DDL (create, delete tables) 。
Zookeeper是HDFS的一部分,负责维护集群状态。
当然底层的存储都是基于 Hadoop HDFS 的:
Hadoop DataNode(对标GFS Chunk Server) 负责存储 Region Server 所管理的数据。所有的 HBase 数据都存储在 HDFS 文件中。Region Server 和 HDFS DataNode 往往是分布在一起的,这样 Region Server 就能够实现数据本地化(data locality,即将数据放在离Region Server尽可能近的地方)。HBase 的数据在写的时候是本地的,但是当 region 被迁移的时候,数据就可能不再满足本地性了,直到完成 compaction,才能又恢复到本地。
Hadoop NameNode (对标GFS Master)维护了所有文件的 HDFS 物理 data block 的元信息。
Regions
HBase 表(Table)根据 row key Range被水平拆分成若干个 region。每个 region 都包含了这个region 的 start key 和 end key 之间的所有行(row)。Regions 被分配给集群中的Region Server来管理,由它们来负责处理数据的读写请求。每个 Region Server 大约可以管理 1000 个 regions。
HBase Master
也叫 HMaster,负责 Region 的分配,DDL(创建,删除表)等操作:
统筹协调所有的region server:
1.启动时分配 regions,在故障恢复和负载均衡时重分配 regions
2.监控集群中所有 Region Server 实例的状态(从 Zookeeper 获取通知信息)
Admin functions:
1.提供创建,删除和更新 HBase Table 的接口
Zookeeper
HBase 使用 Zookeeper 做分布式管理服务,来维护集群中所有服务的状态。Zookeeper 维护了哪些 servers 是健康可用的,并且在 server 故障时做出通知。Zookeeper 使用一致性协议来保证分布式状态的一致性,一般需要三台或者五台机器来实现一致性协议。
How the Components Work Together
Zookeeper 用来协调分布式系统中集群状态信息的共享。Region Servers 与 active HMaster都和Zookeeper 保持session。Zookeeper 通过heartbeat来维护所有临时节点(ephemeral nodes)的active session。
每个 Region Server 都会在zookeeper中创建一个 ephemeral node(对标BigTable中TabletServer在Chubby的server dictionary中创建的独占锁)。HMaster 会监控这些节点来发现可用的 Region Servers,同样它也会监控这些节点是否出现故障。
HMaster 们会竞争创建 ephemeral 节点,而 Zookeeper 决定谁是第一个作为active HMaster,保证只有一个active HMaster。active HMaster会给 Zookeeper 发送心跳,inactive HMaster会监听 active HMaster 可能出现的故障并随时准备上位。
如果有一个 Region Server 或者 HMaster 出现故障或各种原因导致发送心跳失败,它们与 Zookeeper 的 session 就会过期,这个 ephemeral 节点就会被删除下线,监听者们就会收到这个消息。Active HMaster 监听的是 region servers 下线的消息,然后会恢复故障的 region server 以及它所负责的 region 数据。而 Inactive HMaster 关心的则是 active HMaster 下线的消息,然后竞争上线变成 active HMaster。
(Zookeeper 是沟通一切的桥梁,所有的参与者都和 Zookeeper 保持心跳会话,并从 Zookeeper 获取它们需要的集群状态信息,来管理其它节点,转换角色。)
First Read or Write
有一个特殊的 HBase table,叫 Meta table,它存放了集群中所有 regions 的位置信息。而这张 Meta table的region位置信息存放在Zookeeper。
当第一次读或者写操作到来时:
1.Client从 Zookeeper 那里获取是哪一台 Region Server 负责管理 Meta table(说明这张表只有一个region?)。
2.Client查询那台管理 Meta table 的 Region Server,进而获知是哪一台 Region Server 负责管理本次数据请求所需要的 row key。Client会缓存这个信息,以及 Meta table 的位置信息。
3.Client访问那台 Region Server,获取数据。
对于以后的读请求,客户端从可以缓存中直接获取 Meta table 的位置信息,以及之前访问过的 row key 的位置信息。当 Region 发生了迁移使缓存失效时,Client会重复上面的步骤,重新获取相关位置信息并更新缓存。
HBase Meta Table
Meta table 是一个特殊的 HBase table,它保存了系统中所有region的Location。这张 table 类似一个 b-tree,结构大致如下:
Key:table, region start key, region id
Value:region server
下面这个图是什么意思?是指meta table 被分为多个region嘛?
Region Server
Region Server 运行在 HDFS DataNode 上,由以下组件组成:
WAL(对标commit log):Write Ahead Log 是分布式文件系统上的一个文件,用于存储新的还未被持久化存储的数据,它被用来做故障恢复。(一个server一个WAL,所有的region共用WAL,和GFS一样 一个tabletserver一个commit log)
BlockCache:这是读缓存,在内存中存储了最常访问的数据,是 LRU cache。
MemStore(对标memtable):这是写缓存,在内存中存储了新的还未被持久化到硬盘的数据。当被写入硬盘时,数据会首先被排序。注意每个 Region 的每个 Column Family 都会有一个 MemStore。
HFile 在硬盘上(HDFS)存储 HBase 数据,以有序 KeyValue 的形式。按RowKey、Column Family、Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。
写数据步骤
当客户端发起一个写数据请求(Put 操作):
1.将数据追加到 WAL 文件尾部 。WAL 用来在故障恢复时恢复还未被持久化的数据。
2.数据被写入 WAL 后,RegionServer根据Put中的TableName和RowKey找到对应的HRegion,并根据Column Family找到对应的MemStore,并写入MemStore中。此时写成功,然后Server就可以向Client返回 ack 。
HBase MemStore
MemStore 在内存中缓存 HBase 的数据更新,以有序 Key/Values 的形式,这和 HFile 中的存储形式一样。Region的每个 Column Family 都有一个 MemStore,所有的更新都以 Column Family 为单位进行排序。它的Key由:【RowKey、Column Family、Column的升序以及Timestamp的倒序】组成 ,如下所示:
HBase Region Flush
MemStore 中累积了足够多的的数据后,整个有序数据集就会被写入一个新的 HFile 文件到 HDFS 上。每个 Column Family 都有多个HFiles,里面存储了具体的 Cell,即 KeyValue 数据。随着时间推移,KeyValue 会不断地从 MemStore 中被flush到硬盘上,HFile 会越来越多。
MemStore的最小Flush单元是HRegion而不是单个MemStore??。这也是为什么 HBase 要限制 Column Family 个数的原因。每个 Column Family 都有一个 MemStore;如果一个 MemStore 满了,该Region所有的 MemStore 都会被刷写到硬盘。同时它也会记录最后写入磁盘的数据的序列号(last sequence number),这样系统就能知道目前为止哪些数据已经被持久化了。
在MemStore Flush过程中,还会在尾部追加一些meta数据,其中就包括Flush时最大的WAL sequence number,以告诉HBase这个HFile写入的最新数据的序列,在HRegion Recover启动时,这个sequence会被读取,那么就知道从哪里开始recover WAL。并取最大sequence作为下一次更新操作的起始sequence。
HBase HFile
数据存储在 HFile 中,以 Key / Values 形式(和memStore一样)。当 MemStore 累积了足够多的数据后,整个有序数据集就会被写入一个新的 HFile 文件到 HDFS 上。由于MemStore中存储的Cell和HFile遵循相同的排列顺序,因而整个过程是一个顺序写的操作,速度非常快,因为它不需要移动磁盘头。
HBase HFile Structure
HFile 使用多层索引来查询数据而不必读取整个文件,这种多层索引类似于一个 B+ tree:
1. Key/Values对有序存储。
2. data block里存的是一个个cell,每个block 64 KB
3. 每个 block 都有接着一个leaf index, 也就是这个block里面所有key以及其在这个HFile的offset。
4. 每个 block 的最后一个 key 都被存储在中间层索引(intermediate index)。也就是中间层索引 存放了每个block的最后一个key 和这个block的leaf index (的offset)的映射关系。
5. 根索引(root index)指向中间层索引。
trailer位于 HFile 文件的尾部,它是在数据持久化为 HFile 完成后写入的,trailer指向meta blocks。trailer 还包含例如布隆过滤器和时间范围等信息。布隆过滤器用来跳过那些不包含指定 row key 的文件,时间范围信息则是根据时间来过滤,跳过那些不在请求的时间范围之内的文件。
首先根据root index找到中间层索引, 根据要查找的key 比如 f,找到f 所在的block的leaf index,也就是第二个leaf index,根据leaf index中的内容 找到f 所在的offset
HFile Index
刚才讨论的索引,在 HFile 被打开时会被载入内存,这样数据查询只要一次硬盘查询。
HBase Read Merge
相同Cell(RowKey/ColumnFamily/Column相同)并不保证在一起,删除一个Cell也只是写入一个新的Cell,它含有Delete标记,而不一定将一个Cell真正删除了,因而这就引起了一个问题,如何实现读的问题?要解决这个问题,我们先来分析一下相同的KeyValue Cell可能存在的位置:这些 cell 可能被写入了 HFile,可能是最近刚更新,还在 MemStore 中,也可能最近刚读过,缓存在 Block Cache 中。所以,当你读一行 row 时,系统怎么将对应的 cells 返回呢?一次 read 操作会将 Block Cache,MemStore 和 HFile 中的 cell 进行合并:
1. scanner 从 Block Cache 读取 cells。最近读取的 KeyValue 都被缓存在这里。
2. scanner 读取 MemStore,包含了最近写入的数据。
3. 如果 scanner 在 BlockCache 和 MemStore 都没找到对应的 cells,则 HBase 会使用 Block Cache 中的索引和布隆过滤器来加载对应的 HFile 到内存,查找到请求的 row cells。
之前讨论过,每个 MemStore 可能会有多个 HFile,所以一次 read 请求可能需要多读个文件,这可能会影响性能,这被称为读放大(read amplification)。
(点评:从时间轴上看,一个个的 HFile 也是有序的,本质上它们保存了每个 region 的每个 column family 的数据历史更新。所以对于同一个 rowkey 的同一个 cell,它可能也有多个版本的数据分布在不同的 HFile 中,所以可能需要读取多个 HFiles,这样性能开销会比较大,尤其是当不满足 data locality 时,这种 read amplification 情况会更加严重。这也是后面会讲到的 compaction 必要的原因)
HBase Minor Compaction
HBase 会自动合并一些小的 HFile,重写成少量更大的 HFiles,称为 minor compaction。它使用归并排序算法,将小文件合并成大文件,有效减少 HFile 的数量。
HBase Major Compaction
Major Compaction:一个 Column Family 的所有的 HFiles合并重写成一个的大 HFile。在这个过程中,被删除的和过期的 cell 会被真正从物理上删除,这能提高读性能。major compaction 会重写所有的 HFile,会产生大量的硬盘 I/O 和网络开销,这种情况被称为:写放大(Write Amplification)。
Major compaction 可以被设定为自动调度。因为存在写放大的问题,major compaction 一般都安排在周末和半夜。MapR Database对此做出了改进,并不需要做 compaction。同时Major compaction 还能将因为服务器 crash 或者负载均衡导致的数据迁移重新移回到离 Region Server 的地方,这样就能恢复 data locality。
Region = Contiguous Keys
我们再来回顾一下 region 的概念:
1. HBase Table 被水平切分成一个或数个 regions。每个 region 包含了连续的,有序的一段 rows,以 start key 和 end key 为边界。
2. 每个 region 的默认大小为 1GB。
3. region 里的数据由 Region Server 负责读写,和 client 交互。
4. 每个 Region Server 可以管理约 1000 个 regions(它们可能来自一张表或者多张表)。
Region Split
一开始每个 table 默认只有一个 region。当一个 region 逐渐变得很大时,它会分裂(split)成两个子 region,每个子 region 都包含了原来 region 一半的数据,这两个子 region 并行地在原来这个 region server 上创建,这个分裂动作会被报告给 HMaster。处于负载均衡的目的,HMaster 可能会将新的 region 迁移给其它 region server。
Read 负载均衡
Splitting 一开始是发生在同一台 region server 上的,但是出于负载均衡的原因,HMaster 可能会将split产生的新 regions 迁移到其他的 region server上,这会导致那些 region server 需要访问离它比较远的 HDFS 数据,直到 major compaction 的到来,它会将那些远方的数据重新移回到离 region server 节点附近的地方。
HDFS 数据备份
所有的读写都发生在 HDFS 的主 DataNode 节点上。 HDFS 会自动备份 WAL 和 HFile 的文件 blocks。HBase 依赖于 HDFS 来保证数据完整安全。当数据被写入 HDFS 时,一份会写入本地节点,另外两个备份会被写入其它节点。
WAL 和 HFiles 都会持久化到硬盘并备份。那么 HBase 是怎么恢复 MemStore 中还未被持久化到 HFile 的数据呢?下面的章节会讨论这个问题。
HBase 故障恢复
当某个 Region Server 发生 crash 时,它所管理的 region 就无法被访问了,直到 crash 被检测到,然后故障恢复完成,这些 region 才能恢复访问。Zookeeper 依靠心跳检测发现节点故障,然后 HMaster 会收到(Zookeeper发送的) region server 故障的通知。
当 HMaster 发现某个 region server 故障,HMaster 会将这个 region server 所管理的 regions 分配给其它健康的 region servers。为了恢复故障 region server 的 MemStore 中(还未被持久化到 HFile)的数据,HMaster 根据Regions将 WAL 分割成几个文件(看新的server1服务哪些regions,就取WAL中这些region的数据,生成WAL.part1),将它们保存在新的 region server 的Data node上。每个 region server replay自己的 WAL.part,来为它所分配到的新 regions 重建 MemStore。
Data Recovery
WAL 包含了一系列的修改操作,每个修改都表示一个 put 或者 delete 操作。这些修改按照时间顺序依次写入,它们被依次写入 WAL 文件的尾部来实现持久化。
当数据仍然在 MemStore 还未被持久化到 HFile 怎么办呢?WAL 文件会被replay,即:依次读取 WAL 文件中的记录,排序并添加所有的修改记录到 MemStore,最后 MemStore 会被刷写到 HFile。
HBase 优点
强一致性
- 当 write 返回时,所有的 reader 都会读到同样的值。
自动扩展性
- 数据变大时 region 会分裂。
- 使用 HDFS 存储备份数据。
内置恢复功能
- 使用WriteAheadLog(类似于文件系统中的日志)
与 Hadoop 结合
- 使用 MapReduce 处理 HBase 数据会非常直观。
HBase 缺点
业务持续可靠性:
- WAL 回放很慢。
- 故障恢复很慢。
- Major Compaction 时候 I/O 会飙升。