admin管理员组

文章数量:1579393

系列文章目录

一、Hive调优(面试重点)
二、Hadoop原理(面试重点)


文章目录

  • 系列文章目录
  • 前言
  • 一、Hive调优(熟悉)
    • 6、join的优化操作
    • 7、列裁剪
    • 8、分区裁剪
    • 9、group by 操作(Map端聚合)
    • 10、count(distinct)
    • 11、笛卡尔积
    • 12、动态分区
    • 13、如何调整map和reduce的数量
    • 14、并行执行
    • 15、严格模式
    • 16、JVM 重用
    • 17、推测执行
    • 18、执行计划explain
    • 19 、Hive调优总结
  • 二、Hadoop原理
    • 1、HDFS
      • 1.1 块和副本
      • 1.2 三大机制
      • 1.3 写入数据原理[面试]
      • 1.4 读取数据原理[面试]
      • 1.5 edits和fsimage文件
      • 1.6 内存/文件元数据
        • 1.6.1 图解
        • 1.6.2 查看历史编辑文件
        • 1.6.3 查看镜像文件
      • 1.7 元数据存储的原理[面试]
      • 1.8 安全模式
      • 1.9 归档机制
      • 1.10 垃圾桶机制
    • 2、MapReduce
      • 2.1 单词统计流程
      • 2.2 MR底层原理[面试]
        • 2.2.1 map阶段
        • 2.2.2 shuffle阶段
        • 2.2.3 reduce阶段


前言

真实大数据开发常问面试题
本文主要论述,工作中常用的Hive调优和面试常考的Hadoop原理。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Hive调优(熟悉)

6、join的优化操作

思考: 在通过hive执行多表查询的SQL, 最终hive翻译的MR,是如何完成join的工作的呢?

默认的join行为, 基于reduce端的join工作

思考, 请问上述join存在哪些问题?

1、导致reduce的压力剧增,所有的数据全部都汇总到reduce中
2、当有了多个reduce之后,如果某个join的字段出现了大量的数据,那么会导致这些大量的数据都分配给到同一个reduce,从而导致数据倾斜

数据倾斜: 实际例子解释,例如有100袋水泥,1袋给别人,99袋给你搬,那么你出现了数据倾斜

那么如何解决reduce端join遇到问题? 可以通过底层map 端 join实现,还可以sql语句join之前提前过滤数据或者转换数据实现

join之前提前过滤数据例子:

1- 没有提前过滤的SQL:
语句: select * from a left join b on a.id = b.id where a.age>=18;
过程: 先将a表和b表的所有数据全部加载出来(全表扫描),然后进行join关联操作,最后对join后的数据进行age>=18的过滤操作。这样会导致最开始处理的数据量比较大

2- 有提前过滤的SQL
语句: select * from (select * from a where age>=18) as new_a left join b on a.id = b.id;
过程: 在对a表数据进行全表扫描的时候同时对数据按照age>=18进行过滤,过滤出来以后再和b表进行join关联。这样能够让后续处理的数据量变少
    1. 大表和小表进行join:
    • 解决方案: map join

    • 如何开启这种操作呢?

      set hive.auto.convert.join = true; -- 默认为true  开启mapJoin支持 
      set hive.mapjoin.smalltable.filesize= 25000000;  设置 小表的最大的数据量  23.84m
      
    • 在执行SQL, 应该将小表放置前面呢, 还是大表放置在前面呢,还是都可以呢 ?

      (hive1旧版本中) : 要求 必须将小表在前大表在后 ,只有这样才可能走Map Join
      (hive2、3新版中): 无所谓, 谁在前, 谁在后, 没有任何的影响, hive会自动进行优化
      
    1. 大表和大表join
    • 解决方案:

      1- 将数据过滤提前,也就是在表join之前就先对数据进行过滤,能够减少后续数据处理的量
      2- 如果join字段上,有很多空值(也就是null值),这种情况进行join的时候,容易出现数据倾斜的问题。可以在该字段的后面(一般是放在后面)跟上一些随机值
      3- 使用分桶表,但是一般在公司里面很少说为了去优化join将表变成分桶
      

7、列裁剪

Hive在读数据的时候,可以只读取查询中所需要用到的列,而忽略其他列

例如:

假设有一个表A: a b c d e   5个字段, 请查看以下SQL
select  a,b from A where a=xxx;

