【Java劝退师】Hadoop 知识脑图 - 大数据分布式存储和计算平台

784 阅读10分钟

Hadoop

Hadoop

大数据分布式存储和计算平台

一、大数据 特点

无法在一定时间范围内,使用常规软件工具进行 捕捉、管理、处理 的数据集合

  • Volume 大量 : 采集、存储、计算 的数据量大
  • Velocity 高速 : 要求尽可能实时完成处理
  • Variety 多样 : 数据 格式、来源 多样化
  • Veracity 真实 : 需确保数据的真实性,才能保证数据分析的正确性
  • Value 低价值 : 数据价值密度低

二、技术框架

  • 狭义的 Hadoop

    • HDFS : 分布式文档 系统

    • MapReduce : 分布式脱机计算 框架

    • Yarn : 作业调度 与 集群计算资源管理 框架

  • 广义的 Hadoop

    • 狭义的 Hadoop
    • Flume : 日志数据采集 框架
    • Flink : 流式数据 引擎
    • Sqoop : 关系型数据库采集 框架
    • Hive : 针对 HDFS 的计算 框架
    • HBase : 基于 HDFS 的列式非关系型数据库

三、版本

  • Apache Hadoop
    • 优点
      • 开源,享有全世界贡献
      • 版本更新快
      • 学习方便
    • 缺点
      • 版本维护、兼容性问题 处理困难
  • Cloudera (CDH)
    • 收费
    • 【建议】生产环境使用
  • Hortonworks (HDP)
    • 免费、开源
    • 可在生产环境使用

四、Hadoop 优 / 缺点

  • 优点

    • 高可靠 - 存储、处理 数据 高可靠

      ○ 可靠性 : 服务连续无故障运行的时间

      ○ 可用性 : 服务可用时间 / 总时间 的百分比

      ○ 一个服务,可靠性很高,可以稳定运行10年,但是一旦服务中断,要用一年的时间来恢复,那么它的可用性只有90%

      ○ 一个服务,可靠性很差,运行10秒就会当机,但是恢复服务只需要1ms, 那么它的可用性是99.99%

    • 高扩展 - 集群轻松 添加、删除 节点

    • 高效性 - 可在节点间动态地移动数据,保证各节点负载均衡,处理速度提升

    • 高容错 - 自动保存多个副本,任务失败自动重新分配

  • 缺点

    • 不适合低延迟数据访问
    • 不能高效存储小文档
    • 不支持任意修改文档

五、HDFS

○ Hadoop Distribute File System

○ 高可靠、高吞吐量的分布式文档系统

○ 负责 数据存储

1. 角色

  • NameNode (NN)

    • 存储文档元数据
      • 文档名
      • 文档属性
      • 各 文档块数据(Block) 所在的 DataNode
    • 维护 HDFS 命名空间(Namespace)
    • 维护副本策略
    • 处理客户端读写请求
    • 文档
      • Fsimage
        • 元数据镜像,通常称为检查点
        • 记录所有 目录、文档 信息(Block数量、副本数量、权限)
      • Edits
        • Client 对 HDFS 所有的更新纪录
      • seen_txid
        • 内容只保存最后一个 Edits 文档名称的数字
      • VERSION
        • 纪录 NameNode 的版本号信息
    • 故障处理
      • 将 2NN 的元数据拷贝到 NN 节点下 - 存在数据丢失风险
      • 使用 Zookeeper 搭建 HDFS 高可用集群 - 一个 Active 的 NameNode,一个 Standby 的 NameNode
  • SecondaryNameNode (2NN)

    • 辅助 NameNode

      • 监控 HDFS 状态
      • 定时获取 HDFS 元数据快照
    • 元数据管理流程

      • 第一阶段 : NameNode 启动

        1. 如 NN 是第一次启动,则将 NN 格式化后创建 Fsimage(镜像文档)、Edits(更新日志);否则直接加载 Fsimage、Edits 到内存
        2. Client 对元数据进行 增、删、改
        3. NN 在 Edit 增加更新纪录
        4. NN 在内存对数据进行 增、删、改
      • 第二阶段 : Secondary NameNode 工作

        CheckPoint 时机 : 1. 定时(默认 1 小时) 2. 操作次数(默认 100 万次)

        1. 2NN 向 NN 询问是否需要 CheckPoint,NN 直接返回结果

        2. 2NN 向 NN 请求运行 CheckPoint

        3. NN 滚动正在运行的 Edits - 产生 002 Edit 文档,并保存 001 Edit 文档

        4. NN 将 Fsimage、Edits 拷贝到 2NN

        5. 2NN 将 Fsimage、Edits 加载到内存合并

        6. 2NN 生成新的镜像文档 fsimage.chkpoint

        7. 2NN 将 fsimage.chkpoint 拷贝到 NN

        8. NN 将 fsimage.chkpoint 重命名为 fsimage

  • DataNode (DN)

    • 存储文档 Block

    • Block 校验

    • Block 读写

    • 定期向 NameNode 汇报自己持有的 Block 信息

  • Client

    • 文件上传时,负责将文档切分成 Block

    • 与 NameNode 交互,获取文档位置信息

    • 与 DataNode 交互,读写文档

    • 管理或访问 HDFS

    • 文件写入流程

      1. Client 透过 Distributed FileSystem 向 NameNode 请求上传文档

      2. NameNode 检查 目标文档是否已存在、父目录是否存在,响应是否可以上传

      3. Client 向 NameNode 询问 第一个 Block 应该上传到哪几个 DataNode

      4. NameNode 返回 3 个 DataNode 节点,dn1、dn2、dn3

      5. Client 透过 FSDataOutputStream 向 dn1 请求上传文档;dn1 调用 dn2;dn2 调用 dn3;将通信管道创建完成

      6. dn3、dn2、dn1 逐级应答 Client

      7. Client 向 dn1 上传 第一个 Block,以 Packet 为单位,dn1 收到一个 Packet 就会传给 dn2,dn2 传给 dn3;dn1 每传一个 Packet 就会放入一个确认队列等待确认

      8. 当一个 Block 上传完成,Client 会再次请求 NameNode 上传 第二个 Block

    • 文件读取流程

      1. Client 向 NameNode 请求下载文档

      2. NameNode 返回 文档元数据、文档 Block 所在的 DataNode 地址

      3. Client 挑选一台 DataNode (先以就近原则,访问失败则随机),请求读取数据

      4. DataNode 返回数据给 Client

        Client 以 Packet 为单位接收数据,先在本地缓存,再写入目标文档

