0. 分布式存储
0.1 分布式存储核心属性
- 分布式存储
- 多机器、横向扩展存储资源
- 相对与纵向扩展,可以分布式存储理论上无上限
- 元数据记录(文件位置索引)
- 记录文件存储位置信息
- 快速定位文件位置
- 分块存储
- 解决文件过大,单机存储不下的问题
- 方便并行计算,提高文件处理的效率
- 并行上传下载,提升文件传输的效率
- 副本机制
- 问题:部署的机器是普通机器,难免存在硬件故障,容易导致数据丢失
- 解决:副本机制、异地备份,做数据冗余,有效保证数据安全
0.2 常见的分布式存储方案
- HDFS / GFS
- Ceph
1. HDFS 概念
1.1. HDFS 工作原理
- HDFS 概念
- HDFS 是Hadoop Distribute File System 的简称,HDFS 是 Hadoop 生态的核心。
- 它允许文件通过网络在多台主机上分享的文件系统,可以让多台机器上的多个用户分享文件和存储空间。
HDFS只是分布式文件管理系统中的一种。
-
HDFS 架构
-
HDFS 遵循主/从架构,由单个 NameNode(NN)、SecondaryNameNode 和多个 DataNode(DN) 组成:
- NameNode :
- 负责维护和管理文件系统元数据
- 执行有关 文件系统命名 空间的操作,例如打开,关闭、重命名文件和目录等;
- 同时还负责 集群元数据 的存储,记录着文件中各个数据块的位置信息;
NameNode 是访问 HDFS 的唯一入口。
- NameNode 管理元数据的方式:
- 内存
- 磁盘文件
- FSimage 内存元数据镜像文件
- edits log(Journal)日志
- NameNode,其实就是 Master,它是一个管理者承担以下职责:
- 管理HDFS的名称空间
- 配置副本策略
- 管理数据块的映射信息
- 处理客户端读写请求
- NameNode 是主服务器,
- NameNode 负责管理文件系统的命名空间以及客户端对文件的访问。当客户端请求数据时,仅仅从 NameNode 中获取文件的元数据信息,具体的数据传输不经过 NameNode,而是直接与具体的 DataNode 进行交互。
- 这里文件的元数据信息,记录了文件系统中的文件名和目录名,以及它们之间的层级关系,同时也记录了每个文件目录的所有者及其权限,甚至还记录每个文件由哪些块组成,这些元数据信息记录在文件 fsimage 中,当系统初次启动时,NameNode 将读取 fsimage 中的信息并保存到内存中。
- 这些块的位置信息是由 NameNode 启动后从每个 DataNode 获取并保存在内存当中的,这样既减少了 NameNode 的启动时间,又减少了读取数据的查询时间,提高了整个系统的效率。
- NameNode 并不持久化文件块的存储位置信息,这些信息会在系统启动时从 DataNode 重建。
NameNode 所在机器通常会配置大量内存(RAM)。
- 负责维护和管理文件系统元数据
- DataNode:
- 负责具体的数据块存储;
- 提供来自文件系统客户端的读写请求,执行块的创建,删除等操作;
- DateNode,其实就是 Slave,NameNode 下达命令,DateNode 执行实际的操作:
- 存储实际的数据块
- 执行数据块的读/写操作
- DataNode 会定期发送心跳信息给 NameNode,告知 NameNode 当前节点存储的文件块信息。
- 当客户端给 NameNode 发送读写请求时,NameNode 告知客户端每个数据块所在的 DataNode 信息,然后客户端直接与 DataNode 进行通信,减少 NameNode 的系统开销。
- 当 DataNode 在执行块存储操作时,DataNode 还会与其他 DataNode 通信,复制这些块到其他 DataNode 上实现冗余。
- Secondary NameNode
-
从字面上来看,SecondaryNameNode 很容易被当作是 NameNode 的备份节点,其实不然。
- NameNode 管理着元数据信息,元数据信息会定期保存到 edits 和 fsimage 文件中。
- 其中的 edits 保存操作日志信息,在 HDFS 运行期间,新的操作日志不会立即与 fsimage 进行合并,也不会存到 NameNode 的内存中,而是会先写到 edits 中。
- 当 edits 文件达到一定域值或间隔一段时间后触发 SecondaryNameNode 进行工作,这个时间点称为 checkpoint。
- SecondaryNameNode 的角色就是定期地合并 edits 和 fsimage 文件,其合并步骤如下:
- 在进行合并之前,SecondaryNameNode 会通知 NameNode 停用当前的 editlog 文件, NameNode 会将新记录写入新的 editlog.new 文件中。
- SecondaryNameNode 从 NameNode 请求并复制 fsimage 和 edits 文件。
- SecondaryNameNode 把 fsimage 和 edits 文件合并成新的 fsimage 文件,并命名为 fsimage.ckpto
- NameNode 从 SecondaryNameNode 获取 fsimage.ckpt,并替换掉 fsimage,同时用 edits.new 文件替换旧的 edits 文件。
- 更新 checkpoint 的时间。
- 在紧急情况下,Secondary NameNode 可辅助恢复 NameNode
-
-
- HDFS 设计目标
- 故障检测 & 自动恢复
- HDFS 运行在廉价普通机器上,硬件出现故障是常态,因此 故障检测 & 自动恢复 是常态;
- 注重数据访问的 高吞吐量
- 不太重视数据访问的反应时间
- HDFS 支持大文件
- 提供很高的聚合数据带宽,一个集群支持数据百个节点,支持千万级别的文件。
- write-one-read-many
- 一次写入,多次访问;
- 文件一旦被写入,就不再被修改;
- 跨平台移植
- 故障检测 & 自动恢复
- HDFS 适用场景
- 大文件
- 数据流式访问
- 高吞吐量
- 一次写入、多次读取
- 低成本部署(部署在廉价PC上)
- 高容错
- HDFS 不适合的场景
- 大量的小文件
- 数据交互式访问
- 频繁任意修改
- 实时数据处理
- 低延迟数据访问
- 多用户写入
- HDFS 只有一个写入者,而且写操作总是在文件末尾,不支持多用户写入
1.2. 分块(Block)存储机制
- HDFS 中的文件在 物理上是分块 (Block) 存储的,可以通过配置参数 ([hdfs-site.xml文件中的]dfs.blocksize) 来设置。
- Block 块不能设置太小,也不能设置太大,HDFS块大小的设置主要取决于磁盘的传输效率:
- HDFS的块设置太小,会增加寻址时间
- HDFS 的块比磁盘的块大,目的是为了最小化寻址开销
磁盘块一般 512 Bit,HDFS 每个块 64Mb。
- HDFS 的块比磁盘的块大,目的是为了最小化寻址开销
- HDFS的块如果设置的也不能太大。
- Hadoop 中一个 map 任务一次通常只处理一个块中的数据,如果块过大,会导致整体任务数量过小,降低作业处理的速度。
- 另外,在一次上传时,如果发生异常,需要重新传输,造成网络IO资源的浪费。
- 在随机读取某部分内容时,不够灵活。
- HDFS的块设置太小,会增加寻址时间
- Hadoop 2.x 默认最小分块大小是 128M
- 原因:寻址时间大约为 10ms,寻址时间为传输时间的 1% 为最佳(即为 10/0.01=1000ms=1s ),目前磁盘的传输率普遍为 100m/s,块的大小需要是 2的n次方,故为 128M)
1.3. 文件系统命名空间(name space)
- HDFS 的文件系统命名空间的层次结构,支持目录和文件的创建、移动、删除和重命名等操作,支持配置用户和访问权限。
- HDFS 不支持硬链接和软连接。
- NameNode负责维护文件系统名称空间,记录对名称空间或其属性的任何更改。
1.4. 副本机制
- 由于 Hadoop 被设计运行在廉价的机器上,这意味着硬件是不可靠的,为了保证容错性,HDFS 提供了数据复制机制。
-
HDFS 将每一个文件存储为一系列块,每个块由多个副本来保证容错,块的大小和复制因子可以自行配置(默认情况下,块大小是 128M,默认复制因子是 3)。
1.5. 副本机制的实现原理
- 大型的 HDFS 实例,通常分布在多个机架的多台服务器上,不同机架上的两台服务器之间通过交换机进行通讯。
- 在大多数情况下,同一机架中的服务器间的网络带宽、大于不同机架中的服务器之间的带宽。
- 因此 HDFS 采用 机架感知 副本放置策略,对于常见情况,当复制因子为 3 时,HDFS 的放置策略是:
- 在写入程序位于 Datanode 上时,就优先将写入文件的一个副本放置在该 Datanode 上,否则放在随机 Datanode 上,在另一个远程机架上的任意一个节点上放置另一个副本,并在该机架上的另一个节点上放置最后一个副本。
- 此策略可以减少机架间的写入流量,从而提高写入性能。
1.6. 副本的选择
- 为了最大限度地减少带宽消耗和读取延迟,HDFS 在执行读取请求时,优先读取距离读取器最近的副本。
节点距离:两个节点到达最近的共同祖先的距离总和。
- 如果在与读取器节点相同的机架上存在副本,则优先选择该副本。
- 如果 HDFS 群集跨越多个数据中心,则优先选择本地数据中心上的副本。
2. HDFS 的稳定性
2.1. 心跳机制
- 每个 DataNode 定期向 NameNode 发送心跳消息,如果超过指定时间没有收到心跳消息,则将 DataNode 标记为死亡。
- NameNode 不会将任何新的 IO 请求转发给标记为死亡的 DataNode,也不会再使用这些DataNode上的数据。
- 由于数据不再可用,可能会导致某些块的复制因子小于其指定值,NameNode会跟踪这些块,并在必要的时候进行重试复制。
2.2. 数据的完整性
- 由于存储设备故障等原因,存储在 DataNode 上的数据块也会发生损坏。
- 为了避免读取到已经损坏的数据而导致错误,HDFS提供了数据完整性校验机制来保证数据的完整性,具体操作如下:
- 当客户端创建 HDFS 文件时,它会计算文件的每个块的校验和,并将校验和存储在同一HDFS命名空间下的单独的隐藏文件中。
- 当客户端检索文件内容时,它会验证从每个DataNode接收的数据是否与存储在关联校验和文件中的校验和匹配。
- 如果匹配失败,则证明数据已经损坏,此时客户端会选择从其他DataNode获取该块的其他可用副本。
2.3. 元数据的磁盘故障
- FsImage 和 EditLog 是 HDFS 的核心数据,这些数据的意外丢失可能会导致整个 HDFS 服务不可用。
- 为了避免这个问题,可以配置 NameNode 使其支持 FsImage 和 EditLog 多副本同步,这样 FsImage 或 EditLog 的任何改变都会引起每个副本 FsImage 和 EditLog 的同步更新。
2.6. 支持快照
- 快照支持在特定时刻存储数据副本,在数据意外损坏时,可以通过回滚操作恢复到健康的数据状态。
3. HDFS 特点
3.1 高容错
- 副本机制: 由于HDFS 采用数据的多副本方案,所以部分硬件的损坏不会导致全部数据的丢失。
3.2 高吞吐量
- HDFS设计的重点是支持高吞吐量的数据访问,而不是低延迟的数据访问。
3.3 大文件支持
- HDFS适合于大文件的存储,文档的大小应该是是GB到TB级别的。
3.3 简单一致性模型
- HDFS 更适合于一次写入多次读取(write-once-read-many)的访问模型。
- 支持将内容追加到文件末尾,但不支持数据的随机访问,不能从文件任意位置新增数据。
3.4 跨平台移植性
- HDFS具有良好的跨平台移植性,这使得其他大数据计算框架都将其作为数据持久化存储的首选方案。
4. HDFS 文件写入流程
4.1. Pipeline 管道
- 通过 pipeline 管道,客户端将数据块写入第一个数据节点,第一个节点保存后再复制到第二个节点,第二个节点保存后再复制到第三个节点。
问题:DataNode 采用 pipeline 传输,而不是用拓扑式传输的原因是什么?
答案:以管道的形式,顺序沿着一个方向传输,这样能够充分利用每台机器的带宽,避免网络瓶颈和高延迟的连接,最小化推送所有数据的延时。
4.2. ACK 应答响应
- ACK (Acknowledge character)即是确认字符,在数据通信中,接收方发给发送方的一种传输类控制字符。表示发来的数据已确认接收无误;
- 在HDFS pipeline管道传输数据的过程中,传输的反方向会进行ACK校验,确保数据传输安全。
4.3. 核心概念–默认 3 副本存储策略
- 默认副本存储策略是由 BlockPlacementPolicyDefault 指定。
- 第一块副本:优先客户端本地,否则随机
- 第二块副本:不同于第一块副本的不同机架。
- 第三块副本:第二块副本相同机架不同机器。
4.4. 文件写入流程
- 客户端在向 NameNode 发送写请求之前,先将数据写入本地的临时文件中。
- 待临时文件块达到系统设置的块大小时,开始向 NameNode 请求写文件。
- 客户端通过 Distributed FileSystem 模块向 NameNode 请求上传文件,NameNode 收到请求后会进行校验:
- 校验是否有写权限;
- 校验路径下是否有同名文件;
- NameNode 在此步骤中会检查集群中每个 DataNode 状态信息,获取空闲的节点,并在检查客户端权限后创建文件;
- NameNode 返回是否可以上传(如果校验失败,会直接报错,如果成功会给客户端返回一个信号,表示可以上传);
- 客户端请求第一个 Block 块上传到哪几个 DataNode 节点上;
- NameNode 返回 3 个 DataNode 节点,dn1、dn2、dn3;
- 客户端通过 FSDataOutputStream 模块请求 dn1 上传数据,dn1 收到请求会继续调用dn2,然后 dn2 调用 dn3,将这个通信管道建立完成;
- dn1、dn2、dn3逐级应答客户端;
- 客户端开始往 dn1 上传第一个 Block(先从磁盘读取数据放到一个本地内存缓存),以 Packet 为单位,dn1 收到一个 Packet 就会传给 dn2,dn2 传给 dn3,dn1 每传一个 Packet 会放入一个应答队列等待应答;
- 当一个 Block 传输完成之后,客户端再次请求 NameNode 上传第二个 Block 的服务器。(重复执行3-7步)。
- 最小复制块由
dfs.namenode.replication.min
指定,默认值是 1。如果 pipeline 传输失败,只要有一个副本上传成功,系统就会自动找其他机器继续复制。如果一个副本都没穿成功,则上传失败。
5. HDFS 读数据流程
- 客户端通过 Distributed FileSystem 向 NameNode 请求下载文件,NameNode 通过查询元数据信息,获取文件块所在的 DataNode 节点地址;
- 挑选一台 DataNode 服务器(就近原则),请求读取数据;
- DataNode 开始传输数据给客户端(从磁盘里读取 数据输入流,以Packet为单位来做校验);
- 客户端以 Packet 为单位接受,先在本地缓存,然后写入目标文件。