在这条SQL, 发现没有使用c d e 字段, 在from A表时候, 读取数据, 只需要将a列 和 b列数据读取出来即可, 不需要读取cde列字段, 这样可以减少读取的数据量, 从而提升效率

如何配置呢?

--【列裁剪(只读取sql语句需要的字段,节省读取开销,提升效率)
set hive.optimize.cp=true;  -- 默认就是true  (在hive 2.x中无需在配置了, 直接为固定值: true)

8、分区裁剪

如果操作的表是一张分区表, 那么建议一定要带上分区字段, 以减少扫描的数据量, 从而提升效率,

如何配置呢?

-- 分区裁剪
set hive.optimize.pruner=true; --默认为就是true (在hive 2.x中无需在配置了, 直接为固定值: true)


-- 优化前
select * from multi_pat_tb where my_date>='2022-05-01' and my_date<='2022-05-02' 

-- 优化后
select * from multi_pat_tb where my_date>='2022-05-01' and my_date<='2022-05-02' 
and year=2022 and month=5

总结:

列裁剪和分区裁剪都是为了减少后续处理的数据量

9、group by 操作(Map端聚合)

方案一:  
    (1)是否在Map端进行聚合,默认为True
    set hive.map.aggr = true;
    (2)在Map端进行聚合操作的条目数目
    set hive.groupby.mapaggr.checkinterval = 100000;

方案二:  官方称为 负载均衡
    (3)有数据倾斜的时候进行负载均衡(默认是false)
    set hive.groupby.skewindata = true;
    第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;
    第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。

10、count(distinct)

说明 : count(distinct) 在数据量比较大的情况下, 效率并不高

思考: 你知道是为什么吗?

因为该操作需要对全局数据进行去重和计数处理

原因如下: 
	请问1: 执行count操作的时候, hive翻译的MR, reduce数量是否可以有多个? 必然不会有多个, 只能有一个, 因为全局求最终结果
	此时如果执行统计的时候, 需要进行去重,那么去重工作是由reduce执行去重操作,  由于reduce只有一个, 所有的数据都在一个reduce中, 此时reduce的压力比较大
		
	希望执行去重工作可能有多个reduce一起来执行操作, 此时可以将SQL优化: 
		原有:
			select count(distinct ip) from ip_tab;
		优化: 
			select 
			    count(ip)
			from
			   (select ip from ip_tab group by ip) tmp;
	
		请注意: 这样的做法, 虽然会运行两个MR, 但是当数据量足够庞大的时候, 此操作绝对是值得的, 如果数据量比较少, 此操作效率更低

-- count(distinct)优化
-- 没有优化前。48s
select count(distinct city_id) from log_text;

-- 优化后。1分22秒
set hive.optimize.countdistinct; -- 默认为true,也就是开启
set hive.optimize.countdistinct=true;
select count(city_id) from (select city_id from log_text group by city_id) as tmp;

注意: 为什么优化后运行时间反而增加了呢?
因为目前数据量太小,体现不出优势。类似雅迪和兰博基尼的启动速度

11、笛卡尔积

什么是笛卡尔积呢? 在进行join的时候, 两个表乘积之后结果就是笛卡尔积的结果

比如: 一个表有5条, 一个表有3条数据, 笛卡尔积结果就有15条数据 , 笛卡尔积中有大量数据都是无用数据

什么时候会产生笛卡尔积呢?

在多表join的时候, 关联条件缺少或者使用错误的关联条件以及将关联条件放置在where中都会导致笛卡尔积

在实际使用中, 建议:

1- 在join的时候一定要加上on条件。如果没有可关联的字段,可以写 on 1=1
2- 关联条件要放在on语句里面。不要放在where语句中,因为这种写法会产生笛卡尔积
3- (从业务/需求层面)如果在实际开发中真的发现没有可关联的字段/条件,可以和业务方进行沟通,是否要调整需求,或者条数

示例:

-- 笛卡尔积
-- 关联条件放在on语句中,不会产生笛卡尔积
select * from (select * from log_text where city_id=156) as new_a left outer join log_text b on new_a.city_id=b.city_id;

-- 关联条件放在where语句中,会产生笛卡尔积
select * from (select * from log_text where city_id=156) as new_a left outer join log_text b where new_a.city_id=b.city_id;

