19.5.3 Hadoop和HBase的实现

前面介绍了Facebook中存储架构的设计需求和它为什么采用Hadoop和HBase来实现存储架构。这部分我们将为大家介绍Facebook如何实现对Hadoop和HBase的应用及进行哪些优化。

1.实时HDFS

HDFS作为Hadoop的分布式文件存储系统,用来支持MapReduce应用程序的操作。该文件系统具有可扩展性以及较好的流数据处理功能,并且具有较强的容错能力。Facebook通过对HDFS的修改和调整,使HDFS具有支持实时操作和在线服务的特性。

(1)高可用性-AvatarNode

在HDFS中,仅有一个唯一的Master,即NameNode。在这种架构下,当NameNode停止服务后系统将处于不可用状态。对于需要7×24小时服务的软件或系统来说,肯定希望能够获得更稳定的服务,因此这样的架构可能并不是十分理想。所以Facebook根据自己的需求对原来的NameNode进行了一部分扩展,称为AvatarNode,来保证其可用性。

AvatarNode

在原生Hadoop的启动阶段,HDFS的NameNode首先从fsimage文件中读取文件系统的metadata信息。这个metadata信息存储了HDFS中每一个文件的名称、目录以及meta信息。然而,NameNode并不是永久地存储每一个块的位置。因此,当发生故障后,NameNode的重新启动将包含了两个阶段:第一阶段是读取文件系统镜像,导入事务日志,将新的文件系统镜像存储回磁盘;第二阶段是DataNode通过对块的处理向NameNode报告未知块的存储位置信息。Facebook中最大的HDFS集群存储了大约一亿五千万的文件,这两个阶段的操作将需要大约45分钟的时间。

如果在原生Hadoop的基础上采用备份节点的话,那么在发生故障时可以避免从磁盘中读取镜像文件,但是仍然需要从所有的DataNode中收集块的信息,这需要20分钟左右的时间。另一个问题是,当采用备份节点的时候,NameNode需要同步地更新备份节点所存储的数据,这样系统的可靠性(一致性)将低于单个NameNode节点的可靠性。因此,AvatarNode应运而生。

如图19-6所示,一个HDFS集群包含两个AvatarNode:主AvatarNode和备用AvatarNode(同一时间只有一个Node处于活跃状态)。主AvatarNode实际上等同于HDFS的NameNode,不同的是HDFS集群将文件系统的镜像和事务日志的备份存储在NFS中。每当主AvatarNode更新了存储在NFS文件系统中的日志之后,备用AvatarNode节点同时读取该更新,然后将更新的事务应用在自己的文件系统镜像以及日志上。备用AvatarNode节点负责生成主AvatarNode的check-point,需要定期合并事务日志并创建fsimage。因此,在该系统中将不再设置Secondary NameNode。

19.5.3 Hadoop和HBase的实现 - 图1

图 19-6 AvatarNode

在集群中,DataNode不仅仅与主AvatarNode进行通信,同时还与备用AvatarNode进行通信(发送心跳、块报告和块分配信息),这样当发生故障时,备用AvatarNode可以马上变为主动AvatarNode,之后启动的原AvatarNode将成为新的备用AvatarNode。集群中的Avatar DataNode也同时与两个AvatarNode进行通信,他们通过与ZooKeeper的整合来识别哪一个AvatarNode是当前的主AvatarNode。此外,Avatar的DataNode仅仅处理来自主AvatarNode的备份/删除等命令。

对HDFS日志文件的改进

当块文件被关闭或者被同步/写出的时候,HDFS会将块对应的ID存储到事务日志中。由于想尽量减少故障恢复的时间,那么备份AvatarNode需要知晓每一个块的位置。因此Facebook选择同时将块分配操作写入到日志中。

另外,当备份AvatarNode从事务日志中读取日志的时候(此时,主AvatarNode正在写该日志文件),那么备份AvatarNode将有可能只读取到部分的事务(非完整的,将有可能导致系统故障)。为了避免这种情况的发生,Facebook修改了事务日志的格式,使其包含了写入该事务的长度,事务的ID以及事务的校验和。

分布式Avatar文件系统(DAFS)

Facebook将修改后的HDFS命名为分布式Avatar文件系统(Distributed Avatar File System, DAFS),它是一个部署在客户端的分层文件系统,能够提供一个对HDFS的跨故障透明访问。DAFS与ZooKeeper整合在一起。ZooKeeper在某一ZNode上保存了某一集群主AvatarNode节点的物理地址,当客户端尝试与HDFS集群(例如,dfs.cluster.com)进行连接的时候,DAFS将查看ZooKeeper中保存了实际主AvatarNode物理路径的ZNode,然后将之后到来的连接重定位到该主AvatarNode上。如果由于网络环境问题使路径不可达,那么DAFS将从ZooKeeper中进行重新检索。如果在刺激前发生故障恢复事件,DAFS将一直阻塞,直到恢复完成。DAFS对于访问HDFS的应用程序来说是完全透明的,即这些应用程序不知道有DAFS的存在。

