Apache HBase MTTR 优化实践:减少恢复时长
摘要:HBase 是 Hadoop Database 的简称,是建立在 Hadoop 文件系统之上的分布式面向列的数据库,它具有高可靠、高性能、面向列和可伸缩的特性,提供快速随机访问海量数据能力。
本文分享自华为云社区《Apache HBase MTTR优化实践》,作者: pippo。
HBase 是 Hadoop Database 的简称,是建立在 Hadoop 文件系统之上的分布式面向列的数据库,它具有高可靠、高性能、面向列和可伸缩的特性,提供快速随机访问海量数据能力。
HBase 采用 Master/Slave 架构,由 HMaster 节点、RegionServer 节点、ZooKeeper 集群组成,底层数据存储在 HDFS 上。
整体架构如图所示:
HMaster 主要负责:
在 HA 模式下,包含主用 Master 和备用 Master。
主用 Master:负责 HBase 中 RegionServer 的管理,包括表的增删改查;RegionServer 的负载均衡,Region 分布调整;Region 分裂以及分裂后的 Region 分配;RegionServer 失效后的 Region 迁移等。
备用 Master:当主用 Master 故障时,备用 Master 将取代主用 Master 对外提供服务。故障恢复后,原主用 Master 降为备用。
RegionServer 主要负责:
存放和管理本地 HRegion。
RegionServer 负责提供表数据读写等服务,是 HBase 的数据处理和计算单元,直接与 Client 交互。
RegionServer 一般与 HDFS 集群的 DataNode 部署在一起,实现数据的存储功能。读写 HDFS,管理 Table 中的数据。
ZooKeeper 集群主要负责:
存放整个 HBase 集群的元数据以及集群的状态信息。
实现 HMaster 主从节点的 Failover。
HDFS 集群主要负责:
HDFS 为 HBase 提供高可靠的文件存储服务,HBase 的数据全部存储在 HDFS 中。
结构说明:
Store
一个 Region 由一个或多个 Store 组成,每个 Store 对应图中的一个 Column Family。
MemStore
一个 Store 包含一个 MemStore,MemStore 缓存客户端向 Region 插入的数据,当 RegionServer 中的 MemStore 大小达到配置的容量上限时,RegionServer 会将 MemStore 中的数据“flush”到 HDFS 中。
StoreFile
MemStore 的数据 flush 到 HDFS 后成为 StoreFile,随着数据的插入,一个 Store 会产生多个 StoreFile,当 StoreFile 的个数达到配置的阈值时,RegionServer 会将多个 StoreFile 合并为一个大的 StoreFile。
HFile
HFile 定义了 StoreFile 在文件系统中的存储格式,它是当前 HBase 系统中 StoreFile 的具体实现。
HLog(WAL)
HLog 日志保证了当 RegionServer 故障的情况下用户写入的数据不丢失,RegionServer 的多个 Region 共享一个相同的 HLog。
HBase 提供两种 API 来写入数据。
Put:数据直接发送给 RegionServer。
BulkLoad:直接将 HFile 加载到表存储路径。
HBase 为了保证数据可靠性,使用 WAL(WriteAhead Log)来保证数据可靠性。它是 HDFS 上的一个文件,记录 HBase 中数据的所有更改。所有的写操作都会先保证将数据写入这个文件后,才会真正更新 MemStore,最后写入 HFile 中。如果写 WAL 文件失败,则操作会失败。在正常情况下,不需要读取 WAL 文件,因为数据会从 MemStore 中持久化为 HFile 文件。但是如果 RegionServer 在持久化 MemStore 之前崩溃或者不可用,系统仍然可以从 WAL 文件中读取数据,回放所有操作,从而保证数据不丢失。
写入流程如图所示:
默认情况下 RegionServer 上管理的所有 HRegion 共享同一个 WAL 文件。WAL 文件中每个记录都包括相关 Region 的信息。当打开 Region 时,需要回放 WAL 文件中属于该 Region 的记录信息。因此,WAL 文件中的记录信息必须按 Region 进行分组,以便可以回放特定 Region 的记录。按 Region 分组 WAL 的过程称为 WAL Split。
WAL Split 由 HMaster 在集群启动时完成或者在 RegionServer 关闭时由 ServershutdownHandler 完成。在给定的 Region 再次可用之前,需要恢复和回放所有的 WAL 文件。因此在数据恢复之前,对应的 Region 无法对外服务。
HBase 启动时,Region 分配简要分配流程如下:
HMaster 启动时初始化 AssignmentManager。
AssignmentManager 通过 hbase:meta 表查看当前 Region 分配信息。
如果 Region 分配依然有效(Region 所在 RegionServer 依然在线),则保留分配信息。
如果 Region 分配无效,调用 LoadBalancer 来进行重分配。
分配完成后更新 hbase:meta 表。
本文主要关注集群重新启动和恢复相关内容,着重描述相关优化,减少 HBase 恢复时长。
RegionServer 故障恢复流程
当 HMaster 检测到故障时,会触发 SCP(Server Crash Procedure)流程。SCP 流程包括以下主要步骤:
HMaster 创建 WAL Split 任务,用于对属于崩溃 RegionServer 上 Region 进行记录分组。
将原属于崩溃 RegionServer 上 Region 进行重分配,分配给正常 RegionServer。
正常 RegionServer 执行 Region 上线操作,对需要恢复数据进行回放。
故障恢复常见问题
HMaster 等待 Namespace 表超时终止
当集群进行重启时,HMaster 进行初始化会找到所有的异常 RegionServer(Dead RegionServer)并开始 SCP 流程,并继续初始化 Namespace 表。
如果 SCP 列表中存在大量的 RegionServer,那么 Namespace 表的分配将可能被延迟并超过配置的超时时间(默认 5 分钟),而这种情况在大集群场景下是最常见的。为临时解决该问题,常常将默认值改大,但是必不能保证一定会成功。
另外一种方式是在 HMaster 上启用表来避免此问题(hbase.balancer.tablesOnMaster=hbase:namespace),HMaster 会优先将这些表进行分配。但是如果配置了其它表也可以分配到 HMaster 或者由于 HMaster 性能问题,这将无法做到 100%解决此问题。此外在 HBase 2.X 版本中也不推荐使用 HMaster 来启用表。解决这个问题的最佳方法是支持优先表和优先节点,当 HMaster 触发 SCP 流程时,优先将这些表分配到优先节点上,确保分配的优先级,从而完全消除此问题。
批量分配时 RPC 超时
HBase 专门线性可扩展性而设计。如果集群中的数据随着表增加而增多,集群可以很容易扩展添加 RegionServer 来管理表和数据。例如:如果一个集群从 10 个 RegionServer 扩展到 20 个 RegionServer,它在存储和处理能力方面将会增加。
随着 RegionServer 上 Region 数量的增加,批量分配 RPC 调用将会出现超时(默认 60 秒)。这将导致重新进行分配并最终对分配上线时间产生严重影响。
在 10 个 RegionServer 节点和 20 个 RegionServer 节点的测试中,RPC 调用分别花费了约 60 秒和 116 秒。对于更大的集群来说,批量分配无法一次成功。主要原因在于对 ZooKeeper 进行大量的读写操作和 RPC 调用,用来创建 OFFLINE ZNode 节点,创建正在恢复的 Region ZNode 节点信息等。
恢复可扩展性测试
在 10 到 100 个节点的集群测试中,我们观察到恢复时间随着集群规模的增大而线性增加。这意味着集群越大,恢复所需的时间就越多。特别是当要恢复 WAL 文件时,恢复时间将会非常大。在 100 个节点的集群中,通过 Put 请求写入数据的情况下,恢复需要进行 WAL Split 操作,发现需要 100 分钟才能从集群崩溃中完全恢复。而在相同规模的集群中,如果不写入任何数据大约需要 15 分钟。这意味着 85%以上的时间用于 WAL Split 操作和回放用于恢复。
下面我们将分析测试过程中发现的瓶颈在哪里?
恢复耗时分析
HDFS 负载
在 10 个节点的 HBase 集群上,通过 JMX 来获取 HDFS 的 RPC 请求监控信息,发现在启动阶段有 1200 万读取 RPC 调用。
其中 GetBlockLocationNumOps:380 万、GetListingNumOps:13 万、GetFileInfoNumOps:840 万。
当集群规模达到 100 个时,RPC 调用和文件操作将会非常大,从而对 HDFS 负载造成很大压力,成为瓶颈。可能由于以下原因导致 HDFS 写入失败、WAL Split 和 Region 上线缓慢超时重试。
巨大的预留磁盘空间。
并发访问达到 DataNode 的 xceiver 的限制。
HMaster 负载
HMaster 使用基于 ZooKeeper 的分配机制时,在 Region 上线过程中 HMaster 会创建一个 OFFLINE ZNode 节点,RegionServer 会将该 ZNode 更新为 OPENING 和 OPENED 状态。对于每个状态变化,HMaster 都会进行监听并处理。
对于 100 个节点的 HBase 集群,大概将会有 6,000,000 个 ZNode 创建和更新操作和 4,000,000 个监听事件要进行处理。
ZooKeeper 的监听事件通知处理是顺序的,旨在保证事件的顺序。这种设计在 Region 锁获取阶段将会导致延迟。在 10 个节点的集群中发现等待时间为 64 秒,而 20 节点的集群中等待时间为 111 秒。
GeneralBulkAssigner 在批量发送 OPENRPC 请求到 RegionServer 之前会获取相关 Region 的锁,再收到 RegionServer 的 OPEN RPC 请求响应时才会释放该锁。如果 RegionServer 再处理批量 OPEN RPC 请求时需要时间,那么在收到确认响应之前 GeneralBulkAssigner 将不会释放锁,其实部分 Region 已经上线,也不会单独处理这些 Region。
HMaster 按照顺序创建 OFFLINE ZNode 节点。观察发现在执行批量分配 Region 到 RegionServer 之前将会有 35 秒的延迟来创建 ZNode。
采用不依赖 ZooKeeper 的分配机制将会减少 ZooKeeper 的操作,可以有 50%左右的优化。HMaster 依然会协调和处理 Region 的分配。
提升 WAL Split 性能
持久化 FlushedSequenceId 来加速集群重启 WAL Split 性能(HBASE-20727)
ServerManager 有每个 Region 的 flushedSequenceId 信息,这些信息被保存在一个 Map 结构中。我们可以利用这些信息来过滤不需要进行回放的记录。但是这个 Map 结构并没有被持久化,当集群重启或者 HMaster 重启后,每个 Region 的 flushedSequenceId 信息将会丢失。
如果这些信息被持久化那么即使 HMaster 重启,这些依然存在可用于过滤 WAL 记录,加快恢复记录和回放。‘hbase.master.persist.flushedsequenceid.enabled’可用于配置是否开启此功能。flushedSequenceId 信息将会定时持久化到如下目录<habse root dir>/.lastflushedseqids。可以通过参数’hbase.master.flushedsequenceid.flusher.interval’ 来配置持久化间隔,默认为 3 小时。
注意:此特性在 HBase 1.X 版本不可用。
改善 WAL Split 在故障切换时稳定性(HBASE-19358)
在 WAL 记录恢复期间,WAL Split 任务将会将 RegionServer 上的所有待恢复记录输出文件打开。当 RegionServer 上管理的 Region 数量较多时将会影响 HDFS,需要大量的磁盘保留空间但是磁盘写入非常小。
当集群中所有 RegionServer 节点都重启进行恢复时,情况将变得非常糟糕。如果一个 RegionServer 上有 2000 个 Region,每个 HDFS 文件为 3 副本,那么将会导致每个 WALSplitter 打开 6000 个文件。
通过启用 hbase.split.writer.creation.bounded 可以限制每个 WAL Splitter 打开的文件。当设置为 true 时,不会打开任何 recovered.edits 的写入直到在内存积累的记录已经达到 hbase.regionserver.hlog.splitlog.buffersize(默认 128M),然后一次性写入并关闭文件,而不是一直处于打开状态。这样会减少打开文件流数量,从 hbase.regionserver.wal.max.splitters * the number of region the hlogcontains 减少为 hbase.regionserver.wal.max.splitters *hbase.regionserver.hlog.splitlog.writer.threads。
通过测试发现在 3 节点集群中,拥有 15GBWAL 文件和 20K Region 的情况下,集群整体重启时间从 23 分钟缩短为 11 分钟,减少 50%。
hbase.regionserver.wal.max.splitters = 5
hbase.regionserver.hlog.splitlog.writer.threads= 50
WAL Split 为 HFile(HBASE-23286)
WAL 恢复时使用 HFile 文件替换 Edits 文件这样可以避免在 Region 上线过程中写入。Region 上线过程中需要完成 HFile 文件校验、执行 bulkload 加载并触发 Compaction 来合并小文件。此优化可以避免读取 Edits 文件和持久化内存带来的 IO 开销。当集群中的 Region 数量较少时(例如 50 个 Region)观察发现性能有显著提升。
当集群中有更多的 Region 时,测试发现由于大量的 HFile 写入和合并将会导致 CPU 和 IO 的增加。可以通过如下额外的措施来减少 IO。
将故障 RegionServer 作为首选 WALSplitter,减少远程读取。
将 Compaction 延迟后台执行,加快 region 上线处理。
Observer NameNode(HDFS-12943)
当 HBase 集群规模变大时,重启会触发大量的 RPC 请求,使得 HDFS 可能成为瓶颈,可以通过使用 Observer NameNode 负担读请求来降低 HDFS 的负载。
总结
通过上述分析,可以配置如下参数来提升 HBase MTTR,尤其是在集群整体从崩溃中恢复的情况。
参考
HBase ZK-less Region Assignment : Apache HBase
Apache HBase ™ Reference Guide
NoSQL HBase schema design and SQL with Apache Drill(slideshare.net)
MapReduce 服务 MRS_华为云 (huaweicloud.com)
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/dd30d872f9dabe6496c6bdfd7】。文章转载请联系作者。
评论