HBase MTTR 优化实践
HBase介绍
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(Write Ahead 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上线操作,对需要恢复数据进行回放。
图片来源: https://www.slideshare.net/caroljmcdonald/scaleyoursql
故障恢复常见问题
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操作和回放用于恢复。
Cluster Size | Recovered edit files count | Total No. Of regions [2k per RS] | Region Assignment (with WAL split) | Empty Region Assignment |
---|---|---|---|---|
10 Node | 4,00,000 | 20,000 | 7.5 Mins | 4 Min |
20 Node | 8,00,000 | 40,000 | 12 Mins | 6 Mins |
40 Node | 1.6 Million | 80,000 | 42 Mins | 7 Mins |
100 Nodes | 4 Million | 0.2 Million | 106 Mins | 15 Mins |
下面我们将分析测试过程中发现的瓶颈在哪里?
恢复耗时分析
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 在批量发送OPEN RPC请求到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副本,那么将会导致每个WAL Splitter打开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 hlog contains减少为hbase.regionserver.wal.max.splitters * hbase.regionserver.hlog.splitlog.writer.threads。
通过测试发现在3节点集群中,拥有15GB WAL文件和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作为首选WAL Splitter,减少远程读取。
- 将Compaction延迟后台执行,加快region上线处理。
Observer NameNode(HDFS-12943)
当HBase集群规模变大时,重启会触发大量的RPC请求,使得HDFS可能成为瓶颈,可以通过使用Observer NameNode负担读请求来降低HDFS的负载。
总结
通过上述分析,可以配置如下参数来提升HBase MTTR,尤其是在集群整体从崩溃中恢复的情况。
Process | Configuration | Recommendation | Remarks |
---|---|---|---|
HMaster | hbase.master.executor.openregion.thread | 2 * no. of cores | Opened region thread to process OPENED ZK events. Can increase based on the cores in large cluster. |
HMaster | hbase.assignment.zkevent.workers | 2 * no. of cores | ZK event worker thread to process RIT ZK notification, can be tuned based on cores |
HMaster | hbase.master.skip.log.split.task | true | Master participate in WAL split as namespace region is assigned to Hmaster. Hmaster may be overloaded during MTTR |
HMaster | hbase.balancer.tablesOnMaster | hbase:namespace | Assign namespace region to Hmaster, so that HM WAL will be split first to recover namespace region and there wont be any Hmaster abort due to Namespace init timeout Note: Later this will be replaced with Assign system tables to specified RS Group |
RegionServer | hbase.regionserver.executor.openregion.threads | 2 * no. of cores | Handlers to process Open region requests |
RegionServer | hbase.regionserver.metahandler.count | 5 * no. of cores | Meta operation handlers. During full cluster restart, all RSs are opening the region concurrently, so we need more handlers. We observed better perf upto 400 handlers. |
RegionServer | hbase.regionserver.wal.max.splitters | Same as hbase.regionserver.maxlogs | To perform WAL split concurrently and avoid overlap with assignment region cycle. If SCP is blocked for assignment which takes more time, WAL split would be delayed. |
RegionServer | hbase.regionserver.hlog.splitlog.writer.threads | 50 | Works in combination with hbase.split.writer.creation.bounded Writer thread to flush the recovered edits region wise. , can be reduced when active regions are less |
RegionServer | hbase.split.writer.creation.bounded | true | To control the number of open files in HDFS for writing recovered edits. If true, edits are cached and flushed once the buffer is full. |
RegionServer | hbase.wal.split.to.hfile | true | When the active regions are less per RS, can use this configurations to reduce IO. But if the active regions are high, this feature may not have impact. |
RegionServer | hbase.regionserver.maxlogs | 20 | Lesser the logs, lesser the time for wal split & recovering edits |
HMaster | hbase.master.persist.flushedsequenceid.enabled | true | Skip WAL edits which are already flushed into Hfile |
HMaster | hbase.rpc.timeout | 120000 | Bulk assignment RPC gets timed out due to slow processing by RS if there are many regions in RS |