热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Hadoop之HDFS内部机制知多少?

在前一篇Hadoop的MapReduce到底有什么问题里,我们一起回顾了MapReduce内部机制和存在的问题。在本文中,主要讨论Hadoop里另外一个重要组件HDFS的架构



在前一篇" Hadoop的MapReduce到底有什么问题 "里,我们一起回顾了MapReduce内部机制和存在的问题。在本文中,主要讨论Hadoop里另外一个重要组件HDFS的架构和高可用相关机制。感兴趣的同学也可进一步阅读 官方HDFS设计文档



HDFS设计的目的就是分布式环境下海量数据的存储。其中最重要的目标就是:



  • 系统的高可用


  • 数据一致性


  • 高并发




HDFS的架构与工作机制


HDFS的架构图如下:



HDFS主要由Namenode和DataNodes组成:



  • NameNode职责 :



* 扮演的是整个分布式存储的大脑角色。


* 存储HDFS所有的metadata信息,比如Namespace的名字,文件的replicas的个数等。


* 执行所有文件操作系统等的动作并向DataNode发相应的Block指令,比如打开、关闭、重命名、复制等操作。


* 负责Block和DataNode之间的mapping关系。


NameNode的角色类似文件系统里的 文件控制块 角色,在linux里文件控制块会记录着文件权限、所有者、修改时间和文件大小等文件属性信息,以及文件数据块硬盘地址索引。 HDFS的Block Size从2.7.3版本开始默认值从64M更改为128M。



  • DataNodes职责 :



* 响应Client的读写请求。


* 执行来自NameNode的block操作请求,比如复制,删除,新建等命令。


* 负责向NameNode汇报自己的Heartbeat和BlockReport。



HDFS的HA



  • 元数据方面



* NameNode在HDFS里的重要性不言而喻,如果NameNode挂了或者元数据丢失了,那么整个HDFS也就瘫了,因此非常需要有HA机制。


* HDFS采取的方案是: 主备双活NameNode + Zookeeper集群(Master选举) + Journal(共享存储)



  • 文件数据方面



* 数据通过 replicas 冗余来保证HA。




更详细的信息可以参考文章 HDFS的HA机制



主备NameNode + 自动主备切换


HDFS也可以通过手动切换主备,本文主要关注通过 ZK进行辅助Master选举 的方式进行主备切换。



建锁结点


当NameNode节点需要申请成为主结点时,需要通过ZK进行Master选举时,通过抢占在ZK里建立对应的锁结点。建立锁结点成功,那么说明选主成功。其中锁结点信息包括两部分:



  • 临时结点 : /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock



* 如果ZK在一定的时间内收到不到对应的NameNode的心跳,会将这个临时结点删掉。



  • 持久结点 : /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb



* 持久结点会在成为主结点是同时创建。建立持久结点的目的是为了NameNode和ZK之间通信假死带来脑裂问题。持久结点里会记录NameNode的地址。当发生脑裂时,下一个被选为主结点的NameNode会去查看是不是存在持久结点,如果存在,就会采取Fencing的措施,来防止脑裂问题。具体的Fencing方法有:


* 通过调用旧的Active NameNode的HAServiceProtocolRPC来去transition旧的NameNode为StandBy状态。


* 通过SSH方式登录到对应的NameNode机器上Kill掉对应的进程。


* 执行用户自定义的Shell脚本去隔离进程。



注册Watch监听


当NameNode申请成为主结点失败时,会向ZK注册一个监听事件,来监听对应的锁节点的目录变化,当然主要监听的是NodeDelete事件,会用来触发自动主备切换事件。



自动主备切换


NameNode的自动主备切换主要由 ZKFailoverController , HealthMontior ActiveStandbyElector 这3个组件来协同实现。



  • ZKFailoverController 启动时会创建HealthMonitor和ActiveStandbyElector两个组件,并向这两个组件注册对应的回调方法。


  • HealthMonitor 主要是用来监控NameNode的健康状态,如果检测到有状态变化,会调用回调函数来通知ZKFailoverController进行自动的主备选举。


  • ActiveStandbyElector 主要是负责和ZK交互, 里面封装了ZK相关的处理逻辑,当ZK master选举完成,会回调ZKFailoverController的相应方法来进行NameNode的主备状态切换。




