大数据面试常见问题
Hadoop
HDFS读写流程
HDFS写流程:
-
Client客户端发送上传请求,通过RPC与NameNode建立通信,NameNode检查该用户是否有上传的权限,以及上传的文件是否在HDFS对应的目录下重名,如果这两者有任意一个不满足,则直接报错,如果两个都满足,则返回给客户端一个可以上传的信息;
-
Client客户端根据文件的大小进行切分,默认128M一块,切分完成之后给NameNode发送请求第一个block块上传到哪些服务器上;
-
NameNode收到请求之后,根据网络拓扑和机架感知以及副本机制进行文件分配,返回可用的DataNode的地址;注:Hadoop在设计时考虑到数据的安全与高效, 数据文件默认在HDFS上存放三份, 存储策略为本地一份,同机架内其它某一节点上一份, 不同机架的某一节点上一份。
-
客户端收到地址之后与服务器地址列表中的一个节点如A进行通信,本质就是RPC调用,建立pipeline(管道),A收到请求之后继续调用B,B再调用C,将整个pipeline建立完成,逐级返回Client;
-
Client开始向A上发送第一个block(先从磁盘读取数据然后放到本地内存缓存 ),以packet(数据包,64kb)为单位 ,A收到一个packet就会放松给B,然后B发送给C,A每传完一个packet就会放入一个应答队列等待应答;
-
数据被分割成一个个的packet数据包在pipeline上依次传输,在pipeline反向传输中逐个发送ack(命令正确应答),最终由pipeline中第一个DataNode节点A将pipeline ack发送给Client;
-
当一个block传输完成之后, Client再次请求NameNode上传第二个block,NameNode重新选择三台DataNode给Client。
HDFS读流程:
- Client向NameNode发送RPC请求。请求文件block的位置;
- NameNode收到请求之后会检查用户权限以及是否有这个文件,如果都符合,则会视情况返回部分或全部的block列表,对于每个block,NameNode都会返回含有该block副本的DataNode地址;这些返回的DataNode地址,会按照集群拓扑结构得出DataNode与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离 Client 近的排靠前;心跳机制中超时汇报的DataNode状态为STALE,这样的排靠后;
- Client选取排序靠前的DataNode来读取block,如果客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性);
- 底层上本质是建立Socket Stream(套接字流)(FSDataInputStream),重复的调用父类DataInputStream的read方法,直到这个块上的数据读取完毕;
- 当读完列表的block后,若文件读取还没有结束,客户端会继续向NameNode 获取下一批的block列表;
- 读取完一个block都会进行checksum验证,如果读取DataNode时出现错误,客户端会通知NameNode,然后再从下一个拥有该block副本的DataNode 继续读;
- read方法是并行的读取block信息,不是一块一块的读取 ;NameNode只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据;
- 最终读取来所有的block会合并成一个完整的最终文件;
HDFS在读取文件的时候,如果其中一个块突然损坏了怎么办
客户端在读取完DataNode上的块之后会进行checksum验证,也就是把客户端读取到本地的块与HDFS上的原始块进行比较校验,如果发现校验结果不一致,客户端会通知NameNode,然后再从下一个拥有该block副本的DataNode继续读。
HDFS在上传文件的时候,如果其中一个DataNode突然挂掉了怎么办
客户端在上传文件时与DataNode建立pipeline管道,管道的正方向是客户端向DataNode发送的数据包,管道反向是DataNode向客户端发送的ack确认,也就是正确接收到数据包之后发送一个已确认接收到的应答。
当DataNode突然挂掉时,客户端接收不到这个DataNode发送的ack确认,客户端会通知NameNode,NameNode检查该块副本与规定的不符,NameNode会通知DataNode去复制副本,并将挂掉的DataNode做下线处理,不再让它参与文件的上传与下载。
NameNode在启动的时候会做哪些操作
NameNode数据存储在内存和本地磁盘,本地磁盘数据存储在fsimage惊喜那个文件和edits编辑日志文件。
首次启动NameNode:
-
格式化文件系统,为了生成fsimage镜像文件;
-
启动NameNode:
- 读取fsimage文件,将文件内容加载进内存;
- 等待DateNode注册与发送block report。
-
启动DataNode:
- 向NameNode注册
- 发送block report
- 检查fsimage中记录的块的数量和block report中的块的总数是否相同
-
对文件系统进行操作(创建目录,上传文件,删除文件等);
此时内存中已经有文件系统改变的信息,但是磁盘中文件系统改变的信息,此时会将这些改变信息写入edits文件中,edits文件中存储的是文件系统元数据改变的信息。
第二次启动NameNode:
- 读取fsimage和edits文件;
- 将fsimage和edits文件合并成新的fsimage文件;
- 创建新的edits文件,内容开始为空;
- 启动DataNode。
Secondary NameNode的工作机制是怎样的
Secondary NameNode是合并NameNode的edit logs到fsimage文件中;
它的具体工作机制:
-
Secondary NameNode询问NameNode是否需要checkpoint。直接带回NameNode是否检查结果;
-
Secondary NameNode请求执行checkpoint;
-
NameNode滚动正在写的edits日志;
-
将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode;
-
Secondary NameNode加载编辑日志和镜像文件到内存,并合并;
-
生成新的镜像文件fsimage.chkpoint;
-
拷贝fsimage.chkpoint到NameNode;
-
NameNode将fsimage.chkpoint重新命名成fsimage;
所以如果NameNode中的元数据丢失,是可以从Secondary NameNode恢复一部分元数据信息的,但不是全部,因为NameNode正在写的edits日志还没有拷贝到Secondary NameNode,这部分恢复不了。
Secondary NameNode不能恢复NameNode的全部数据,那如何保证NameNode数据存储安全
这个问题就要说NameNode的高可用了,即 NameNode HA。
一个NameNode有单点故障的问题,那就配置双NameNode,配置有两个关键点,一是必须要保证这两个NameNode的元数据信息必须要同步的,二是一个NameNode挂掉之后另一个要立马补上。
元数据信息同步在 HA 方案中采用的是"共享存储"。每次写文件时,需要将日志同步写入共享存储,这个步骤成功才能认定写文件成功。然后备份节点定期从共享存储同步日志,以便进行主备切换。
监控NameNode状态采用zookeeper,两个NameNode节点的状态存放在zookeeper中,另外两个NameNode节点分别有一个进程监控程序,实施读取zookeeper中有NameNode的状态,来判断当前的NameNode是不是已经down机。如果Standby的NameNode节点的ZKFC发现主节点已经挂掉,那么就会强制给原本的Active NameNode节点发送强制关闭请求,之后将备用的NameNode设置为Active。
在NameNode HA中,会出现脑裂问题吗?怎么解决脑裂
脑裂:假设 NameNode1 当前为 Active 状态,NameNode2 当前为 Standby 状态。如果某一时刻 NameNode1 对应的 ZKFailoverController 进程发生了"假死"现象,那么 Zookeeper 服务端会认为 NameNode1 挂掉了,根据前面的主备切换逻辑,NameNode2 会替代 NameNode1 进入 Active 状态。但是此时 NameNode1 可能仍然处于 Active 状态正常运行,这样 NameNode1 和 NameNode2 都处于 Active 状态,都可以对外提供服务。这种情况称为脑裂。
脑裂对于NameNode这类对数据一致性要求非常高的系统来说是灾难性的,数据会发生错乱且无法恢复。zookeeper社区对这种问题的解决方法叫做 fencing,中文翻译为隔离,也就是想办法把旧的 Active NameNode 隔离起来,使它不能正常对外提供服务。
在进行 fencing 的时候,会执行以下的操作:
-
首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态。
-
如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施,通常会选择 sshfence:
-
sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死;
-
shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离。
小文件过多会有什么危害,如何避免
Hadoop上大量HDFS元数据信息存储在NameNode内存中,因此过多的小文件必定会压垮NameNode的内存。
每个元数据对象约占150byte,所以如果有1千万个小文件,每个文件占用一个block,则NameNode大约需要2G空间。如果存储1亿个文件,则NameNode需要20G空间。
显而易见的解决这个问题的方法就是合并小文件,可以选择在客户端上传时执行一定的策略先合并,或者是使用Hadoop的**CombineFileInputFormat<K,V>**实现小文件的合并。
请说下HDFS的组织架构
- Client:客户端
-
切分文件。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行存储
-
与NameNode交互,获取文件的位置信息
-
与DataNode交互,读取或者写入数据
-
Client提供一些命令来管理HDFS,比如启动关闭HDFS、访问HDFS目录及内容等
- NameNode:名称节点,也称主节点,存储数据的元数据信息,不存储具体的数据
-
管理HDFS的名称空间
-
管理数据块(Block)映射信息
-
配置副本策略
-
处理客户端读写请求
- DataNode:数据节点,也称从节点。NameNode下达命令,DataNode执行实际的操作
-
存储实际的数据块
-
执行数据块的读/写操作
- Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务
-
辅助NameNode,分担其工作量
-
定期合并Fsimage和Edits,并推送给NameNode
-
在紧急情况下,可辅助恢复NameNode
请说下MR中Map Task的工作机制
简单概述:
inputFile通过split被切割为多个split文件,通过Record按行读取内容给map(自己写的处理逻辑的方法) ,数据被map处理完之后交给OutputCollect收集器,对其结果key进行分区(默认使用的hashPartitioner),然后写入buffer,每个map task 都有一个内存缓冲区(环形缓冲区),存放着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式溢写到磁盘,当整个map task 结束后再对磁盘中这个maptask产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task的拉取。
详细步骤:
-
读取数据组件 InputFormat (默认 TextInputFormat) 会通过 getSplits 方法对输入目录中的文件进行逻辑切片规划得到 block,有多少个 block就对应启动多少个 MapTask。
-
将输入文件切分为 block 之后,由 RecordReader 对象 (默认是LineRecordReader) 进行读取,以 \n 作为分隔符, 读取一行数据, 返回 <key,value>, Key 表示每行首字符偏移值,Value 表示这一行文本内容。
-
读取 block 返回 <key,value>, 进入用户自己继承的 Mapper 类中,执行用户重写的 map 函数,RecordReader 读取一行这里调用一次。
-
Mapper 逻辑结束之后,将 Mapper 的每条结果通过 context.write 进行collect数据收集。在 collect 中,会先对其进行分区处理,默认使用 HashPartitioner。
-
接下来,会将数据写入内存,内存中这片区域叫做环形缓冲区(默认100M),缓冲区的作用是 批量收集 Mapper 结果,减少磁盘 IO 的影响。我们的 Key/Value 对以及 Partition 的结果都会被写入缓冲区。当然,写入之前,Key 与 Value 值都会被序列化成字节数组。
-
当环形缓冲区的数据达到溢写比列(默认0.8),也就是80M时,溢写线程启动,需要对这 80MB 空间内的 Key 做排序 (Sort)。排序是 MapReduce 模型默认的行为,这里的排序也是对序列化的字节做的排序。
-
合并溢写文件,每次溢写会在磁盘上生成一个临时文件 (写之前判断是否有 Combiner),如果 Mapper 的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个临时文件存在。当整个数据处理结束之后开始对磁盘中的临时文件进行 Merge 合并,因为最终的文件只有一个写入磁盘,并且为这个文件提供了一个索引文件,以记录每个reduce对应数据的偏移量。
请说下MR中Reduce Task的工作机制
简单描述:
Reduce 大致分为 copy、sort、reduce 三个阶段,重点在前两个阶段。
copy 阶段包含一个 eventFetcher 来获取已完成的 map 列表,由 Fetcher 线程去 copy 数据,在此过程中会启动两个 merge 线程,分别为 inMemoryMerger 和 onDiskMerger,分别将内存中的数据 merge 到磁盘和将磁盘中的数据进行 merge。待数据 copy 完成之后,copy 阶段就完成了。
开始进行 sort 阶段,sort 阶段主要是执行 finalMerge 操作,纯粹的 sort 阶段,完成之后就是 reduce 阶段,调用用户定义的 reduce 函数进行处理。
详细步骤:
-
Copy阶段:简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求maptask获取属于自己的文件(map task 的分区会标识每个map task属于哪个reduce task ,默认reduce task的标识从0开始)。
-
Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
-
merge有三种形式:内存到内存;内存到磁盘;磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值,就直接启动内存到磁盘的merge。与map端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。内存到磁盘的merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的文件。
-
合并排序:把分散的数据合并成一个大的数据后,还会再对合并后的数据排序。
-
对排序后的键值对调用reduce方法:键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到HDFS文件中。
请说下MR中Shuffle阶段
shuffle阶段分为四个步骤:依次为:分区,排序,规约,分组,其中前三个步骤在map阶段完成,最后一个步骤在reduce阶段完成。
shuffle 是 Mapreduce 的核心,它分布在 Mapreduce 的 map 阶段和 reduce 阶段。一般把从 Map 产生输出开始到 Reduce 取得数据作为输入之前的过程称作 shuffle。
-
Collect阶段:将 MapTask 的结果输出到默认大小为 100M 的环形缓冲区,保存的是 key/value,Partition 分区信息等。
-
Spill阶段:当内存中的数据量达到一定的阀值的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了 combiner,还会将有相同分区号和 key 的数据进行排序。
-
MapTask阶段的Merge:把所有溢出的临时文件进行一次合并操作,以确保一个 MapTask 最终只产生一个中间数据文件。
-
Copy阶段:ReduceTask 启动 Fetcher 线程到已经完成 MapTask 的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上。
-
ReduceTask阶段的Merge:在 ReduceTask 远程复制数据的同时,会在后台开启两个线程对内存到本地的数据文件进行合并操作。
-
Sort阶段:在对数据进行合并的同时,会进行排序操作,由于 MapTask 阶段已经对数据进行了局部的排序,ReduceTask 只需保证 Copy 的数据的最终整体有效性即可。
Shuffle 中的缓冲区大小会影响到 mapreduce 程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。
缓冲区的大小可以通过参数调整, 参数: mapreduce.task.io.sort.mb 默认100M
Shuffle阶段的数据压缩机制
在shuffle阶段,可以看到数据通过大量的拷贝,从map阶段输出的数据,都要通过网络拷贝,发送到reduce阶段,这一过程中,涉及到大量的网络IO,如果数据能够进行压缩,那么数据的发送量就会少得多。
hadoop当中支持的压缩算法:
gzip、bzip2、LZO、LZ4、Snappy,这几种压缩算法综合压缩和解压缩的速率,谷歌的Snappy是最优的,一般都选择Snappy压缩。谷歌出品,必属精品。
在写MR时,什么情况下可以使用规约
规约(combiner)是不能够影响任务的运行结果的局部汇总,适用于求和类,不适用于求平均值,如果reduce的输入参数类型和输出参数的类型是一样的,则规约的类可以使用reduce类,只需要在驱动类中指明规约的类即可。
YARN集群的架构和工作原理知道多少
YARN的基本设计思想是将MapReduce V1中的JobTracker拆分为两个独立的服务:ResourceManager和ApplicationMaster。
ResourceManager负责整个系统的资源管理和分配,ApplicationMaster负责单个应用程序的的管理。
ResourceManager: RM是一个全局的资源管理器,负责整个系统的资源管理和分配,它主要由两个部分组成:调度器(Scheduler)和应用程序管理器(Application Manager)。
调度器根据容量、队列等限制条件,将系统中的资源分配给正在运行的应用程序,在保证容量、公平性和服务等级的前提下,优化集群资源利用率,让所有的资源都被充分利用应用程序管理器负责管理整个系统中的所有的应用程序,包括应用程序的提交、与调度器协商资源以启动ApplicationMaster、监控ApplicationMaster运行状态并在失败时重启它。
ApplicationMaster: 用户提交的一个应用程序会对应于一个ApplicationMaster,它的主要功能有:
与RM调度器协商以获得资源,资源以Container表示。
将得到的任务进一步分配给内部的任务。
与NM通信以启动/停止任务。
监控所有的内部任务状态,并在任务运行失败的时候重新为任务申请资源以重启任务。
NodeManager: NodeManager是每个节点上的资源和任务管理器,一方面,它会定期地向RM汇报本节点上的资源使用情况和各个Container的运行状态;另一方面,他接收并处理来自AM的Container启动和停止请求。
Container: Container是YARN中的资源抽象,封装了各种资源。一个应用程序会分配一个Container,这个应用程序只能使用这个Container中描述的资源。不同于MapReduceV1中槽位slot的资源封装,Container是一个动态资源的划分单位,更能充分利用资源。
YARN的任务提交流程是怎样的
当jobclient向YARN提交一个应用程序后,YARN将分两个阶段运行这个应用程序:一是启动ApplicationMaster;第二个阶段是由ApplicationMaster创建应用程序,为它申请资源,监控运行直到结束。 具体步骤如下:
-
用户向YARN提交一个应用程序,并指定ApplicationMaster程序、启动ApplicationMaster的命令、用户程序。
-
RM为这个应用程序分配第一个Container,并与之对应的NM通讯,要求它在这个Container中启动应用程序ApplicationMaster。
-
ApplicationMaster向RM注册,然后拆分为内部各个子任务,为各个内部任务申请资源,并监控这些任务的运行,直到结束。
-
AM采用轮询的方式向RM申请和领取资源。
-
RM为AM分配资源,以Container形式返回。
-
AM申请到资源后,便与之对应的NM通讯,要求NM启动任务。
-
NodeManager为任务设置好运行环境,将任务启动命令写到一个脚本中,并通过运行这个脚本启动任务。
-
各个任务向AM汇报自己的状态和进度,以便当任务失败时可以重启任务。
-
应用程序完成后,ApplicationMaster向ResourceManager注销并关闭自己。
YARN的资源调度三种模型了解吗
在Yarn中有三种调度器可以选择:FIFO Scheduler ,Capacity Scheduler,Fair Scheduler。
Apache版本的hadoop默认使用的是Capacity Scheduler调度方式。CDH版本的默认使用的是Fair Scheduler调度方式
FIFO Scheduler(先来先服务):
FIFO Scheduler把应用按提交的顺序排成一个队列,这是一个先进先出队列,在进行资源分配的时候,先给队列中最头上的应用进行分配资源,待最头上的应用需求满足后再给下一个分配,以此类推。
FIFO Scheduler是最简单也是最容易理解的调度器,也不需要任何配置,但它并不适用于共享集群。大的应用可能会占用所有集群资源,这就导致其它应用被阻塞,比如有个大任务在执行,占用了全部的资源,再提交一个小任务,则此小任务会一直被阻塞。
Capacity Scheduler(能力调度器):
对于Capacity调度器,有一个专门的队列用来运行小任务,但是为小任务专门设置一个队列会预先占用一定的集群资源,这就导致大任务的执行时间会落后于使用FIFO调度器时的时间。
Fair Scheduler(公平调度器):
在Fair调度器中,我们不需要预先占用一定的系统资源,Fair调度器会为所有运行的job动态的调整系统资源。
比如:当第一个大job提交时,只有这一个job在运行,此时它获得了所有集群资源;当第二个小任务提交后,Fair调度器会分配一半资源给这个小任务,让这两个任务公平的共享集群资源。
需要注意的是,在Fair调度器中,从第二个任务提交到获得资源会有一定的延迟,因为它需要等待第一个任务释放占用的Container。小任务执行完成之后也会释放自己占用的资源,大任务又获得了全部的系统资源。最终的效果就是Fair调度器即得到了高的资源利用率又能保证小任务及时完成。
Hive
什么是数据仓库?
数据仓库是一个面向主题的、集成的、随时间变化的、但信息本身相对稳定的数据集合,用于对管理决策过程的支持。
面向主题:数据仓库都是基于某个明确主题,仅需要与该主题相关的数据,其他的无关细节数据将被排除掉
集成的:从不同的数据源采集数据到同一个数据源,此过程会有一些ETL操作
随时间变化:关键数据隐式或显式的基于时间变化
数据仓库的数据是不可更新的:数据装入以后一般只进行查询操作,没有传统数据库的增删改操作。数据仓库的数据反映的是一段相当长的时间内历史数据的内容,是不同时点的数据库快照的集合,以及基于这些快照进行统计、综合和重组的导出数据,而不是联机处理的数据。
数据库:是一种逻辑概念,用来存放数据的仓库。通过数据库软件来实现。数据库由很多表组成,表是二维的,一张表里可以有很多字段。字段一字排开,对应的数据就一行一行写入表中。数据库的表,在于能够用二维表现多维关系。目前市面上流行的数据库都是二维数据库。如:Oracle、DB2、MySQL、Sybase、MS SQL Server等。
数据仓库:是数据库概念的升级。从逻辑上理解,数据库和数据仓库没有区别,都是通过数据库软件实现的存放数据的地方,只不过从数据量来说,数据仓库要比数据库更庞大得多。数据仓库主要用于数据挖掘和数据分析,辅助领导做决策。 数据仓库的表结构是依照分析需求,分析维度,分析指标进行设计的。
数据仓库架构
贴源层:ODS (Operation data store) 直接接入源数据的:业务库、埋点日志、消息队列等
数据明细层:DWD(Data Warehouse Detail)业务层和数据仓库层的隔离层,保持和ODS层相同颗粒度,进行数据清洗和规范化操作:去空/脏数据、离群值等
数据中间层:DWM(Data WareHouse Middle)在DWD的基础上进行轻微的聚合操作、算出相应的统计指标、聚合之后会生成中间表
数据服务层:DWS(Data WareHouse Servce)在DWM的基础上,整合汇总成一个主题的数据服务层,汇总结果通常是宽表
数据应用层:ADS (Application data service)供数据分析和挖掘使用
宽表概念: 通常是指业务主题相关的指标、维度、属性关联在一起的一张数据库表。
优点:用户使用方便,通过一张表以及对应标签、维度的筛选就可以得到对应的数据。
缺点:如果遇到废弃字段、新增字段、修改逻辑等需求,数据开发人员维护成本较高。
拉链表概念:记录历史数据的每个状态,记录一个事物从开始,一直到当前状态的所有变化的信息;拉链表通常是对账户信息的历史变动进行处理保留的结果。
Hive内部表和外部表的区别
未被external修饰的是内部表,被external修饰的为外部表。
区别:
- 内部表数据由hive自身管理,外部表数据由HDFS管理;
- 内部数据存储的位置是hive.metastore.warehouse.dir(默认:/user/hive/warehouse),外部表数据的存储位置由自己指定(如果没有LOCATION,Hive将在HDFS上的/user/hive/warehouse文件夹下以外部表的表名创建一个文件夹,并将属于这个表的数据存放在这里);
- /user/hive/warehouse文件夹下以外部表的表名创建一个文件夹,并将属于这个表的数据存放在这里);
Hive有索引吗
Hive支持索引(3.0版本之前),但是Hive的索引与关系型数据库中的索引并不相同,比如,Hive不支持主键或者外键。并且Hive索引提供的功能很有限,效率也并不高,因此Hive索引很少使用。
索引适用的场景:
适用于不更新的静态字段。以免总是重建索引数据。每次建立、更新数据后,都要重建索引以构建索引表。
Hive索引的机制如下:
hive在指定列上建立索引,会产生一张索引表(Hive的一张物理表),里面的字段包括:索引列的值、该值对应的HDFS文件路径、该值在文件中的偏移量。
Hive 0.8版本后引入bitmap索引处理器,这个处理器适用于去重后,值较少的列(例如,某字段的取值只可能是几个枚举值) 因为索引是用空间换时间,索引列的取值过多会导致建立bitmap索引表过大。
注意:Hive中每次有数据时需要及时更新索引,相当于重建一个新表,否则会影响数据查询的效率和准确性,Hive官方文档已经明确表示Hive的索引不推荐被使用,在新版本的Hive中已经被废弃了。
扩展:Hive是在0.7版本之后支持索引的,在0.8版本后引入bitmap索引处理器,在3.0版本开始移除索引的功能,取而代之的是2.3版本开始的物化视图,自动重写的物化视图替代了索引的功能。
运维如何对Hive进行调度
- 将hive的sql定义在脚本当中;
- 使用azkaban或者oozie进行任务的调度;
- 监控任务调度页面。
ORC、Parquet等列式存储的优点
ORC和Parquet都是高性能的存储方式,这两种存储格式总会带来存储和性能上的提升。
Parquet
-
Parquet支持嵌套的数据模型,类似于Protocol Buffers,每一个数据模型的schema包含多个字段,每一个字段有三个属性:重复次数、数据类型和字段名。
重复次数可以是以下三种:required(只出现1次),repeated(出现0次或多次),optional(出现0次或1次)。每一个字段的数据类型可以分成两种: group(复杂类型)和primitive(基本类型)。
-
Parquet中没有Map、Array这样的复杂数据结构,但是可以通过repeated和group组合来实现的。
-
由于Parquet支持的数据模型比较松散,可能一条记录中存在比较深的嵌套关系,如果为每一条记录都维护一个类似的树状结可能会占用较大的存储空间,因此Dremel论文中提出了一种高效的对于嵌套数据格式的压缩算法:Striping/Assembly算法。通过Striping/Assembly算法,parquet可以使用较少的存储空间表示复杂的嵌套格式,并且通常Repetition level和Definition level都是较小的整数值,可以通过RLE算法对其进行压缩,进一步降低存储空间。
-
Parquet文件是以二进制方式存储的,是不可以直接读取和修改的,Parquet文件是自解析的,文件中包括该文件的数据和元数据。
ORC
-
ORC文件是自描述的,它的元数据使用Protocol Buffers序列化,并且文件中的数据尽可能的压缩以降低存储空间的消耗。
-
和Parquet类似,ORC文件也是以二进制方式存储的,所以是不可以直接读取,ORC文件也是自解析的,它包含许多的元数据,这些元数据都是同构ProtoBuffer进行序列化的。
-
ORC会尽可能合并多个离散的区间尽可能的减少I/O次数。
-
ORC中使用了更加精确的索引信息,使得在读取数据时可以指定从任意一行开始读取,更细粒度的统计信息使得读取ORC文件跳过整个row group,ORC默认会对任何一块数据和索引信息使用ZLIB压缩,因此ORC文件占用的存储空间也更小。
-
在新版本的ORC中也加入了对Bloom Filter的支持,它可以进一 步提升谓词下推的效率,在Hive 1.2.0版本以后也加入了对此的支持;
数据建模用的哪些模型?
星型模型
星形模式(Star Schema)是最常用的维度建模方式。星型模式是以事实表为中心,所有的维度表直接连接在事实表上,像星星一样。 星形模式的维度建模由一个事实表和一组维表成,且具有以下特点:
a. 维表只和事实表关联,维表之间没有关联;
b. 每个维表主键为单列,且该主键放置在事实表中,作为两边连接的外键;
c. 以事实表为核心,维表围绕核心呈星形分布。
雪花模型
雪花模式(Snowflake Schema)是对星形模式的扩展。雪花模式的维度表可以拥有其他维度表的,虽然这种模型相比星型更规范一些,但是由于这种模型不太容易理解,维护成本比较高,而且性能方面需要关联多层维表,性能比星型模型要低。
星座模型
星座模式是星型模式延伸而来,星型模式是基于一张事实表的,而星座模式是基于多张事实表的,而且共享维度信息。前面介绍的两种维度建模方法都是多维表对应单事实表,但在很多时候维度空间内的事实表不止一个,而一个维表也可能被多个事实表用到。在业务发展后期,绝大部分维度建模都采用的是星座模式。
为什么要对数据仓库分层?
-
用空间换时间,通过大量的预处理来提升应用系统的用户体验(效率),因此数据仓库会存在大量冗余的数据。
-
如果不分层的话,如果源业务系统的业务规则发生变化将会影响整个数据清洗过程,工作量巨大。
-
通过数据分层管理可以简化数据清洗的过程,因为把原来一步的工作分到了多个步骤去完成,相当于把一个复杂的工作拆成了多个简单的工作,把一个大的黑盒变成了一个白盒,每一层的处理逻辑都相对简单和容易理解,这样我们比较容易保证每一个步骤的正确性,当数据发生错误的时候,往往我们只需要局部调整某个步骤即可。
使用过Hive解析JSON串吗
Hive处理json数据总体来说有两个方向的路走:
-
将json以字符串的方式整个入Hive表,然后通过使用UDF函数解析已经导入到hive中的数据,比如使用LATERAL VIEW json_tuple的方法,获取所需要的列名。
-
在导入之前将json拆成各个字段,导入Hive表的数据是已经解析过的。这将需要使用第三方的 SerDe。
sort by 和 order by 的区别
-
order by 会对输入做全局排序,因此只有一个reducer(多个reducer无法保证全局有序)只有一个reducer,会导致当输入规模较大时,需要较长的计算时间。
-
sort by不是全局排序,其在数据进入reducer前完成排序. 因此,如果用sort by进行排序,并且设置mapred.reduce.tasks>1, 则sort by只保证每个reducer的输出有序,不保证全局有序。
怎么排查哪里出现数据倾斜
数据倾斜大多数都是某个key数据分布不均问题导致的,判断方法:
- 通过时间判断,如果某个reduce的时间比其他reduce的时间长得多,排除两种情况,a.reduce都特别长,可能是自己的reduce设置过少,b.可能某一个task节点出现了问题。
- 通过counter判断,counter会记录整个job以及每个task的统计信息。
- 通过输入记录数。
- 分析执行日志,作业在reduce阶段停留在99%,很长时间完成不了,很可能发生了数据倾斜。
造成数据倾斜的主要原因:
- group by逻辑造成
- distinct count(distinct xx)
- 小表join大表
- 大表join大表
数据倾斜产生原因主要有以下几点:数据key值分布不均,机器配置和数据量比例不合理,业务数据本身特性导致。
数据倾斜怎么解决
数据倾斜问题主要有以下几种:
-
空值引发的数据倾斜:实际业务中有些大量的null值或者一些无意义的数据参与到计算作业中,表中有大量的null值,如果表之间进行join操作,就会有shuffle产生,这样所有的null值都会被分配到一个reduce中,必然产生数据倾斜。解决方案:
第一种:可以直接不让null值参与join操作,即不让null值有shuffle阶段:增加空值条件判断
第二种:因为null值参与shuffle时的hash结果是一样的,那么我们可以给null值随机赋值,这样它们的hash结果就不一样,就会进到不同的reduce中
-
不同数据类型引发的数据倾斜:对于两个表join,表a中需要join的字段key为int,表b中key字段既有string类型也有int类型。当按照key进行两个表的join操作时,默认的Hash操作会按int型的id来进行分配,这样所有的string类型都被分配成同一个id,结果就是所有的string类型的字段进入到一个reduce中,引发数据倾斜。解决方案:
如果key字段既有string类型也有int类型,默认的hash就都会按int类型来分配,那我们直接把int类型都转为string就好了,这样key字段都为string,hash时就按照string类型分配了
-
不可拆分大文件引发的数据倾斜:当对文件使用GZIP压缩等不支持文件分割操作的压缩方式,在日后有作业涉及读取压缩后的文件时,该压缩文件只会被一个任务所读取。解决方案:
我们在对文件进行压缩时,为避免因不可拆分大文件而引发数据读取的倾斜,在数据压缩的时候可以采用bzip2和Zip等支持文件分割的压缩算法。
-
group by分组时key值分布不均匀
采用两阶段聚合的方式。两阶段聚合指的是先局部聚合再全局聚合。局部聚合是会给每个key值加上随机前缀进行打散,使其成为全新的key值,由原来单一的task处理编程多个task上做聚合,从而欢姐单个task处理数据量过多的问题。再去除前缀做全局聚合即可。
-
count distinct去重的时候key值分布不均
- 将空值部门与非空值部分拆分成两个部分单独处理后再做合并。
- 使用sum() group by来替代count(distinct )。
-
表连接时引发的数据倾斜:两表进行普通的repartition join时,如果表连接的键存在倾斜,那么在 Shuffle 阶段必然会引起数据倾斜。
将其中做连接的小表分发到所有的Map Task端join,从而避免reduce Task。也就是说在map端进行了join操作,省去了reduce阶段,没有shuffle过程,没有reduce任务,避免了数据倾斜。
-
确实无法减少数据量引发的数据倾斜
这类问题最直接的方式就是调整reduce所执行的内存大小。
调整reduce的内存大小使用
mapreduce.reduce.memory.mb这个配置。
Hive 小文件过多怎么解决
1. 使用 hive 自带的 concatenate 命令,自动合并小文件
使用方法:
hive
#对于非分区表alter table A concatenate;、
#对于分区表alter table B partition(day=20201224) concatenate;
注意:
1、concatenate 命令只支持 RCFILE 和 ORC 文件类型。
2、使用concatenate命令合并小文件时不能指定合并后的文件数量,但可以多次执行该命令。
3、当多次使用concatenate后文件数量不在变化,这个跟参数 mapreduce.input.fileinputformat.split.minsize=256mb 的设置有关,可设定每个文件的最小size。
2. 调整参数减少Map数量:设置map输入合并小文件的相关参数(执行Map前进行小文件合并)
3. 减少Reduce的数量:reduce 的个数决定了输出的文件的个数,所以可以调整reduce的个数控制hive表的文件数量。
4. 使用hadoop的archive将小文件归档:Hadoop Archive简称HAR,是一个高效地将小文件放入HDFS块中的文件存档工具,它能够将多个小文件打包成一个HAR文件,这样在减少namenode内存使用的同时,仍然允许对文件进行透明的访问。
Hive优化有哪些
-
**数据存储及压缩:**针对hive中表的存储格式通常有orc和parquet,压缩格式一般使用snappy。
相比与textfile格式表,orc占有更少的存储。因为hive底层使用MR计算架构,数据流是hdfs到磁盘再到hdfs,而且会有很多次,所以使用orc数据格式和snappy压缩策略可以降低IO读写,还能降低网络传输量,这样在一定程度上可以节省存储,还能提升hql任务执行效率;
-
通过调参优化:并行执行,调节parallel参数;调节jvm参数,重用jvm;设置map、reduce的参数;开启strict mode模式;关闭推测执行设置。
-
有效地减小数据集将大表拆分成子表,结合使用外部表和分区表。
-
SQL优化:大表对大表,尽量减少数据集,可以通过分区表,避免扫描全表或者全字段;大表对小表:设置自动识别小表,将小表放入内存中去执行。
Tez引擎优点
Tez可以将多个有依赖的作业转换为一个作业,这样只需写一次HDFS,且中间节点较少,从而大大提升作业的计算性能。
Mr/tez/spark区别:
Mr引擎:多job串联,基于磁盘,落盘的地方比较多。虽然慢,但一定能跑出结果。一般处理,周、月、年指标。
Spark引擎:虽然在Shuffle过程中也落盘,但是并不是所有算子都需要Shuffle,尤其是多算子过程,中间过程不落盘 DAG有向无环图。 兼顾了可靠性和效率。一般处理天指标。
Tez引擎:完全基于内存。 注意:如果数据量特别大,慎重使用。容易OOM。一般用于快速出结果,数据量比较小的场景。Spark
Spark
1. 通常来说,Spark与MapReduce相比,Spark运行效率更高。请说明效率更高来源于Spark内置的哪些机制?
- spark是基于内存计算的,mapreduce会将数据暂存在文件系统中,spark计算性能更好;
- spark的有向无环图可以减少shuffle,在不需要其他节点数据的情况下(窄依赖),可以在不落盘的情况下对数据进行连续计算,遇到宽依赖时,spark也会需要将数据落盘。
- spark是粗粒度的资源申请,在application启动之前资源就已经申请完成,task可以直接运行,启动更快,运行也就更快了,但是task要执行完之后才释放,集群资源得不到充分利用。mapreduce是细粒度资源申请,在task执行时自己申请资源,这样task启动慢,但是用完马上释放,集群利用率更高。
2.hadoop和spark的使用场景。
两者都适合做离线型数据分析,但是hadoop更适合做单词分析的数据量很大情况,spark更适合做数据量不是很大的。
- 一般情况下,对于中小互联网的企业级的大数据应用而言,单词分析的数据量都不会"很大",因此可以有限考虑spark。
- 业务通常认为spark更适用于机器学习之类的"迭代式"应用。
3.spark如何保证宕机迅速恢复?
- 适当增加spark standby master
- 编写shell脚本,定期检测master状态,出现宕机后对master进行重启操作
4.hadoop和spark的相同点与不同点
Hadoop底层使用MapReduce计算架构,只有map和reduce两种操作,表达能力比较欠缺,而且在MR过程中会重复的读写hdfs,造成大量的磁盘io读写操作,所以适合高时延环境下批处理计算的应用;
Spark是基于内存的分布式计算架构,提供更加丰富的数据集操作类型,主要分成转化操作和行动操作,包括map、reduce、filter、flatmap、groupbykey、reducebykey、union和join等,数据分析更加快速,所以适合低时延环境下计算的应用;
spark与hadoop最大的区别在于迭代式计算模型
spark的劣势在于真正面对大数据时,在没有调优的情况下,很可能会出现各种各样的问题,比如OOM内存溢出等情况,导致spark无法运行起来,而mapreduce虽然运行缓慢,但是可以慢慢运行完。
5.RDD持久化原理
这是spark一个非常重要的功能特性。调佣cache()和persist()方法即可。两个方法的区别在于cache()是persist()的一种简化方法,cache()的底层就是调用persist()的无参版本,将数据持久化到内存中。如果需要清除缓存,可使用unpersist()方法。
6.checkpoint检查点机制
应用场景:当spark应用程序特别复杂,从初始的RDD开始到最后整个应用程序完成有很多的步骤,而且整个应用运行时间特别长,这种情况下就比较适合使用checkpoint功能。
原因:由于复杂的spark应用可能导致出现反复使用的RDD,但可能由于节点故障导致数据丢失,没有容错机制,需要重新计算一次数据。
checkpoint是我们在spark streaming中用来保障容错性的主要机制。可以把应用数据存储到注入HDFS等可靠存储系统中,以供恢复时使用。基于以下两个目的服务:
- 控制发生失败时需要重算的状态数,
- 提供驱动器程序容错。
7.checkpoint和持久化机制的区别?
最主要的区别就是持久化机制是将数据持久化储存在内存中,可能会因为节点的故障导致内存里的数据丢失。checkpoint的数据通常是存储在高可用的文件系统中,如HDFS,数据丢失的可能性低。
持久化机制没有改变RDD的lineage关系,只是将数据保存在BlockManager中,但是checkpoint执行后,rdd已经没有之前所谓的依赖rdd了,只有一个强行设置的checkpointRDD,lineage就改变了。
8.RDD机制理解吗?
RDD是弹性分布式数据集,简单理解来说就是一种数据结构,是spark框架上的通用货币,所有算计都是基于rdd执行的,不同的场景有不同的rdd实现类,但是可以相互转换。rdd执行时会形成一个dag图,形成lineage保证容错性。
RDD在逻辑上是一个hdfs文件,在抽象上是一种元素集合,包含了数据。它是被分区的,分为多个分区,每个分区分布在集群中的不同结点上,从而让RDD中的数据可以被并行操作。
RDD的数据默认是存放在内存中的,但是当内存不足时,spark会自动将RDD数据写入磁盘中。弹性就体现于RDD自动进行内存与磁盘之间权衡和切换的机制。
9.Spark streaming以及基本工作原理
Spark streaming是spark core API的一种扩展,可以用于进行大规模、搞吞吐量、容错的实时数据流的处理,支持从多种数据源读取数据,比如kafka、flume、tcp socket等,并且能够使用算子,比如map、reduce、join等来处理数据,处理后的数据可以存储到文件系统、数据库等存储中。
Spark streaming内部的基本工作原理是:接受实时输入数据流,然后将数据拆分成batch,比如每收集一秒的数据封装成一个batch,然后将每个batch交给spark的计算引擎进行处理,最后会生产处一个结果数据流,其中的数据也是一个一个的batch组成的。
10.DStream以及其工作原理
-
DStream是spark streaming提供的一种高级抽象,代表了一个持续不断的数据流。
-
DStream可以通过输入数据源来创建,比如Kafka、flume等,也可以通过其他DStream的高阶函数来创建,比如map、reduce、join和window等。
-
DStream内部其实不断产生RDD,每个RDD包含了一个时间段的数据。
-
Spark streaming一定是有一个输入的DStream接收数据,按照时间划分成一个一个的batch,并转化为一个RDD,RDD的数据是分散在各个子节点的partition中。