(2)Hadoop RPC兼容性问题

在Facebook中需要为消息应用程序运行不同的Hadoop集群。因此,就需要系统能够一次性地在不同的集群中部署新版的软件。这就需要Hadoop的客户端能够与运行不同版本Hadoop软件的服务器进行交互操作。Facebook对Hadoop的RPC软件进行修改使其能够自动地识别所处服务器的软件版本,然后选择合适的协议与之通信。

(3)块放置策略

默认的HDFS放置策略对块的放置位置有很少的限制,对于非本地的副本,块一般随机存放在任意机架的任意节点中。为了降低多个节点同时宕机时数据丢失的概率,Facebook设计了一个可插拔式块放置策略。它将块副本放置在较小的且可配置的一组节点中。通过实验,DAFS使数据丢失概率降低了100倍。

(4)实时作业性能提升

HDFS是一个高吞吐量的系统,然而对于响应时间却并不十分理想。例如,当应对故障时,它更倾向于“重试”或“等到”,而不是对错误进行处理。

RPC超时:Hadoop使用TCP连接来发送RPC调用。当RPC客户端侦测到RPC连接超时时,Facebook并不是马上将连接断开,而是首先向服务器发送一个ping,如果服务器仍旧有效,那么客户端将等待服务器的响应而不断开连接。因为,在这种情况下服务器很可能处于繁忙状态,断开重连要么导致失败要么给服务器增加额外的负担。

然而一直等待服务器的响应将陷入另一个极端。那么在Facebook中为RPC链接设置一个超时时间,当超时之后,客户端尝试向集群的其它DataNode发起连接。

备份文件契约(Lease)机制:另一个改进是快速撤回写者所持有的契约。HDFS仅支持单个写者对文件的写操作,NameNode通过下放契约来控制对文件的写操作。然后在某些情况下当需要对文件进行读的时候,读操作可能与对文件的写操作进行冲突。在之前,后续的操作通过向日志文件添加等待信息来触发文件的“软契约”过期,从而获得该文件的契约。文件的“软契约”相比契约较短,默认值为1分钟,该契约是为了应对这种冲突的发生。然而,这种机制依赖于管道,当发生故障时重建管道的时间过长,对系统性能影响较大。

为了克服这种问题,Facebook设计了一种轻量级的API:recoverLease,它能够显式地撤销文件的契约。当NameNode接收到recoverLease请求时,它马上撤回文件的契约,然后进行契约恢复处理。当契约恢复操作完成之后,请求方可以获得文件的契约。

读取本地副本:HDFS虽然增强了数据存储系统的可扩展性和性能,然而往往带来的是写操作和读操作的延迟。因此,Facebook在其中加入了地点侦测机制,若客户端发现数据处于本地节点中,那么它将优先从本地节点读取数据。

(5)系统新特性

HDFS同步操作:Hflush/sync对于HBase和Scribe来说同样重要,该机制首先将数据缓存在本地,然后将数据写入管道。在数据被完全接收之前,该数据在本地将一直有效,用户可以直接从本地读取到数据的信息。另外,该机制允许后续的操作不必等待操作的完成,在他们看来Hflush/sync完全是透明的。

并发读者:在Facebook中某种应用程序需要对正在写的文件执行读操作。此时,读者需要首先与NameNode进行通信来获取文件的meta信息。由于此时NameNode并没有文件的最新块信息,读者需要与文件某一副本所在DataNode进行通信来获取文件的快信息,然后再对文件执行读操作。

2.HBase的实现

上面介绍了Facebook对Hadoop的一些修改和优化,下面介绍它在实际使用HBase时进行的修改。

(1)数据库ACID特性

一些应用程序开发者总是希望新型数据库系统能够保持原有的ACID特性,Facebook同样对HBase系统进行了改进,使其尽量满足ACID特性。首先,Facebook采用类MVCC的读写一致性控制策略(Read-Write Consistency Control, RWCC)来为系统提供隔离性的保证,并且采用“先写日志”的方法来保证数据的持久性。下面将介绍Facebook是如何对系统进行改进,使其满足原子性和一致性。

首先,系统设计的第一步是要保证数据库系统行级别的原子性。RWCC在大多数情况下可以提供有效的保证。然而,当节点发生故障的时候该保障将可能失效。例如,在最初的时候,系统的日志是顺序存入HLog文件中的。如果在执行日志写操作期间,RegionServer发生故障,那么将可能只有部分日志被写入。Facebook重新设计了HLog,命名为WALEdit,它能够保证写事务要么全部执行要么全部不执行。