12、动态分区

需求: 请将下面的一个分区表数据, 拷贝到另一个分区表, 保证对应区数据放置到另一个表的对应区下

如何配置呢?

作用:  帮助一次性灌入多个分区的数据
参数: 
	set hive.exec.dynamic.partition.mode=nonstrict;  -- 开启非严格模式 默认为 strict(严格模式)
	set hive.exec.dynamic.partition=true;  -- 开启动态分区支持, 默认就是true
	
可选的参数:
	set  hive.exec.max.dynamic.partitions=1000; -- 在所有执行MR的节点上,最大一共可以创建多少个动态分区。
	set hive.exec.max.dynamic.partitions.pernode=100; -- 每个执行MR的节点上,最大可以创建多少个动态分区
	set hive.exec.max.created.files=100000; -- 整个MR Job中,最大可以创建多少个HDFS文件

示例:

-- 动态分区
-- 创建分区表
create table score_part_tb(
    name string,
    subject string,
    score int
) partitioned by (year string)
row format delimited fields terminated by '\t';

-- load导入数据
load data inpath '/dir/score.txt' into table score_part_tb partition (year="2022");

-- 数据验证
select * from score_part_tb;


-- 动态分区参数设置
set hive.exec.dynamic.partition.mode=nonstrict;  -- 开启非严格模式 默认为 strict(严格模式)
set hive.exec.dynamic.partition=true;  -- 开启动态分区支持, 默认就是true

-- 创建表
create table score_part_tb_dynamic(
    name string,
    subject string,
    score int
) partitioned by (dynamic_year string)
row format delimited fields terminated by '\t';

-- 验证表结构
show create table score_part_tb_dynamic;
select * from score_part_tb_dynamic;

-- 动态分区插入数据中,注意partition后面小括号中指定的是前面表的分区字段
insert into score_part_tb_dynamic partition (dynamic_year) select * from score_part_tb;

13、如何调整map和reduce的数量

1>是不是map数越多越好?
答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

2>是不是保证每个map处理接近128m的文件块,就高枕无忧了?
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

3>是不是reduce数越多越好?
答案是否定的。如果reduce设置的过大,对整个作业会产生一定的影响。
①过多的启动和初始化reduce也会消耗时间和资源;
②另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

4>在什么情况下, 只能有一个reduce呢?

以下几种, 不管如何设置, 最终翻译后reduce只能有一个
​ 1) 执行order by操作
​ 2) 执行不需要group by直接聚合的操作
​ 3) 执行笛卡尔积

  • 如何调整mapTask数量:

    小文件场景:当input的文件都很小,把小文件进行合并归档,减少map数, 设置map数量:

    -- 每个Map最大输入大小(这个值决定了合并后文件的数量)
        set mapred.max.split.size=256000000;
    -- 一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
        set mapred.min.split.size.per.node=1;
    -- 一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
        set mapred.min.split.size.per.rack=1;
    -- 执行Map前进行小文件合并默认CombineHiveInputFormat
        set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    

    大文件场景:当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。

    举例:如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的,
    ​ 这种情况下,我们要考虑将这一个文件合理的拆分成多个,这样就可以用多个map任务去完成。
    ​ set mapred.reduce.tasks=10;
    ​ create table a_1 as select * from tab_info distribute by rand(123);
    ​ 这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。

  • 如何reduce的数量:

    -- 查看reduces数量
    -- 该值默认为-1,由hive自己根据任务情况进行判断。
    set mapred.reduce.tasks;
    set mapreduce.job.reduces;
    -- (1)每个Reduce处理的数据量默认是256MB左右
    set	hive.exec.reducers.bytes.per.reducer=256000000;
    -- (2)每个任务最大的reduce数,默认为1009;
    set	hive.exec.reducers.max=1009;
    
    
    注意: 以下几种, 不管如何设置, 最终翻译后reduce只能有一个
    1) 执行order by操作 
    2) 执行不需要group by直接聚合的操作
    3) 执行笛卡尔积
    

14、并行执行

在执行一个SQL语句的时候, SQL会被翻译为MR, 一个SQL有可能被翻译成多个MR, 那么在多个MR之间, 有些MR之间可能不存在任何的关联, 此时可以设置让这些没有关联的MR 并行执行, 从而提升效率 , 默认是 一个一个来

