目录
1. HDFS元数据存储
2. HDFS HA 高可用
1. HDFS元数据存储
HDFS中的元数据按类型可以分为:
- 文件系统的元数据:包括文件名、目录名、修改信息、block的信息、副本信息等。
- datanodes的状态信息:比如节点状态、使用率等。
HDFS中的元数据按存储位置可以分为内存中元数据和磁盘上的元数据
磁盘上的元件数据包括fsimage镜像文件和edits log编辑日志,因为在磁盘上可以保证持久化存储。
fsimage镜像文件:是元数据的一个持久化的检查点,包含Hadoop文件系统中的所有目录和文件元数据信息,但不包含文件块位置的信息。文件块位置信息只存储在内存中,是在 datanode加入集群的时候,namenode询问datanode得到的,并且间断的更新。
edits 编辑日志:用来记录文件系统中发生的所有更改操作(如创建、删除、修改文件等)的日志。当客户端对文件系统执行变更操作时,这些操作会被优先记录到 edits 文件中。
当NN启动后,首先将fsimage镜像文件加载到内存中,再根据edits 编辑日志里的内容更新内存元数据,使得内存元数据和实际的同步,因此内存中的元数据是最完整最新的元数据,用户可以对其进行读操作,接下来HDFS中的更新操作会重新写到edits 编辑日志里,因为fsimage镜像文件比较大,直接在fsimage镜像文件中更新太慢。
这样一来就不必担心内存中的元数据丢失,磁盘里的fsimage镜像文件和edits log编辑日志能够确保元数据安全。
SNN
SNN,也就是HDFS中的一个角色secondarynamenode,它的职责是合并NameNode的edit logs到fsimage文件中。因为长时间不合并,edit logs会越来越多,fsimage镜像文件会越来越旧,不太好。secondary namenode将namenode上积累的所有edits和一个最新的fsimage下载到本地,并加载到内存进行merge的过程称为checkpoint。
checkpoint流程:
- 当产生新的操作时,首先写到磁盘里的edits日志里,然后再更新到nn的内存。
- 当达到dfs.namenode.checkpoint.period和dfs.namenode.checkpoint.txns 两个配置中的任意一个secondarynamenode就会执行checkpoint的操作。两个参数可以在core-site.xml配置,第一个是两次连续的checkpoint之间的时间间隔。默认1小时,第二个是最大的没有执行checkpoint事务的数量,满足将强制执行紧急checkpoint,即使尚未达到检查点周期。默认设置为100万。
- 触发checkpoint后,NN生成一个新的edits文件,因为旧的不能再写了,越写越大,需要及时合并更新fsimage镜像文件,接下来的操作写道新的edits文件里,同时SNN把NN的edits文件和fsimage通过HTTP GET方式复制到本地。
- 然后SNN把fsimage加载到SNN的内存里,然后一条一条地执行edits文件中的各项更新操作,更新fsimage,更新完后生成一个最新的fsimage文件。SNN把这个最新的fsimage文件复制到NN磁盘里。这样一来新的fsimage文件和edits文件就把旧的替换掉了,保证了edits不太大,fsimage不太旧。然后这样一直循环。
注意NN内存里的元数据始终是最新的,因为要及时的使用,checkpoint的作用只是为了容错和持久化。
2. HDFS HA 高可用
单点故障(single point of failure,SPOF):
是指系统中某一点一旦失效,就会让整个系统无法运行成熟的做法就是给单点故障设置备份,形成主备架构。通俗描述就是当主挂掉,备份顶上,短暂的中断之后继续提供服务。常见的是一主一备架构,当然也可以一主多备。备份越多,容错能力越强,与此同时,冗余也越大,浪费资源。
QJM方案:
使用zookeeper中ZKFC来实现主备切换;使用Journal Node(JN)集群实现edits log的共享以达到数据同步的目的
在HDFS高可用中,没有SNN了,多了一个备用NN节点standby NN,基本原理是通过使用 2N+1 台 JournalNode 来存储 EditLog,每次写操作需要至少 N+1 个 JournalNode 返回成功时,才认为该次写入操作成功,确保数据不会丢失。这个算法能够容忍最多 N 台机器故障,若出现超过 N 台机器故障,则算法将失效。该原理基于 Paxos 算法。
如何保证备用NN元数据与active元数据同步?
由于在高可用(HA)架构中,SecondaryNameNode 已不再存在。为了保证 Standby NameNode 与 Active NameNode 之间的元数据始终保持一致,它们通过 JournalNode 进行数据同步。当 Active NameNode 执行任何修改操作时,JournalNode 会将修改日志同时记录到至少半数以上的 JournalNode 中。此时,Standby NameNode 会监测到 JournalNode 中的日志变化,并读取相应的修改日志,随后将这些修改同步到自己的目录镜像树中。
Active NameNode 挂掉如何接管?
当发生故障时,若 Active NameNode 挂掉,Standby NameNode 在接管成为新的 Active NameNode 前,先读取所有 JournalNode 中的修改日志。这一过程确保了 Standby NameNode 的目录镜像树与故障的 Active NameNode 保持一致,从而能够高效且可靠地接管原 Active NameNode 的工作,继续处理来自客户端的请求,确保整个系统的高可用性。
如果新的NN上任后原有挂掉的NN复活了怎么办?
在 HA 模式下,DataNode 必须确保在任何时刻只有一个 NameNode 能对其发出指令。为此,每当 NameNode 改变状态时,它会向 DataNode 发送当前状态及一个序列号。DataNode 在运行期间会持续跟踪这个序列号。当原NameNode挂了时,新的 Active NameNode 会在向 DataNode 发送心跳信息时,附带自己的 Active 状态和一个更高的序列号。收到这个心跳的 DataNode 会认为该 NameNode 是新的 Active,并开始执行其命令。如果原 Active NameNode 恢复并发送心跳时,携带的是原先的序列号和 Active 状态,DataNode 会拒绝该心跳信息,并继续与新的 Active NameNode 保持同步。
如何监视NN的健康?
HA下,在每个NameNode的节点上有一个FailoverController作为一个单独的进程用来监视NN的健康状态。FailoverController主要包括三个组件:
HealthMonitor: 监控NameNode是否处于unavailable或unhealthy状态。当前通过RPC调用NN相应的方法完成。
ActiveStandbyElector: 监控NN在ZK中的状态。
ZKFailoverController(ZKFC): 订阅HealthMonitor 和ActiveStandbyElector 的事件,并管理NN的状态,另外zkfc还负责解决脑裂问题。
上述三个组件都在跑在一个JVM中,这个JVM与NN的JVM在同一个机器上。但是两个独立的进程。一个典型的HA集群,有两个NN组成,每个NN都有自己的ZKFC进程。
ZKFC:
- 首先集群启动,如果本地NameNode运行状况良好,并且ZKFC看到当前没有其他节点持有锁znode,它将自己尝试获取该锁。如果成功,则表明它“赢得了选举”,并负责运行故障转移以使其本地NameNode处于Active状态。如果已经有其他节点持有锁,zkfc选举失败,则会对该节点注册监听,等待下次继续选举。
- ZKFC周期性的向它监控的NN发送健康探测命令,从而来确定NameNode是否处于健康状态,如果机器宕机,心跳失败,那么zkfc就会标记它处于一个不健康的状态。同时如果这个不健康的是active的NN,zkfc将其持有的锁znode删除,然后备用的NN将会得到这把锁,升级为主NN,同时标记状态为Active。
- 当宕机的NN新启动时,它会再次注册zookeper,发现已经有znode锁了,便会自动变为Standby状态,如此往复循环,保证高可靠,需要注意,目前仅仅支持最多配置2个NN。
脑裂:
在HA集群中,脑裂指的是当两个节点断开联系时,分裂成为两个独立的节点。由于相互失去了联系,主备节点之间像"裂脑人"一样,使得整个集群处于混乱状态。
脑裂后果:
- 都认为对方是状态好的,自己是备份角色,后果是无服务;
- 都认为对方是故障的,自己是主角色。相互争抢共享资源,结果会导致系统混乱,数据损坏。客户端也不知道找谁了。
因此保持任意时刻系统有且只有一个主角色提供服务我避免脑裂的核心。
故障转移过程也就是俗称的主备角色切换的过程,切换过程中最怕的就是脑裂的发生。因此需要Fencing机制来避免,将先前的Active节点隔离,然后再将Standby转换为Active状态。
Hadoop公共库中对外提供了两种Fencing实现,分别是sshfence和shellfence(缺省实现)。
sshfence是指通过ssh登陆目标节点上,使用命令fuser将进程杀死(通过tcp端口号定位进程pid,该方法比jps命令更准确);shellfence是指执行一个用户事先定义的shell命令(脚本)完成隔离。