具体的主备切换流程如下(可参考上面的HA图):



  • Active NameNode



① Active NameNode上的HealthMonitor发现NameNode上状态发生变化,比如没有响应。


② 通过回调ZKFailoverController函数通知。


③ ZKFailoverController收到通知后会调用ActiveStandbyElector去删除掉在ZK集群上创建的锁结点。对于正常情况下关闭的Active NameNode,也会将持久锁结点一并删除。


④ ActiveStandbyElector call ZK集群删除对应的锁结点。


⑤ 当删除结点成功后,AcitveStandbyElector会回调ZKFailoverController方法进行通知。


⑥ ZKFailoverController会去将Active NameNode的状态切换为Standby。



  • Standby NameNode



① Standby NameNode在第一次主备竞选时在ZK建立锁结点失败时会注册Watch监听。


② 当Active NameNode进行主备切换删除锁结点,NodeDelete的事件触发Standby NameNode的ActiveStandByElector的自动创建锁结点,申请成为主结点的动作。


③ 当申请主结点被ZK通过后,会回调ZKFailoverController进行NameNode的状态切换。


④ ZKFailoverController调NameNode方法将状态从Standby更新为Active。


⑤ NameNode从Journal集群里Sync最新元数据EditLog信息。


⑥ 当所有的元数据信息整体对齐后,此时的NameNode才会真正对外提供服务。



以上是正常情况下的主备切换流程。当Active NameNode整个机器宕机,或者和ZK失去通信后,根据ZK临时节点的特性,锁节点也会自动删除,自动触发主备切换。



脑裂和Fencing


Zookeeper的工程实践里会经常出现“假死”的情况,即客户端到服务端的心跳不能正常发出,通讯出现问题。这样当超时超过设置的Session Timeout参数时,Zookeeper就会认为客户端已经挂掉了,会自动关闭session,删除锁节点,从而引发分布式系统里的双主或者脑裂的情况。比如HDFS里,会触发自动的主备切换,而实际上原来的Active NameNode还是好的,这样就存在两个Active NameNode在工作。



HDFS HA里解决脑裂问题就是在ZK里建立持久结点通过Fencing机制,可以阅读


具体到主备切换机制里,当Standby结点在② 时,会发现ZK上存在永久锁结点,那就会采取Fencing机制。当成功将原来的Active NameNode隔离(Kill或者进程隔离等),才会真正去call ZKFaioverController进行状态切换。



Journal共享存储元数据



  • Active NameNode向Journal集群结点同步写EditLog元数据,具体可参考 部分。


  • 而Standby NameNode则是定时从Journal集群同步EditLog元数据到本地。


  • 在发生NameNode主备切换的时候,需要将Standby的NameNode的元数据同Journal集群结点的信息完全对齐后才可对外提供数据。




Journal本身也是分布集群来通过Paxos算法来提供分布式数据一致性的保障。只有多数据结点通过投票以后才认为真正的数据写成功。



元数据保护



  • 可以通过维护多份FSImage(落盘) + EditLog 副本来防止元数据损坏。




HDFS的数据一致性


元数据一致性



  • 主备双活NameNode之间的元数据



* 通过Journal共享存储EditLog,每次切换主备时只有对齐EditLog以后才能对外提供服务。



  • 内存与磁盘里元数据



* 内存里的数据 = 最新的FSImage + EditLog


* 当有元数据修改时,往内存写时,需要先往EditLog里记录元数据的操作记录。


* 当EditLog数据满了以后,会将EditLog应用FSImage里并和内存里的数据做同步,生成新的FSImage,清空EditLog。



数据一致性