如何配置:

set hive.exec.parallel=false;              -- 打开任务并行执行,默认关闭
set hive.exec.parallel.thread.number=8;  -- 同一个sql允许最大并行度,默认为8。

前提: 服务器必须有资源, 如果没有 即使支持并行, 也没有任何作用

案例:

下面这些SQL案例,都是会翻译成多个MapReduce

select  * from A ....
union all
select * from B ...;

例如:
select from (select * from A group by ...) tmp1 join (select * from B group by xxx) on ...

没有开启并行执行的时候,SQL语句间是串行(一个一个来,排了一个对)执行:

15、严格模式

​ hive提供一种严格模式, 主要目的, 是为了限制一些 效率极低的SQL 防止其执行时间过长, 影响其他的操作

屏蔽一下操作:
1) 执行order by 不加 limit
2) 出现笛卡尔积的现象SQL
3) 查询分区表, 不带分区字段

前提: 数据量足够大, 如果数据量比较少, 严格模式对此三项内容不生效

如何配置:

set hive.mapred.mode = strict;  --开启严格模式 
set hive.mapred.mode = nostrict; --开启非严格模式   最新默认

注意: 
	1- 从Hive3.x版本开始,该参数已经失效,内部就是严格模式
	2- 前提: 表数据量足够大才会进行严格模式的检查。如果数据量比较少, 严格模式对此三项内容不生效

16、JVM 重用

此操作, 在hive2.x已经不需要配置了, 默认支持

jvm重用: 默认情况下, container资源容器  只能使用一次,不能重复使用, 开启JVM重用, 运行container容器可以被重复使用,在hive2.x已经默认支持了

17、推测执行

调优举例:
大数据小组,假设张三有离职苗头,大数据主管会将张三负责的项目和功能分配给到其他同事来负责。
Hadoop采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
hadoop中默认两个阶段(map和reduce)都开启了推测执行机制。
hive本身也提供了配置项来控制reduce-side的推测执行:

set hive.mapred.reduce.tasks.speculative.execution=true;

关于调优推测执行机制,还很难给一个具体的建议。如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大。


什么时候需要开启?
如果提交的SQL语句,运行的耗时的远超你的预期。你就可以开启推测执行

什么时候不需要开启?
如果对SQL的运行耗时没有特别严格的要求,那么可以不开启。不开启同时能够节约Yarn集群资源。

18、执行计划explain

使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道Hive是如何处理你的SQL语句的。帮助我们了解底层原理,hive调优,排查数据倾斜等有很有帮助 

使用示例:explain [...]  sql查询语句;

explain sql语句: 查看执行计划的基本信息

(1)stage dependencies:各个stage之间的依赖性
包含多个stage阶段,例如 Stage-1和Stage-0,Stage-1 是根stage,Stage-0 依赖 Stage-1,
(2)stage plan:各个stage的执行计划
包含两部分: map端执行计划树和reduce端执行计划树

explain select * from score_part_tb_dynamic order by score desc;

19 、Hive调优总结

1- 在实际工作中一般不需要针对所有参数去做调整。因为Hive官方已经是提供性能很好的参数配置
2- 理解这些调优手段带来的好处。能够在面试的时候回答出来4-5条具体的调优方式即可,不需要去记忆配置参数
3- 参数调优也不是说一下子就能调的很好,需要反复多次的进行参数调整测试

二、Hadoop原理

1、HDFS

hdfs默认文件配置:https://hadoop.apache/docs/r3.3.4/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml

1.1 块和副本

为什么需要将大文件分成不同的block分散到多台服务器上进行存储?因为有如下的优点
1- 避免单台服务器存储空间不够的情况
2- 能够同时利用多台服务器的性能,例如:磁盘读写速度等
block块: HDFS被设计成能够在一个大集群中跨机器可靠地存储超大文件。它将每个文件拆分成一系列的数据块进行存储,这个数据块被称为block,除了最后一个,所有的数据块都是同样大小的。

block 块大小默认: 128M(134217728字节)     注意: 块同样大小方便统一管理

注意: 为了容错,文件的所有block都会有副本。每个文件的数据块大小和副本系数都是可配置的。
	
副本系数默认:  3个       副本好处: 副本为了保证数据安全(用消耗存储资源方式保证安全,导致了大数据瓶颈是数据存储)