2. 概念

  • 透过统一的命名空间目录树来定位文档
  • 文档在物理上分块(Block)存储
    • 【默认】Block 大小为 128M
    • 【默认】副本数量 3 个
  • 适合 一次写入,多次读出 的场景
  • 不支持文档的随机修改 - 支持追加写入,不支持随机更新
  • 不适合做为网盘原因
    • 不方便修改
    • 延迟高
    • 网络开销大
  • 安全模式
    • 只接受读请求,不接受删除、修改请求
    • 【默认】集群刚启动的 30 秒为安全模式
  • 归档技术
    • 解决大量小文档问题,避免 NN 内存资源浪费
    • 使用 HAR 归档工具对文档进行压缩

六、MapReduce

○ 分布式脱机计算框架

○ 负责 制定计算逻辑

○ 核心思想 : 分而治之

1. 阶段

  1. Map : 分,并行处理输入数据,彼此间没有依赖关系 - Mapper类
  2. Reduce : 合,对 Map 阶段结果汇总 - Reducer类

将计算任务分成多个小任务,再将小任务结果合并

2. 类

  • Mapper 类

    • 一行文本数据调用 map() 方法一次

    • map() 方法尽量不做聚合运算

  • Reducer 类

    • Mapper 输出数据的 Key 相同 → 同一个分区 → 运行同一个 reduce(key, values) 方法

    • Reduce Task 的数量决定了结果文档的数量 - 文档内数据有序

    • 使用自定义分区器,可以将不同类型的数据交给不同的 Reduce Task

      • 分区数量 与 Reduce Task 数量最好保持一致

      • 分区数量 > 1,Reduce Task 数量 = 1 - 1 个结果文档,可用做全排序

      • 分区数量 > Reduce Task 数量 - 输出多个空文档

      • 分区数量 < Reduce Task 数量 - 报错

  • Driver 类

    • 创建要提交给 Yarn 集群的 Job 对象

    • Job 对象

      对 计算逻辑 的封装

      • 输入数据路径

      • 输出数据路径

      • 相关参数

3. 串行化

○ 序列化 : 将 Java 对象转换为 二进制

○ 反序列化 : 将 二进制 转换回 Java 对象

  • Hadoop 自己实现序列化原因

    • 紧凑 : 让数据更紧凑,充分利用网络带宽
    • 快速 : 让 序列化、反序列化 开销更低
  • 对应类型

    • boolean : BooleanWritable

    • byte : ByteWritable

    • int : IntWritable

    • float : FloatWritable

    • long : LongWritable

    • double : DoubleWritable

    • String : Text

    • map : MapWritable

    • array : ArrayWritable

    • 自定义 Bean : 实现 Writeable 接口

      • 序列化 与 反序列化 的 字段顺序 必须完全一样

      • 如需作为 Mapper 类输出的 Key,需要实现 Comparable 接口,因为 Shuffle 过程需要排序

        Shuffle : map() 方法之后,reduce() 方法之前的过程