HDFS会对写入的所有数据计算校验和(checksum),并在读取数据时验证。



  • 写入的时候会往DataNode发Checksum值,最后一个写的DataNode会负责检查所有负责写的DataNode的数据正确性。


  • 读数据的时候,客户端也会去和存储在DataNode中的校验和进行比较。




HDFS高并发


元数据的高并发修改


主要的流程图如下:




参考 博文



主要的过程 :


当有多个线程排除申请修改元数据时,会需要经过两阶段的对元数据资源申请加锁的过程。



  • 第一次申请锁成功的线程,会首先生成 全局唯一且递增 的txid来作为这次元数据的标识,将元数据修改的信息(EditLog的transaction信息)写入到当下其中一个Buffer里(没有担任刷数据到磁盘的角色的Buffer里)。然后第一次快速释放锁。


  • 此时前一步中的线程接着发起第二次加锁请求:



* 如果请求失败(比如现在正在有其他的线程正在写Buffer)会将自己休眠1s然后再发起新的加锁请求。


* 如果第二次请求加锁成功,会先check是否有线程正在进行刷磁盘的操作:


* 如果是,那么就快速释放第二次加锁然后再把自己休眠等待下次加锁请求(因为已经有人在刷磁盘了,为了不阻塞其他线程写Buffer,先释放锁信息)。


* 如果不是,那么会接着check是否自己的EditLog信息已经由在后面的其他线程刷进磁盘里:


* 如果是,那么就直接释放第二次加锁请求直接线程退出,因为不再需要它做任何事情;


* 如果还没刷进去,那么就由该线程担任起切换Buffer并刷数据到磁盘和Journal集群结点的重任。在切换Buffer以后,该线程会进行第二次释放锁的动作,这样其他线程可以继续往切换后的Buffer写数据了。在慢慢刷数据到本地磁盘或者通过网络刷数据到Journal结点的过程中,不会阻塞其他线程同时的写请求,提高并发量。



主要的方法 :



  • 分段加锁机制 + 内存双缓冲机制



* 分段加锁是指:


* 第一阶段是在写内存缓冲区的申请对修改加锁。


* 第二段是在申请刷缓冲区的数据到磁盘、Journal集群资格的时候申请加锁。


* 整个过程中只有一个锁,保护的元数据的资源。当开始刷数据时,会立刻释放锁,不会阻塞后续其他往内存缓冲区写数据的线程。


* 内存双缓存:


* 缓冲1用来当下的写入Log。


* 缓冲2用来读取已经写入的刷到磁盘和Journal结点。


* 两个缓存会交换角色(需要时机判断)



  • 缓冲数据批量刷 磁盘+网络 优化


  • 多线程并发吞吐量支持




Reference



  • Hadoop文档


  • HDFS Design文档


  • HDFS的HA机制




更多大数据相关分享,可在微信公众号搜索“ 数据元素 ”或扫描下方二维码。





