Hadoop 主要解决了海量数据的存储问题
一个 block 块在 NameNode 中占150byte(固定),过多小文件会占用 NameNode 内存
小文件的寻址时间大于读取时间
不支持并发写和随机写
一个文件只能有一个写,不允许多线程同时写
Hadoop 核心组件包含以下两种
我们后端开发接触的比较多的是Hadoop 生态系统中的附加组件,Sqoop 以及 Hive,分别用来同步数据和查询数据
HDFS 是 Hadoop 的分布式文件系统,解决了海量数据的存储问题
HDFS 使用 Master/Slave 架构,架构逻辑比较类似 Kafka、ES 等 Apache 的其他项目
一般一个集群有一个 NameNode 和一定数目 DataNode 组成,Namenode 是 HDFS 集群主节点,Datanode 是 HDFS 集群从节点,两种角色各司其职,共同协调完成分布式的文件存储服务
HDFS 中文件在物理上是分块存储,通过 dfs.blocksize 配置,2.x之后的版本默认128M
HDFS 中文件在逻辑上是连续的,提供一个文件目录树
1,客户端向 NameNode 发送写数据请求(包含待上传文件名和将要上传的路径)
2,NameNode 检查路径是否存在,文件是否重名等(假设满足上传条件)
3,NameNode 向客户端响应数据,可以上传文件
4,客户端根据文件大小进行切分成一个个 block 块,并向 NameNode 发送提交即将上传 block1 的请求
5,NameNode 查询 DataNode 信息,规划 block1 的存储位置
6,NameNode 向客户端返回 block1 可以存储的数据节点 ip 列表
7,客户端直接请求数据节点1上传 block1,数据节点1存储 block1 完毕并根据 ip 列表将 block1 发送给数据节点8,数据节点2存储完毕 block1 并根据 ip 列表将 block1 发送给数据节点3,数据节点3存储完成响应数据给数据节点2,数据节点2将响应数据给数据节点1,数据节点1将存储结果返回给 NameNode 和客户端
9,重复第四步上传下一个block
我知道你们想问什么,为什么 HDFS 的写流程必须一个个的上传块,不能并发上传吗?这是设计者对于 HDFS 写少读多场景的这种考量。该场景如此设计的好处:
1,每个数据块的写入是原子操作,即要么成功写入,要么完全不写入。这确保了数据的一致性。如果并发写入需要考量并发安全性问题
2,集群默认全同步,数据高可靠
1,客户端向 NameNode 请求下载文件
2,NameNode 返回目标文件的元数据
3,客户端根据元数据请求 DataNode 读取数据 block
4,DataNode 向客户端传输数据
5,重复第三步,直到所有的块传输完成
6,客户端根据元数据组装 block 块完成读取数据
NameNode 元数据的存储位置是在内存中,但是内存一旦断电元数据将丢失,因此必须将内存中的元数据存储在磁盘中用于备份,这里引入额外一个概念叫 Fsimagem
Fsimagem 为内存元数据的备份。若内存的元数据发生改变,如果同时更新 Fsimage 会降低效率,如果不更新会发生数据不一致问题
针对上述问题,最终逻辑是不更新 Fsimage 文件,为解决数据不一致问题,引入 edits 文件,该文件只记录操作并且采用追加写的形式,即每当内存的元数据发生改变的同时记录本次操作记录追加到磁盘中的 edits,这样内存元数据等于磁盘的 Fsimage + edits
当 NameNode 启动时先滚动 edits 并生成一个空的 edits.inprogress,会将 Fsimage 和 edits 文件加载到内存中进行合并,之后的操作(增删)将追加到 edits.inprogress 中
其行为类似 redis 的 RDB 和 AOF 机制
MapReduce 是一种编程模型和分布式计算框架,是开发基于 Hadoop 的数据分析应用的核心框架。MapReduce 的主要用途包括:
总之,只要是统计或者计算 Hadoop 中的数据,都会用到 MapReduce。Hive(基于 Hadoop 的数据仓库工具,它提供了一种 SQL-like 的查询语言,使得用户可以方便地进行数据查询和分析)底层对接 MapReduce 来执行查询和数据处理任务。Hive 的查询最终会被转换成一个或多个 MapReduce 作业来执行
MapReduce 实现分布式计算分成2个阶段,Map(映射)和 Reduce(归约)
第一个阶段 MapTask 并发实例,完全并行运行,互不干扰。首先会将输入数据分割成多个小块,每个小块称为一个切片(split)。每个切片的大小通常与 HDFS 的块大小(默认 128MB)一致。这么做使数据能够并行处理,提高处理速度。
随后我们对输入数据进行处理,生成中间键值对(key-value pairs)。每个 Map 任务从输入切片中读取数据,对每条记录调用用户定义的 Map 函数,生成中间键值对
第二个阶段 ReduceTask 完全并行运行,数据依赖上一个阶段所有 MapTask 并发实例输出。将 Map 任务生成的中间键值对进行分区、排序和合并
MapReduce 编程模型只能包含一个 Map 阶段一个 Reduce 阶段,但可以实现多个 MapReduce 串行运行
上面的描述可能有些抽象,让大家有很多问题,比如为啥 ReduceTask 也可以并行执行?如果 MapTask 的产物是键值对的话,那么存放在 HDFS 的关系型表会怎么转换成键值对然后暴露给我们?接下来举个例子让大家更加深入的了解问题:
假设我们有一个关系型表 users,其结构如下:
user_id | name | age | city |
---|---|---|---|
1 | Alice | 25 | New York |
2 | Bob | 30 | London |
3 | Carol | 22 | New York |
4 | Dave | 28 | Tokyo |
假设我们要计算每个城市的用户数量:
SELECT city, COUNT(*) AS user_count
FROM users
GROUP BY city;
在这个例子中,Map 阶段会将每行数据转换为键值对,其中键是城市名称,值是用户 ID。例如:
Map 阶段结束我们就得到很多键值对,在 Reduce 阶段,Hive 会将 Map 任务生成的中间结果按键进行分组,相同城市的键值对会被分到一个 ReduceTask 中:
ReduceTask 输出最后的处理结果。所以一个 sql 在 Hive 中的执行流程和普通 db 中是完全不一样的。通过上面的 case 我们可以看出,先 map 后 reduce 事实上就是想利用并发的能力处理大量数据,Hadoop 的创作者们将我们所有的查询操作都抽象成了这完全可以并行执行的两步