hdfs默认文件配置:https://hadoop.apache/docs/r3.3.4/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml

如何修改块大小和副本数量呢?可以在hdfs-site.xml中配置如下属性:
<property>
	<name>dfs.blocksize</name>
	<value>134217728</value>
	<description>设置HDFS块大小,单位是b</description>
</property>

<property>
    <name>dfs.replication</name>
    <value>3</value>
</property>

图解:

块 :默认统一大小128m : 为了方便统一管理

副本: 默认3个的原因: 为了保证数据的安全性

1.2 三大机制

副本机制: 为了保证数据安全和效率,block块信息存储多个副本,第一副本保存在客户端所在服务器,第二副本保存在和第一副本不同机架服务器上,第三副本保存在和第二副本相同机架不同服务器

负载均衡机制: namenode为了保证不同的datanode中block块信息大体一样,分配存储任务的时候会优先保存在距离近且余量比较大的datanaode上

心跳机制: datanode每隔3秒钟向namenode汇报自己的状态信息,如果某个时刻,datanode连续10次不汇报了,namenode会认为datanode有可能宕机了,namenode就会每5分钟(300000毫秒)发送一次确认消息,连续2次没有收到回复,就认定datanode此时一定宕机了(确认datanode宕机总时间3*10+5*2*60=630秒)。观察地址http://node3:9864/datanode.html

1.3 写入数据原理[面试]

1.客户端发起写入数据的请求给namenode
2.namenode接收到客户端请求,开始校验(是否有权限,路径是否存在,文件是否存在等),如果校验没问题,就告知客户端可以写入
3.客户端收到消息,开始把文件数据分割成默认的128m大小的的block块,并且把block块数据拆分成64kb的packet数据包,放入传输序列

4.客户端携带block块信息再次向namenode发送请求,获取能够存储block块数据的datanode列表
5.namenode查看当前距离上传位置较近且不忙的datanode,放入列表中返回给客户端
6.客户端连接datanode,开始发送packet数据包,第一个datanode接收完后就给客户端ack应答(客户端就可以传入下一个packet数据包),同时第一个datanode开始复制刚才接收到的数据包给node2,node2接收到数据包也复制给node3(复制成功也需要返回ack应答),最终建立了pipeline传输通道以及ack应答通道
7.其他packet数据根据第一个packet数据包经过的传输通道和应答通道,循环传入packet,直到当前block块数据传输完成(存储了block信息的datanode需要把已经存储的块信息定期的同步给namenode)

8.其他block块数据存储,循环执行上述4-7步,直到所有block块传输完成,意味着文件数据被写入成功(namenode把该文件的元数据保存上)
9.最后客户端和namenode互相确认文件数据已经保存完成(也会汇报不能使用的datanode)

注意: 不要死记硬背,要结合自己的理解,转换为自己的话术,用于面试

1.4 读取数据原理[面试]

1.客户端发送读取文件请求给namenode

2.namdnode接收到请求,然后进行一系列校验(路径是否存在,文件是否存在,是否有权限等),如果没有问题,就告知可以读取
3.客户端需要再次和namenode确认当前文件在哪些datanode中存储
4.namenode查看当前距离下载位置较近且不忙的datanode,放入列表中返回给客户端

5.客户端找到最近的datanode开始读取文件对应的block块信息(每次传输是以64kb的packet数据包),放到内存缓冲区中
6.接着读取其他block块信息,循环上述3-5步,直到所有block块读取完毕(根据块编号拼接成完整数据)
7.最后从内存缓冲区把数据通过流写入到目标文件中

8.最后客户端和namenode互相确认文件数据已经读取完成(也会汇报不能使用的datanode)

注意: 不要死记硬背,要结合自己的理解,转换为自己的话术,用于面试

1.5 edits和fsimage文件

namenode管理元数据: 基于edits和fsimage的配合,完成整个文件系统文件的管理。每次对HDFS的操作,均被edits文件记录, edits达到大小上限后,开启新的edits记录,定期进行edits的合并操作
    如当前没有fsimage文件,  将全部edits合并为第一个fsimage文件
    如当前已存在fsimage文件,将全部edits和已存在的fsimage进行合并,形成新的fsimage

    edits编辑文件: 记录hdfs每次操作(namenode接收处理的每次客户端请求)
    fsimage镜像文件: 记录某一个时间节点前的当前文件系统全部文件的状态和信息(namenode所管理的文件系统的一个镜像)

