MapReduce计算流程
先看Map端,文件上传HDFS会切成Block,默认128M,然后有一个逻辑切片叫Split,它不改动存储,只是用来控制参与计算的节点数,一个切片就对应一个MapTask,Map任务从自己的切片里读数据,每次读一行,拼成KV,Key是偏移量,Value是一行内容。然后数据不是直接写磁盘,而是先写进内存里的环形缓冲区,默认100M,写到80%就触发溢写,写进去之前会先分区,默认是Key的哈希对Reduce数取模。缓冲区里的数据在溢写之前还会排个序,先按分区排,再按Key排,所以溢写出去的小文件内部是有序的。溢写会产生很多小文件,后面再归并成一个大文件,这个合并过程还能顺便用Combiner做一次局部汇总。
然后进入Shuffle,这是Map和Reduce之间最关键的环节。Reduce端通过HTTP去各个Map拉取属于自己的那份数据。相同的Key拉到同一个Reduce,然后把拉回来的数据进行一次全局排序,排好之后,按Key分组,每一组调一次reduce方法。最后每个Reduce把结果写到HDFS上,一个作业就走完了
YARN集群工作流程
首先客户端先向 ResourceManager 发一个 getNewApplication 请求,ResourceManager返回给客户端一个JobID和一个存资源的临时目录,然后客户端把作业的jar包,配置,算好的分片都传到HDFS,再用submitApplication把作业提交给ResourceManager,ResourceManager里的应用管理器收到之后,交给调度器,调度器选一台NodeManager,开一个Container,在里面把ApplicaitonMaster起起来。ApplicationMaster起来后的第一件事是向ResourceManager注册,然后收集分片情况,向ResourceManager申请资源,它用轮询的方式通过RPC拿资源,拿到之后再找对应的NodeManager,让它启动Container,然后把任务起起来,任务跑的过程中,通过RPC向ApplicationMaster汇报状态和进度,ApplicationMaster一旦发现哪个任务失败,就重新申请资源把它重启。
最后所有任务跑完,ApplicationMaster 向 ResourceManager申请注销,然后把自己关掉,资源全部释放,yarn的工作就完成了,最后计算框架更新计算进度和状态。
为什么会产生yarn
在Hadoop1的时候,MapReduce是把资源调度和计算揉在一起的,集群里就一个JobTracker,它既管资源分配,又管作业调度,下面的TaskTracker干活然后向它汇报。问题有两个。第一,JobTracker一个人扛所有事,因为是单点的,集群一大,就不行了,第二,这种系统是专用的,只能跑MapReduce这一类计算,想跑别的计算模型,就得再搭一套,资源和框架都对不上。所以Hadoop2就把MapReduce拆成了两块,一块是yarn,专门负责资源调度管理,另一块是MapReduce本身,专门负责计算,这样一抽出来,资源调度就变成了一个通用的平台,以后其他计算框架比如spark只要照着yarn的接口来,就能共享同一个集群的资源,不同再重新搭一套。
MapReduce数据倾斜
第一,将默认的哈希分区改成自定义分区,结合业务让每个分区数据量基本平衡
第二,可以改Key,在Key前面加随机数,让哈希之后能均匀一点。
第三,Reduce慢,那就给它加内存,加虚拟核
第四,如果是只有一个Reduce拖慢的,那就加Reduce数量分摊压力,前面这一层做完,再来一个作业做最终聚合。
Hadoop压缩算法
按位置选择,第一次读进来,选可切分的,也就是BZip2 或者 LZO,不然只有一个Map,中间Map到Reduce这一段,要选压缩解压都快的,比如Snappy、LZO、LZ4、Zstd,最后Reduce输出,看下游,如果还要接着算就选可切分的BZip2,如果是归档存起来,就选压缩比高的Gzip或者BZip2。
MR优化
先说小文件,上传到HDFS之前就把小文件合并掉,第二,合并之后如果文件太大,换存储格式或者压缩,但要注意能不能切片,第三,如果小文件已经在HDFS里了,那就用CombineFileInputFormat这类读取类,在读的时候合并。
然后就是数据倾斜,自定义分区,加盐改Key,给Reduce加资源,或者加Reduce数量做二次聚合
然后是推测执行,做法是给慢任务起一个备份任务,两份同时跑同一份数据,谁先跑完用谁,另一个杀掉,本质就是利用资源换时间,所以集群资源紧张的时候不能开他。
最后从执行流程上优化,分Map和Reduce两段,Map这边,环形缓冲区默认100M,到80%溢写,内存够就调大,能少溢写几次,合并因子默认一次合10个,调大能少合几轮;再加上Combiner做局部汇总、压缩中间数据、调响应线程数。Reduce这边,并行拉取默认5,可以调大,然后就是读缓存,默认数据是要写磁盘再读回来的,把那个参数从0调大一点,让一部分数据直接进Reduce,能省一次落盘回读。