HDFS为数据提供了副本,因此需要采用一定的策略来保证系统的一致性。在Facebook中,它们使用管道的机制,当有新数据更新到来的时候,NameNode首先为不同的数据副本创建管道,当所有的副本更新完成之后,副本需要向NameNode发送ACK确认。在此期间,HBase将会一直等待,直到所有的操作完成。假如期间有某一个副本写操作失败,HBase将控制系统参考日志进行回滚操作。另外,Facebook还采用一定机制保证数据不被破坏,在数据读取时,HBase首先检查数据的校验和,当校验和错误,系统将自动删除该份数据,然后检查其他副本。

(2)HBase可用性改进

由于HBase中很多重要信息保存在HBase的Master中,而HBase Master只有一个,当发生故障时将有数据丢失的可能性。为了尽量避免这种情况的发生,Facebook转而将数据存储在ZooKeeper中,因为ZooKeeper采用的是一个“大多数”的策略,数据将被存储在多个节点当中。当某一个节点发生故障时,用户仍旧能从其它节点获取数据。

Facebook指出,HBase集群的停机问题往往是由节点的随机性宕机引起的,并不是由于系统的日常维护工作。因此Facebook通过对系统的改进来尽量缩短停机的时间。例如,某一节点在发起停机请求之后会间歇性地发生停机事件,这是由于较长的数据压缩周期造成的。因此Facebook将压缩设置为可中断性操作,这样能够将停机时间缩短到秒的级别。

另外一个问题是软件的更新。为了应对软件的更新,HBase需要将集群“停机”,然后再更新之后进行“重新启动”。为了处理这个问题,Facebook选择采用轮询的方式对集群节点进行更新。例如,首先对某一台机器进行更新,当这一台机器更新完成之后,系统转而对下一台进行更新,周而复始,直到全部系统更新完成。

在HBase中,当某一个RegionServer发生故障时,处于该机器的HLog需要被重新分配到集群其余有效的节点上。在之前该操作由HMaster来完成,但是由于一个节点上可能保存了大量的HLog,该操作将花费很长的时间。在Facebook中,他们采用ZooKeeper集群来负责HLog的划分,这使得该操作的时间降低了很多倍。

(3)HBase性能提升

对于HBase的写操作,Facebook通过缓存机制将对数据库的多次写减少到更少次数地写。当数据到来的时候,数据首先被写入提交日志,然后并非直接写入数据库而是首先写入到缓存系统MemStore中。当缓存系统容量到达阈值之后,缓存将数据写入HFile中,HFile是不变型HDFS文件(不被更改)。数据的更新采用的是附加的方式,即继续写HFile文件,而并非对HFile文件进行修改。当需要读取数据的时候,HBase控制系统并行读取HFile文件并抽取相关记录进行整合。当一定时间之后,HBase对HFile进行压缩、合并操作,以避免后续读操作带来的延迟。

众所周知,系统中文件数目的多少对系统的读操作以及网络IO都有很大的影响,因此在系统中定期对文件进行压缩是非常有必要的。HBase中的压缩分为两种类型:次要的和主要的。次要的压缩操作仅仅选择部分文件进行压缩。主要的压缩不但对所有的文件进行压缩,并且在必要情况下对系统执行删除、重写以及清除过期数据等操作。在这种情况下,次要压缩产生的效果并不理想,并且生成的HFile文件可能更大,这种文件不但会对块缓存系统的性能产生影响,也会对今后的进一步压缩产生影响。因此在Facebook中对压缩块的大小进行限制,从而避免大数据块的产生。此外,Facebook还对HBase原有的压缩算法进行了改写,避免额外的操作。

对于读操作来说,某一个Region中文件数目的多少对其有很大的影响,在之前的介绍中可以知道,通过对数据的压缩可以大大减少文件的数目。另外Facebook可以通过某些技术来加快文件的搜索,跳过不必要的文件。例如:Bloom Filter和时间戳策略。Bloon Filter记录的是HFile文件特定统计信息,由于Region中文件数目相当多,因此Facebook中通过使用折叠技术(folding),进一步降低Bloom Filter的大小。这样讲Bloom Filter存储到内存之中,从而加快文件的检索。另外通过对时间戳的比对,同样可以加快文件的检索速度。

3.展望

虽然Facebook修改后的Hadoop和HBase存储架构很大程度上满足了其对存储架构的设计需求,但展望未来,Facebook还提出了未来这个存储架构完善的几个方面:

1)对Hadoop和HBase应用迭代的优化;

2)对HBase二级索引和视图摘要的支持,以及对这些特性的异步维护;

3)HBase内存管理和扩充,可通过slab或者JNI进行内存管理,通过flash memory来扩展HBase cache;

4)解决HBase多数据中心replication和冲突问题。