SecondaryNameNode辅助合并元数据: SecondaryNameNode会定期从NameNode拉取数据(edits和fsimage)然后合并完成后提供给NameNode使用。
    对于元数据的合并,是一个定时过程,基于两个条件,下面的两个条件只需要满足其中一个都会触发:
    dfs.namenode.checkpoint.period:默认3600(秒)即1小时
    dfs.namenode.checkpoint.txns: 默认1000000,即100W次事务
 
    dfs.namenode.checkpoint.check.period: 检查是否达到上述两个条件,默认60秒检查一次,只要有一个达到条件就执行拉取合并

1.6 内存/文件元数据

namenode和secondarynamenode:  配合完成对元数据的保存

元数据: 内存元数据 和 文件元数据 两种分别在内存和磁盘上

内存元数据: namnode运行过程中产生的元数据会先保存在内存中,再保存到文件元数据中。
内存元数据优缺点: 优点: 因为内存处理数据的速度要比磁盘快。  缺点: 内存一断电,数据全部丢失

文件元数据: Edits 编辑日志文件和fsimage 镜像文件
Edits编辑日志文件: 存放的是Hadoop文件系统的所有更改操作(文件创建,删除或修改)的日志,文件系统客户端执行的更改操作首先会被记录到edits文件中
Fsimage镜像文件: 是元数据的一个持久化的检查点,包含Hadoop文件系统中的所有目录和文件元数据信息,但不包含文件块位置的信息。文件块位置信息只存储在内存中,是在 datanode加入集群的时候,namenode询问datanode得到的,并且不间断的更新

fsimage和edits关系: 两个文件都是经过序列化的,只有在NameNode启动的时候才会将fsimage文件中的内容加载到内存中,之后NameNode把增删改查等操作记录同步到edits文件中.使得内存中的元数据和实际的同步,存在内存中的元数据支持客户端的读操作,也是最完整的元数据。
1.6.1 图解

edits和fsimage文件所在的位置:namenode运行所在机器的/export/data/hadoop/dfs/name/current

secondaryNameNode对应的目录:/export/data/hadoop/dfs/namesecondary/current

注意:千万不要随便去修改/删除这些文件

1.6.2 查看历史编辑文件
命令:  hdfs oev -i edits文件名 -o 自定义文件名.xml
[root@node1 current]# cd /export/data/hadoop/dfs/name/current
[root@node1 current]#  hdfs oev -i edits_0000000000000039578-0000000000000039647 -o /home/39647.xml
[root@node1 current]# cat /home/39647.xml
1.6.3 查看镜像文件
命令:  hdfs oiv -i fsimage文件名 -p XML -o 自定义文件名.xml
[root@node1 current]# cd /export/data/hadoop/dfs/name/current
[root@node1 current]# hdfs oiv -i fsimage_0000000000000033405 -p XML -o /home/405_fsimage.xml
[root@node1 current]# cat /home/405_fsimage.xml

1.7 元数据存储的原理[面试]

注意: 第一次启动namenode的时候是没有编辑日志文件和镜像文件的,下图主要介绍的是第二次及以后访问的时候情况流程

1.namenode第一次启动的时候先把最新的fsimage文件中内容加载到内存中,同时把edits文件中内容也加载到内存中
2.客户端发起指令(增删改查等操作),namenode接收到客户端指令把每次产生的新的指令操作先放到内存中
3.然后把刚才内存中新的指令操作写入到edits_inprogress文件中
4.edits_inprogress文件中数据到了一定阈值的时候,把文件中历史操作记录写入到序列化的edits备份文件中
5.namenode就在上述2-4步中循环操作...

6.当secondarynamenode检测到自己距离上一次检查点(checkpoint)已经1小时或者事务数达到100w,就触发secondarynamenode询问namenode是否对edits文件和fsimage文件进行合并操作
7.namenode告知可以进行合并
8.secondarynamenode将namenode上积累的所有edits和一个最新的fsimage下载到本地,并加载到内存进行合并(这个过程称checkpoint)
9.secondarynamenode把刚才合并后的fsimage.checkpoint文件拷贝给namenode

