Hadoop
MapReduce
split分片
hadoop将mapreduce的输入数据划分成等长的小数据块,称为输入分片。hadoop为每个分片构建一个map任务,并由该任务来运行用户自定义的map函数从而处理分片中的每条记录
分片是并行处理的,分片小,那么整个处理过程将获得更好的负载平衡;然后分片切的太小,那么管理分片的总时间和构建map任务的总时间将决定作业的执行时间。
通常,一个合理的分片大小趋于hdfs的一个块大小,默认128MB。因为它是确保可以存储在单个节点上的最大输入块大小。如果分片跨越两个数据块,那么对于任何一个hdfs节点,基本上不可能同时存储这两个数据块,因此分片中的部分数据需要通过网络传输到map任务运行的节点,使得效率降低
数据本地化
hadoop在存储有输入数据的节点上运行map任务,可以获得最佳性能
Map输出
map的任务将其输出写入本地磁盘,而非hdfs。因为map的输出是中间结果,该中间结果由reduce任务处理后才产生最终输出结果,而且一旦作业完成,map的输出结果就可以删除
Reduce输出
reduce任务并不具备数据本地化优势,单个reduce任务的输入通常来自所有mapper的输出。reduce输出存储在hdfs中,第一个复本存储在本地节点上,其他复本处于可靠性考虑存储在其他机架的节点中
Reduce数量
reduce任务的数量是独立指定的。如果有好多个reduce任务,每个map任务都会针对输出进行分区(partition),如对reduce个数hash,即为每个reduce任务建一个分区
MapReduce工作机制
有如下5个独立实体:
- 客户端,提交MapReduce作业
- YARN资源管理器,负责协调集群上计算机资源的分配
- YARN节点管理器,负责启动和监视集群中机器上的计算容器(container)
- MapReduce的application master,负责协调运行MapReduce作业的任务。它和MapReduce任务在容器中运行,这些容器由资源管理器分配并由节点管理器运行管理
- 分布式文件系统,用来与其他实体间共享作业文件
作业的提交
Job的submit()方法创建一个内部的JobSummiter实例,并且调用其submitJobInternal()方法。提交作业后,waitForCompletion()每秒轮询作业的进度,如果发现自上次报告后有改变,便把进度报告到控制台。作业完成后,如果成功,就显示作业计数器;如果失败,则错误回被记录到控制台
JobSummiter所实现的作业提交过程如下:
1⃣️向资源管理器请求一个新应用ID,用于MapReduce作业ID(步骤2)
2⃣️检查作业的输出说明。例如,如果没有指定输出目录或输出目录已存在,作业就不提交,错误抛给MapReduce程序
3⃣️计算作业的输入分片。如果输入分片无法计算,比如因为输入路径不存在,作业就不提交,错误抛给MapReduce程序
4⃣️将运行作业所需要的资源(包括作业JAR文件、配置文件和计算所得的输入分片)复制到一个以作业ID命名的目录下的共享文件系统中(步骤3)。作业JAR的复本较多(默认10),因此在运行作业的任务时,集群中有多个复本可供节点管理器访问
5⃣️通过调用资源管理器的submitApplication()方法提交作业(步骤4)
作业的初始化
1⃣️资源管理器收到调用它的submitApplication()消息后,便将请求传递给YARN调度器(scheduler)。调度器分配一个容器,然后资源管理器
在节点管理器的管理下在容器中启动application master的进程(步骤5a和5b)
2⃣️MapReduce作业的apllication master是一个Java应用程序,它的主类是MRAppMaster。由于将接受来自任务的进度和完成报告(步骤6),因此application master对作业的初始化是通过创建多个标记对象以保持对作业进度的跟踪来完成的。接下来,它接受来自共享文件系统的、在客户端计算的输入分片(步骤7)。然后对每一个分片创建一个map任务对象以及由mapreduce.job.reduces属性确定的多个reduce任务对象。任务ID在此时分配。
3⃣️最后,在任何任务运行之前,application master调用setupJob()方法设置OutputCommitter。FileOutputCommitter为默认值,表示将建立作业的最终输出目录及任务输出的临时工作空间
任务的分配
如果作业不适合作为uber任务(小作业)运行,那么application master就会为该作业中的所有map任务和reduce任务向资源管理器请求容器(步骤8)。首先为Map任务发出请求,该请求优先级要高于reduce任务的请求,因为所有的map任务必须在reduce的排序阶段能够启动前完成。直到有5%的map任务已经完成时,为reduce任务的请求才会发出。
map任务的请求有数据本地化局限,理想情况下,任务在分片驻留的同一节点上运行,可选情况是,任务可能是机架本地化的,即和分片在同一机架而非同一节点上运行。reduce任务能在集群中任意位置运行。
请求也为任务指定内存需求和CPU数。默认,每个map任务和reduce任务都分配1024MB的内存和一个虚拟内核,分别通过4个属性来设置mapreduce.map.memory.mb、mapreduce.reduce.memory.mb、mapreduce.map.cpu.vcores、mapreduce.reduce.cpu.vcoresp.memory.mb
任务的执行
一旦资源管理器的调度器为任务分配了一个特定节点上的容器,application master就通过与节点管理器通信来启动容器(步骤9a和9b)。该任务由主类为YarnChild的一个Java应用程序执行。在它运行任务之前,首先将任务需要的资源本地化,包括作业的配置、jar文件和所有来自分布式缓存的文件(步骤10)。最后,运行map任务或reduce任务(步骤11)
YarnChild在指定的JVM运行,因此用户定义的map或reduce函数(甚至YarnChild)中的任何缺陷不会影响到节点管理器
每个任务都能够执行搭建(setup)和提交(commit)动作,并由作业的OutputCommitter确定。对于基于文件的作业,提交动作将任务输出由临时位置搬移到最终位置。提交协议确保当推测执行被启用时,只有一个任务副本被提交,其他都被取消
进度和状态的更新
对于map任务,任务进度是已处理输入所占的比例
对于reduce任务,整个过程分为三部分,与shuffle的三个阶段相对应。
比如,如果任务已经执行了reducer一半的输入没那么任务的进度便是5/6,这是因为已经完成复制和排序阶段(每个占1/3),并且已经完成reduce阶段的一半(1/6)
当map或reduce任务运行时,子进程每隔3秒钟通过接口通信向application master报告进度和状态,application master会形成一个作业的汇聚视图
而客户端则每秒钟轮询一次application master以接收最新状态
失败
任务运行失败
任务挂起:一旦application master注意到已经有一段时间没有收到进度的更新,便会将任务标记为失败。在此之后,任务JVM进程将被自动杀死。任务被认为失败的超时时间间隔默认为10分钟
application master在一个任务尝试失败后,将重新调度该任务的执行,并且会视图避免在以前失败过的节点管理器上重新调度该任务。此外,如果一个任务失败过4此,将不会再重试
application master运行失败
application master失败最多尝试次数默认为2次。恢复过程如下,application master向资源管理器发送周期性的心跳,当application master失败,资源管理器在一个新的容器中开始一个新的master示例,新的application master将使用作业历史来恢复失败的应用程序所运行任务的状态,使其不必重新运行
客户端向application master轮询进度报告时,在作业初始化期间,客户端向资源管理器询问并缓存application master地址,其后查询时不必重载资源管理器。但如果application master运行失败,客户端就会在发出状态更新请求时超时,这时客户端会折回向资源管理器请求新application master地址
节点管理器运行失败
节点管理器每隔10分钟(yarn.resourcemanager.nm.liveness-monitor.expiry-interval-ms)向资源管理器发送心跳信息
shuffle
系统经过排序、将map输出作为输入传给reducer的过程称为shuffle
map端
每个map任务都有一个环形内存缓冲区用于存储任务输出,默认大小为100MB,一旦缓冲内容达到阈值(默认80%),一个后台线程便开始把内容溢出(spill)到磁盘,将缓冲区的内容写到mapreduce.cluster.local.dir属性在作业特定子>目录下指定的目录中。在溢出写磁盘过程中,map输出继续写到缓冲区,如果此期间缓冲区被填满,map会被阻塞直到写磁盘过程完成
在写磁盘前,线程首先根据数据要传的reducer把数据划分成相应的分区(partition)。在每个分区中,后台线程按键进行内存中排序,如果有一个conbiner函数,它就在排序后的输出上运行。运行combiner函数使得map输出结果更紧凑,因此减少写到磁盘的数据和传递给reducer的数据
在任务完成之前,溢出文件被合并成一个已分区且已排序的输出文件。默认一次最多合并10个文件。如果至少存在3个溢出文件,则combiner会在输出文件写到磁盘之前再次运行。[我的理解,溢出写到磁盘时第一次combiner,溢出文件已经在磁盘里,等map处理完记录,溢出文件会执行一次合并,生成输出文件再写到磁盘,这里会进行第二次combiner]。通过反复的combiner,使得最终的输出文件只有1或2个
将map输出写到磁盘过程中进行压缩是一个好主意,因为这样会写磁盘的速度更快、节约磁盘空间、并减少传给reducer的数据量
细节:
在map task执行时,它的输入数据来源于HDFS的block,当然在MapReduce概念中,map task只读取split,Split与block的对应关系可能是多对一,默认是一对一
当map task真正完成时,内存缓冲区中的数据也全部溢写到磁盘中形成一个溢写文件
reduce端
每个reduce task不断的通过RPC从JobTracker[书上说的是application master]那里获取map task是否完成的信息
copy阶段:reducer进程启动一些数据copy线程,通过HTTP请求Map task所在的task tracker获取map task的输出文件
merge阶段:Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置,因为Shuffle阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。1)内存到内存 2)内存到磁盘 3)磁盘到磁盘。默认情况下第一种形式不启用,当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是spill的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的spill文件,第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。
Reducer的输入文件,不断地merge后,最后会生成一个“最终文件”。为什么加引号?因为这个文件可能存在于磁盘上,也可能存在于内存中。对我们来说,当然希望它存放于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的,当Reducer的输入文件已定,整个Shuffle才最终结束,然后就是Reducer执行,把结果放到HDFS上
Reducer在拷贝数据的时候只需拷贝与自己对应的partition中的数据
shuffle的map中间结果分发给reducer的策略
shuffle的时候,记录会根据key进行partition,相同的key被分配到一个分区。
map的输出文件溢出到磁盘时,溢出文件时分区的,根据reduce task个数而定,并且溢出文件内部是有序的
将多个溢出文件的每个分区合并(归并排序)后分发给对应的reduce task
默认的,map使用Hash算法、根据reduce的个数,对key值进行Hash计算,这样保证了相同key值的数据能够划分到相同的partition中,也保证了不同partition之间的数据量大致相当
MR计算框架会将不同的数据划分成不同的partition,数据相同的多个partition最后会分到同一个reduce节点上面进行处理,也就是说一类partition对应一个reduce
数据倾斜
就是大量的相同key被partition分配到一个分区里,map /reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长,
这是因为某一个key的条数比其他key多很多(有时是百倍或者千倍之多),这条key所在的reduce节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完
调优原则
总的原则是给shuffle尽可能多的提供内存空间。写map函数和reduce函数时尽可能少使用内存,尽量避免在函数中堆积数据
在map端,可以通过避免多次溢出写磁盘来获得最佳性能,一次是最佳情况
在reduce端,中间数据驻留在内存时,是最佳情况
推测执行
当一个任务运行比预期慢的时候,它会尽量检测,并启动一个相同的任务做备份,这就是所谓的“推测执行”。推测执行能减少作业的执行时间,并以集群效率为代价
首先,在中启动所有任务。为那些已经运行了一段时间(至少一分钟)且比作业中其他任务平均进度慢的任务启动推测任务。如果原始任务在推测性任务之前完成,那么推测任务将被终止,相反,如果推测性任务在原始任务之前完成,那么原始任务被终止。一个任务成功完成之后,任何正在运行的重复任务都将被终止
即使开启了,如果没有任务运行时间低于预期,那么可能不会有推测执行任务产生
outputCommitter作业完成时的文件移动
MapReduce使用一个提交协议来确保作业(job)和任务(task)都完全成功或失败。这个通过 OutputCommiter来实现。新版本 MapReduce API中,默认为FileOutputCommitter。它会创建最终的输出目录${mapreduce.output.fileoutputformat.outputdir},并为任务的输出创建一个临时文件夹 _temporary,作为最终输出目录的子目录
如果作业成功,则调用 commitJob() 方法。此方法会做临时文件的清理(cleanupJob()),并在最终输出目录中创建名为_SUCCESS的文件,表示Job成功执行完成。若是Job 执行失败,则被状态对象调用abortJob(),默认会调用 cleanupJob() 的方法,对临时文件进行清理
如果task成功执行,并且有输出,则会调用commitTask() 方法,(默认的实现为)将临时目录下的输出文件移动到最终目录(mapreduce.output.fileoutputformat.outputdir)。若是执行失败,则调用abortTask(),删除任务输出的临时目录及文件
执行框架会保证一个task在有多次尝试的情况下,仅有一个task会被提交
在 Hadoop 2 中,commitTask 会将 task 的输出文件从 task 的临时目录移动到 job 的临时目录下。
在所有 task 任务完成后,commitJob 将生成的数据从 job 的临时目录移动到最终的 job 目录下。这个工作在 spark 中由 driver 完成
mapreduce的类型与格式
默认的partitioner是HashPartitioner,它对每条记录的键进行哈希操作以决定该记录应该属于哪个分区。每个分区由一个reduce任务处理,所以分区数等于作业的reduce任务个数
map任务的数量等于输入文件被划分成的分块数,这取决于输入文件的大小以及输入文件块的大小(如果此文件在HDFS中)
输入分片
一个输入分片就是一个由单个map操作来处理的输入块。客户端先调用方法计算分片,然后将他们发送到application master,application master使用其存储位置信息来调度map任务map任务从而在集群上处理分片数据。map任务处理分片,得到分片的一个迭代器
小文件
FileInputFormat生成的分块是一个文件或该文件的一部分,即一个块不能包含两个文件的数据。如果有很多小文件,那么就会有很多map任务,造成很多额外的开销
Hadoop集群
回收站
hadoop文件系统的文件被删除后,会先转移到回收站中,并保留一段时间。由core-site.xml里的fs.trash.interval设置。默认为0,表示回收站无效
当回收站被启用时,每个用户都有独立的回收站目录,即:home目录下的.Trash目录。恢复文件,只需要在.Trash的子目录中找到文件毛病移出.Trash目录
安全
ldap
LDAP是轻量目录访问协议,协议就是标准,并且是抽象的。使用树状结构,底层是B/B+树数据结构,数据存储在叶子节点上。
AD(Active Directory)是微软出的一套实现,可以把LADP理解成存储数据的数据库。LDAP也是有client端和server端, server端是用来存放资源,client端用来操作增删改查等操作
Kerberos
kerberos 主要是用来做网络通信时候的身份认证。
kerberos存了所有主体(client/TGT/service)的帐号和密码,因此也有一个数据库
相关概念
-
principal
认证的主体,简单来说就是”用户名” -
realm
realm有点像编程语言中的namespace ,realm可以看成是principal的一个”容器”或者”空间”。相对应的,principal的命名规则是”what_name_you_like@realm”。
在kerberos, 大家都约定成俗用大写来命名realm, 比如”EXAMPLE.COM” -
credential
credential是“证明某个人确定是他自己/某一种行为的确可以发生”的凭据。在不同的使用场景下, credential的具体含义也略有不同:
对于某个principal个体而言,他的credential就是他的password。
在kerberos认证的环节中,credential就意味着各种各样的ticket
这个principal的对应物可以是service,可以是host,也可以是user,对于Kerberos来说,都没有区别。
Kdc(Key distribute center)知道所有principal的secret key,但每个principal对应的对象只知道自己的那个secret key。 通过下面的博客可以知道,接入了kerberos的服务也会有服务id-服务密钥,TGS-TGS密钥,而不仅仅是存了client-client密钥
架构
一个kdc server包含
- krb5-admin-server: kdc管理员程序,可以让使用者远程管理kdc数据库。
- krb5-kdc:kdc主程序
- krb5-user: kerberos的一些客户端命令,用来获取、查看、销毁ticket等等
配置包含:
- /etc/krb5.conf:kdc配置
- /etc/krb5.keytab:用户的密钥存放的文件
kinit
kinit -k -t /etc/krb5.keytab test-client/myhost@EXAMPLE.COM
使用kinit工具来进行服务器认证,它会向/etc/krb5.conf中指定的kdc server来发送请求。
如果TGT请求成功,你就可以用klist看到它
原理参考:
https://blog.youkuaiyun.com/sky_jiangcheng/article/details/81070240
https://blog.youkuaiyun.com/nosqlnotes/article/details/79495978
https://blog.youkuaiyun.com/dog250/article/details/5468741
Sentry
Cloudera公司发布的一个Hadoop安全开源组件 ,提供了细粒度级、基于角色的授权.
优点:
Sentry支持细粒度的hdfs元数据访问控制,对hive支持列级别的访问控制
Sentry通过基于角色的授权简化了管理,将访问同一数据集的不同特权级别授予多个角色
Sentry提供了一个统一平台方便管理
Sentry支持集成Kerberos
Sentry 组件
- Sentry Server
Sentry的RPC服务管理授权元数据。它支持安全检索和操作元数据的接口。在CDH5.13及更高版本中,您可以配置多个Sentry服务以实现高可用性 ,内部含数据库 - Data Engine
这是一个数据处理应用程序,比如Hive或Impala,它们需要授权访问数据或元数据资源。数据引擎(data engine)加载Sentry插件,拦截所有客户端访问资源的请求并将其路由到Sentry插件进行验证 - Sentry Plugin
Sentry plugin在data engine中运行。它提供了操作存储在Sentry Server中的授权元数据的接口,包括授权策略引擎,该引擎使用从服务器检索的授权元数据来评估访问请求
sentry与hadoop集成
Apache Sentry可以与多个Hadoop组件一起工作。从本质上讲,您拥有存储授权元数据的Sentry Server,并提供API工具以安全地检索和修改此元数据
Sentry Server主要用于管理元数据。实际的授权决策由在Hive或Impala等数据处理应用程序中运行的策略引擎判断。每个组件都加载Sentry插件,其中包括用于处理Sentry服务的客户端和用于验证授权请求的策略引擎
hive和sentry
hdfs和sentry
对于hadoop
hadoop服务器的账户、LDAP的账户、kerberos的账户他们三者应该是同步的,怎么保证呢?
1)用户使用kerberos,会传入用户名即Principal,访问HDFS时,Hadoop 可以通过配置 hadoop.security.auth_to_local,将 Principal 映射为系统中的 OpenLDAP 的用户。用户注册LDAP时,平台会为每一个新注册的用户生成 Principal 以及相对应的 Keytab 文件。这样,用户就会使用Principal对应的keytab文件进行认证
2)hadoop可以使用LdapGroupsMappings 同步 LDAP 创建的用户和用户组,这样当我们在 LDAP 中添加用户和组时,会自动同步到 Hadoop 集群内的所有机器上
之前我们提数做项目帐号的时候,也是先在LDAP创建组,并在组下创建帐号,然后在hue执行命令,给角色授权,并把角色赋给组,这样用户就有了组的权限
Create database risk_control_dw_test;
Create database risk_control_dw;
Create role risk_control_dw_test;
Create role risk_control_dw;
grant role risk_control_dw to group risk_control_dw;
Grant select on database risk_control_dw_test to role risk_control_dw_test
Grant all on database dw_test to role risk_control_dw;
Grant all on database risk_control_dw_test to role risk_control_dw;
Grant all on database risk_control_dw to role risk_control_dw;
LDAP 用来做账号管理,Kerberos作为服务认证。
Kerberos只有账号密码概念,而无帐号管理的概念
hdfs
特点
1⃣️由于namenode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总数受限于n