推荐阅读
  • 【并发编程】全面解析 Java 内存模型,一篇文章带你彻底掌握
    本文深入解析了 Java 内存模型(JMM),从基础概念到高级特性进行全面讲解,帮助读者彻底掌握 JMM 的核心原理和应用技巧。通过详细分析内存可见性、原子性和有序性等问题,结合实际代码示例,使开发者能够更好地理解和优化多线程并发程序。 ... [详细]
  • 第二章:Kafka基础入门与核心概念解析
    本章节主要介绍了Kafka的基本概念及其核心特性。Kafka是一种分布式消息发布和订阅系统,以其卓越的性能和高吞吐量而著称。最初,Kafka被设计用于LinkedIn的活动流和运营数据处理,旨在高效地管理和传输大规模的数据流。这些数据主要包括用户活动记录、系统日志和其他实时信息。通过深入解析Kafka的设计原理和应用场景,读者将能够更好地理解其在现代大数据架构中的重要地位。 ... [详细]
  • 2019年后蚂蚁集团与拼多多面试经验详述与深度剖析
    2019年后蚂蚁集团与拼多多面试经验详述与深度剖析 ... [详细]
  • 本文详细介绍了HDFS的基础知识及其数据读写机制。首先,文章阐述了HDFS的架构,包括其核心组件及其角色和功能。特别地,对NameNode进行了深入解析,指出其主要负责在内存中存储元数据、目录结构以及文件块的映射关系,并通过持久化方案确保数据的可靠性和高可用性。此外,还探讨了DataNode的角色及其在数据存储和读取过程中的关键作用。 ... [详细]
  • Cosmos生态系统为何迅速崛起,波卡作为跨链巨头应如何应对挑战?
    Cosmos生态系统为何迅速崛起,波卡作为跨链巨头应如何应对挑战? ... [详细]
  • 揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节
    揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节 ... [详细]
  • Hadoop 2.6 主要由 HDFS 和 YARN 两大部分组成,其中 YARN 包含了运行在 ResourceManager 的 JVM 中的组件以及在 NodeManager 中运行的部分。本文深入探讨了 Hadoop 2.6 日志文件的解析方法,并详细介绍了 MapReduce 日志管理的最佳实践,旨在帮助用户更好地理解和优化日志处理流程,提高系统运维效率。 ... [详细]
  • 字节跳动深圳研发中心安全业务团队正在火热招募人才! ... [详细]
  • 构建高可用性Spark分布式集群:大数据环境下的最佳实践
    在构建高可用性的Spark分布式集群过程中,确保所有节点之间的无密码登录是至关重要的一步。通过在每个节点上生成SSH密钥对(使用 `ssh-keygen -t rsa` 命令并保持默认设置),可以实现这一目标。此外,还需将生成的公钥分发到所有节点的 `~/.ssh/authorized_keys` 文件中,以确保节点间的无缝通信。为了进一步提升集群的稳定性和性能,建议采用负载均衡和故障恢复机制,并定期进行系统监控和维护。 ... [详细]
  • 技术日志:深入探讨Spark Streaming与Spark SQL的融合应用
    技术日志:深入探讨Spark Streaming与Spark SQL的融合应用 ... [详细]
  • 在Linux系统中,原本已安装了多个版本的Python 2,并且还安装了Anaconda,其中包含了Python 3。本文详细介绍了如何通过配置环境变量,使系统默认使用指定版本的Python,以便在不同版本之间轻松切换。此外,文章还提供了具体的实践步骤和注意事项,帮助用户高效地管理和使用不同版本的Python环境。 ... [详细]
  • 如何正确配置与使用日志组件:Log4j、SLF4J及Logback的连接与整合方法
    在当前的软件开发实践中,无论是开源项目还是日常工作中,日志框架都是不可或缺的工具之一。本文详细探讨了如何正确配置与使用Log4j、SLF4J及Logback这三个流行的日志组件,并深入解析了它们之间的连接与整合方法,旨在帮助开发者高效地管理和优化日志记录流程。 ... [详细]
  • 开发心得:利用 Redis 构建分布式系统的轻量级协调机制
    开发心得:利用 Redis 构建分布式系统的轻量级协调机制 ... [详细]
  • NoSQL数据库,即非关系型数据库,有时也被称作Not Only SQL,是一种区别于传统关系型数据库的管理系统。这类数据库设计用于处理大规模、高并发的数据存储与查询需求,特别适用于需要快速读写大量非结构化或半结构化数据的应用场景。NoSQL数据库通过牺牲部分一致性来换取更高的可扩展性和性能,支持分布式部署,能够有效应对互联网时代的海量数据挑战。 ... [详细]
  • 在Python 3环境中,当无法连接互联网时,可以通过下载离线模块包来实现模块的安装。具体步骤包括:首先从PyPI网站下载所需的模块包,然后将其传输到目标环境,并使用`pip install`命令进行本地安装。此方法不仅适用于单个模块,还支持依赖项的批量安装,确保开发环境的完整性和一致性。 ... [详细]
author-avatar
我的爱来了2012_800
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有