10.namenode把拷贝过来的最新的fsimage.checkpoint文件,重命名为fsimage,覆盖原来的文件

注意: 不要死记硬背,要结合自己的理解,转换为自己的话术,用于面试

1.8 安全模式

安全模式: 是namenode的自我保护。不允许HDFS客户端进行任何修改文件的操作,包括上传文件,删除文件等操作。

1.9 归档机制

归档原因: 每个小文件单独存放到hdfs中(占用一个block块),那么hdfs就需要依次存储每个小文件的元数据信息,相对来说浪费资源。和压缩文件一样,也就是将多个小文件压缩到一起。小文件了个数过多,会导致map程序过多,这样会占用过多的Yarn集群资源

归档格式: hadoop archive -archiveName 归档名.har -p 原始文件的目录 归档文件的存储目录

准备工作: HDFS上准备一个目录binzi,里面存储三个文件 1.txt 2.txt 3.txt …

[root@node1 /]# hadoop archive -archiveName dir.har -p /dir /

归档特性:
Hadoop Archives的URI是:har://scheme-hostname:port/路径/归档名.har
scheme-hostname格式为hdfs-域名:端口
如果没有提供scheme-hostname,它会使用默认的文件系统: har:///路径/归档名.har

[root@node1 home]# hdfs dfs -cat /dir.har/part-0
512 霸王防脱 20 200
513 蓝月亮洗衣液 10 15
514 心心相印抽纸 18 10
515 雕牌洗洁精 8 104151 耳机 40 1
4152 霸王防脱 28 1
4153 衣架 12 1
511 飘柔洗发水 15 2
512 霸王防脱 20 1
513 蓝月亮洗衣液 10 1
514 心心相印抽纸 18 1521 牙刷套装 15 2
522 小米手机壳 20 1
[root@node1 home]# hdfs dfs -cat har:///dir.har/part-0
cat: `har:///dir.har/part-0': No such file or directory
[root@node1 home]# hdfs dfs -cat har:///dir.har/*
512 霸王防脱 20 200
513 蓝月亮洗衣液 10 15
514 心心相印抽纸 18 10
515 雕牌洗洁精 8 104151 耳机 40 1
4152 霸王防脱 28 1
4153 衣架 12 1
511 飘柔洗发水 15 2
512 霸王防脱 20 1
513 蓝月亮洗衣液 10 1
514 心心相印抽纸 18 1521 牙刷套装 15 2
522 小米手机壳 20 1
您在 /var/spool/mail/root 中有新邮件
[root@node1 home]# 

1.10 垃圾桶机制

在虚拟机中rm命令删除文件,默认是永久删除

在虚拟机中需要手动设置才能使用垃圾桶回收: 把删除的内容放到: /user/root/.Trash/Current/
先关闭服务: 在 node1 中执行 stop-all.sh 新版本不关闭服务也没有问题
再修改文件 core-site.xml : 进入/export/server/hadoop-3.3.0/etc/hadoop目录下进行修改

<property>
<name>fs.trash.interval</name>
<value>1440</value>
</property>

其中,1440 表示 1440分钟,也就是 24小时,一天的时间。

设置了垃圾桶机制好处: 文件不会立刻消失,可以去垃圾桶里把文件恢复,继续使用

# 没有开启垃圾桶效果
[root@node1 hadoop]# hdfs dfs -rm /binzi/hello.txt
Deleted /binzi/hello.txt

# 开启垃圾桶
[root@node1 ~]#cd /export/server/hadoop-3.3.0/etc/hadoop
[root@node1 hadoop]# vim core-site.xml
# 注意: 放到<configuration>内容</configuration>中间
<property>
    <name>fs.trash.interval</name>
    <value>1440</value>
</property>

# 开启垃圾桶效果
[root@node1 hadoop]# hdfs dfs -rm -r /test1.har
2023-05-24 15:07:33,470 INFO fs.TrashPolicyDefault: Moved: 'hdfs://node1.itcast:8020/test1.har' to trash at: hdfs://node1.itcast:8020/user/root/.Trash/Current/test1.har

# 开启垃圾桶后并没有真正删除,还可以恢复
[root@node1 hadoop]# hdfs dfs -mv /user/root/.Trash/Current/test1.har  / 

2、MapReduce

2.1 单词统计流程

已知文件内容:  
    hadoop hive hadoop spark hive 
    flink hive linux hive mysql

input结果: 
        k1(行偏移量)   v1(每行文本内容)
        0   		 hadoop hive hadoop spark hive 
        30   		 flink hive linux hive mysql
map结果:
        k2(split切割后的单词)  v2(拼接1)     
        hadoop  			 1
        hive   				 1
        hadoop  			 1
        spark  				 1
        hive   				 1
        flink  				 1
        hive   				 1
        linu  				 1
        hive   				 1
        mysql  				 1
分区/排序/规约/分组结果:
        k2(排序分组后的单词)   v2(每个单词数量的集合)
        flink   		    [1]
        hadoop  			[1,1]
        hive    			[1,1,1,1]
        linux   			[1] 
        mysql   			[1]
        spark   			[1]
reduce结果:
        k3(排序分组后的单词)   v3(聚合后的单词数量)
        flink 				 1
        hadoop 				 2
        hive   				 4
        linux  				 1
        mysql  				 1
        spark  				 1
output结果:   注意: 输出目录一定不要存在,否则报错
        flink   1
        hadoop  2
        hive    4
        linux   1
        mysql   1
        spark   1

2.2 MR底层原理[面试]

2.2.1 map阶段
第一阶段是把输入目录下文件按照一定的标准逐个进行逻辑切片,形成切片规划。默认情况下Split size 等于 Block size。每一个切片由一个MapTask处理(当然也可以通过参数单独修改split大小)
第二阶段是对切片中的数据按照一定的规则解析成对。默认规则是把每一行文本内容解析成键值对。key是每一行的起始位置(单位是字节),value是本行的文本内容。(TextInputFormat)
第三阶段是调用Mapper类中的map方法。上阶段中每解析出来的一个,调用一次map方法。每次调用map方法会输出零个或多个键值对
第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。默认是只有一个区。分区的数量就是Reducer任务运行的数量。默认只有一个Reducer任务
第五阶段是对每个分区中的键值对进行排序。首先,按照键进行排序,对于键相同的键值对,按照值进行排序。比如三个键值对<2,2>、<1,3>、<2,1>,键和值分别是整数。那么排序后的结果是<1,3>、<2,1>、<2,2>。
如果有第六阶段,那么进入第六阶段;如果没有,直接输出到文件中
第六阶段是对数据进行局部聚合处理,也就是combiner处理。键相等的键值对会调用一次reduce方法。经过这一阶段,数据量会减少。本阶段默认是没有的。

注意: 不要死记硬背,要结合自己的理解,转换为自己的话术,用于面试
2.2.2 shuffle阶段
shuffle是Mapreduce的核心,它分布在Mapreduce的map阶段和reduce阶段。一般把从Map产生输出开始到Reduce取得数据作为输入之前的过程称作shuffle。
Collect阶段:将MapTask的结果输出到默认大小为100M的环形缓冲区,保存的是key/value,Partition分区信息等
Spill阶段:当内存中的数据量达到一定的阀值(80%)的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了combiner,还会将有相同分区号和key的数据进行排序
Merge阶段:把所有溢出的临时文件进行一次合并操作,以确保一个MapTask最终只产生一个中间数据文件
Copy阶段: ReduceTask启动Fetcher线程到已经完成MapTask的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上
Merge阶段:在ReduceTask远程复制数据的同时,会在后台开启两个线程对内存到本地的数据文件进行合并操作。
Sort阶段:在对数据进行合并的同时,会进行排序操作,由于MapTask阶段已经对数据进行了局部的排序,ReduceTask只需保证Copy的数据的最终整体有效性即可。 

注意: 不要死记硬背,要结合自己的理解,转换为自己的话术,用于面试
2.2.3 reduce阶段
第一阶段是Reducer任务会主动从Mapper任务复制其输出的键值对。Mapper任务可能会有很多,因此Reducer会复制多个Mapper的输出。

第二阶段是把复制到Reducer本地数据,全部进行合并,即把分散的数据合并成一个大的数据。再对合并后的数据排序。

第三阶段是对排序后的键值对调用reduce方法。键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对。最后把这些输出的键值对写入到HDFS文件中。

注意: 不要死记硬背,要结合自己的理解,转换为自己的话术,用于面试

本文标签: 详解案例Hive调优