4. 原理分析

  • Map 阶段

    1. 将 物理切片 Block 转换成 逻辑切片 Split (两者关系默认为一对一),有多少 Split 就会启动多少 MapTask

      • Split 目的是为了确定一个 MapTask 要处理多大量的数据
      • 物理切片、逻辑切片 的默认大小皆为 128M
      • 如果一个文档仅比 128M 大一点点,也会被当成一个 Split
    2. 将 Split 交给 RecordReader对象(默认为 LineRecordReader) 读取,以 \n 作为分割符读取一行数据,返回 <key, value>。key 为每行首字符的偏移量,value 为这一行的文本内容

    3. 将 <key, value> 传入 Mapper ,调用 map() 方法 - 读取一行调用一次

    4. map() 方法末尾,调用 context.write() 方法,对数据设置分区 - 【默认】分区按照 key Hash 后以 Reduce Task 数量取模,来确定该数据要给哪一个 Reducer 运行

    5. 将数据写入 环形收集区,当环形收集区数据达到域值(默认 80%),将启动溢写线程,对 key 进行排序

    6. 使用 Combiner 压缩数据,减少网络传输量

      • 【注意】Combiner 处理过程不应影响最终结果,常用于 累加、最大值
      • Combiner 父类是 Reducer
    7. 合并溢写文档 - 每次溢写都会在硬盘上生成临时文档,但最终输出的文档一个 MapTask 只能有一个

  • Reduce 阶段

    1. 使用 HTTP 请求拉取该 ReduceTask 对应的分区数据,将数据放入内存

    2. 当数据量超出内存域值,将溢写到硬盘,并使用 Combiner 压缩数据

    3. 合并溢写文档,并进行归并排序,生成最终文档

    4. 按照 Key 进行分组,将 Key 相同的数据传入同一个 reduce() 方法

    5. 读取数据

    • 【默认】TextInputFormat : 普通文本

    • KeyValueTextInputFormat : 按照指定分割符,将一行文本数据封装成 kv 类型

    • NLineInputFormat : 按照行数进行分片划分

    • CombineTextInputFormat : 合并小文档,避免启动过多 MapTask 任务

    • 自定义 InputFormat

    6. 输出数据

    • 【默认】TextOutputFormat : 将每条纪录转为文本行,key、value 可以为任意类型,使用 toString() 方法输出

    • SequenceFileOutputFormat : 作为后续 MapReduce 的输入

    • 自定义 OutputFormat

七、Yarn

○ 作业调度 与 集群资源管理 框架

○ 负责 协调计算资源

1. 角色

  • ResourceManager (RM)【母公司】

    • 处理客户端请求

    • 监控 ApplicationManager

    • 监控 NodeManager

    • 资源分配 与 调度

  • NodeManager (NM)【子公司】

    • 单节点资源管理

    • 处理来自 ResourceManager 的命令

  • ApplicationMaster (AM)【项目负责人】

    • 向 ResourceManager 申请资源

    • 向 NodeManager 发送进程启动脚本

    • 监控、容错 MapTask、ReduceTask 任务

  • Container【工人】

    • 对任务运行环境的抽象

    • 运行任务

    • 向 ApplicationMaster 回报任务进度

  • Client【顾客】

    • 每 1 秒向 ApplicationMaster 询问任务进度
    • 每 5 秒调用 waitForCompletion() 方法来检查任务是否完成

2. 作业提交过程

1. 作业提交

  1. Client 调用 job.waitForCompletion() 方法,向集群提交 MapReduce 作业
  2. Client 向 RM 申请一个 作业ID
  3. RM 向 Client 返回 作业ID 和 Job资源提交路径
  4. Client 提交 jar 包、切片信息、配置文档 到指定的资源提交路径
  5. 向 RM 申请运行 AM

2. 作业初始化

  1. RM 将申请运行 AM 请求初始化为 Task
  2. RM 将该 Task 分派给某个空闲的 NM
  3. 该 NM 创建 Container,并产生 AM
  4. 该 NM 下载 Job 资源到本地

3. 任务分配

  1. AM 向 RM 申请运行多个 MapTask 任务资源
  2. RM 将任务分派给另外两个 NM
  3. 两个 NM 创建 Container

4. 运行任务

  1. AM 向两个 NM 发送进程启动脚本
  2. 两个 NM 运行 MapTask 并对输出数据进行排序
  3. AM 等待所有 MapTask 运行完毕,向 RM 申请容器,运行 ReduceTask
  4. RM 将 ReduceTask 分派到另外的 NM 中
  5. ReduceTask 向 MapTask 获取对应的分区数据
  6. 运行完毕后 AM 会向 RM 申请注销自己

MapTask、ReduceTask 会将进度、状态返回给 AM

3. 调度策略

  • FIFO 先进先出调度器
    • 缺点 : 任务优先级只考虑时间因素,没有考虑更多条件
  • 【默认】Capacity Scheduler 容量调度器
    • 多个组共享集群,每个组能获得集群的一部分计算能力
    • 每个组都有一个 FIFO 的队列维护任务顺序
  • 【CDH版默认】Fair Scheduler 公平调度器
    • 使用分组队列维护任务
    • 当只有一个队列中有 1 任务时,该任务可抢占全部的集群资源
    • 当 2 个队列都各有 1 个任务时,2 个任务各占 50% 集群资源
    • 当 2 个队列分别有 1 个 和 2 个 任务时,任务各占 50%、25%、25% 的集群资源