大数据面试总结

大数据基础部分面试题:
一.你了解Hadoop吗?讲一下Hadoop中HDFS的读写原理。
读:
客户端调用FileSystem的open方法,来打开希望读取的文件。
FileSystem通过Rpc与namenode通信,namenode将获取到的信息整理,并将文件的所有内容发送给FileSystem对象,所有的副本块都会有对应的datanode位置信息;namenode会根据当前的所有节点状态判断最佳datanode位置,并且将此datanode的位置信息封装到传送的信息中。
客户端通过FileSystem返回的流对象,调用read方法,开始读取文件的数据,如果一个文件非常大,数据块数量庞大,namenode不会一次性返回所有的块信息,而是一批一批的返回,并且从第一批开始逐次读取。
对于客户端来讲,读取数据的操作是不透明的,用户并不需要关心底层流在数据块之间的跳转,或者读取失败后的流跳转,底层流对象会从第一个数据块开始,直接调用当前的FileSystem返回的流对象的read方法进行连续读取。
按照顺序每当读完一个数据块,底层流会跳转到下一个数据块读取。
当所有的数据块读取完毕,用户调用FSDataInputStream的close方法进行关流即可。

写:

  1. 客户端调用文件系统create()方法,创建一个新文件。
  2. FileSystem通过Rpc通信,与namenode沟通,在HDFS的命名空间创建一个空的文件名称,对应一条元数据记录,namenode会根据文件名称和客户端的操作权限进行校验,如果校验失败,则返回IO异常,如果校验成功就创建临时的数据记录。
  3. create()方法调用后,FileSystem对象返回一个流对象(DFSOutputStream),客户端调用流的write方法进行写入操作,数据源将会被底层流切分成一个一个64kb大小的package,并在内部以数据队列“dataqueue”的形式管理这些package。
  4. 保存在DataStreamer队列(dataqueue)里的package将会被写入到对应第一个数据块的最近datanode中,与此同时,package将会从dataqueue里移出,复制到ack queue中等待确认信息的反馈;在传输过程中,namenode返回的每个数据块副本所在的datanode都会彼此之间建立pipeline,写入到pipeline中的第一个datanode的package会通过双工通信,传到第二个,第二个传到第三个,并且每个datanode每次传完一个package都会放入一个到ack queue中。
  5. 每一个节点写入package的时候,在完成写入后会反馈一个当前package的写成信息,当最后一个datanode成功存储之后会返回一个ack package,ack queue成功收到datanode返回的ack package后,保存在ack queue中的package才会移出,到此为止才算一个package写入成功。
  6. 当所有的数据客户端调用write方法写完后会立刻调用close方法,将内部的所有缓存flush,这时留存在dataqueue里的package也会全部写入到pipeline中。
  7. 等到所有的写入完成,会判断当前的文件写入是否成功,判断的条件是所有文件的block的副本数量都达到了最小值(默认情况下是1);

二.Mapreduce 的Shuffle过程
在 Map 端的运作
Map function 的运行方式就是从 RecordReader 那边读出一个 input key value pair, 处理,然后把处理结果(通常也是 key value pair 形式)写到一个Hadoop maintained memory buffer 里,然后读取下一个 input key value pair.

Hadoop maintained memory buffer 里的 key value pair 按 key 值排序,并且按照 reduce partition 分到不同 partition 里(这就是 partitioner 被调用的时候)。一旦 memory buffer 满了,就会被 Hadoop 写到 file 里,这个过程叫 spill, 写出的 file 叫 spill file.

注意,这些 spill file 存在 map 所在 host 的 local disk 上,而不是我们之前介绍过的 HDFS.

随着 Map 不断运行,有可能有多个 spill file 被制造出来。当 Map 结束时,这些 spill file 会被 merge 起来——不是 merge 成一个 file,而是按 reduce partition 分成多个。

在 Reduce 端的运作:
由于 Map tasks 有可能在不同时间结束,所以 reduce tasks 没必要等所有 map tasks 都结束才开始。事实上,每个 reduce task 有一些 threads 专门负责从 map host copy map output(默认是5个,可以通过 $mapred.reduce.parallel.copies 参数设置);考虑到网络的延迟问题,并行处理可以在一定程度上提高效率。

通过前面的学习,我们知道 Hadoop JobTracker 会根据输入数据位置安排 map tasks,但 reduce tasks 是不知道这种安排的。那么当 reduce task 需要从map host copy map output 时,它怎么知道 map host 的位置呢(URL/IP)?

其实当 Map tasks 成功结束时,他们会通知负责的 tasktracker, 然后消息通过 jobtracker 的 heartbeat 传给 jobtracker. 这样,对于每一个 job, jobtracker 知道 map output 和 map tasks 的关联。Reducer 内部有一个 thread 负责定期向 jobtracker 询问 map output 的位置,直到 reducer 得到所有它需要处理的 map output 的位置。

Reducer 的另一个 thread 会把拷贝过来的 map output file merge 成更大的 file. 如果 map task 被 configure 成需要对 map output 进行压缩,那 reduce 还要对 map 结果进行解压缩。当一个 reduce task 所有的 map output 都被拷贝到一个它的 host上时,reduce 就要开始对他们排序了。

排序并不是一次把所有 file 都排序,而是分几轮。每轮过后产生一个结果,然后再对结果排序。最后一轮就不用产生排序结果了,而是直接向 reduce 提供输入。这时,用户提供的 reduce function 就可以被调用了。输入就是 map task 产生的 key value pairs.

三.NIO和IO的区别
NIO vs IO之间的理念上面的区别(NIO将阻塞交给了后台线程执行)
IO是面向流的,NIO是面向缓冲区的
Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;
NIO则能前后移动流中的数据,因为是面向缓冲区的
IO流是阻塞的,NIO流是不阻塞的
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
选择器
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
四.Kfaka中topic和partition的关系是什么,如何理解?
topic在逻辑上可以被认为是一个queue。每条消费都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得 Kafka的吞吐率可以水平扩展,物理上把topic分成一个或多个partition,每个partition在物理上对应一个文件夹,该文件夹下存储 这个partition的所有消息和索引文件。partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。

五.Mapreduce的shuffle过程
当MAP函数开始产生输出时,不是直接将文件写入到磁盘中,而是先将数据写入到缓存中,并对数据进行一系列的优化操作,以减少数据传输的量,并减少REDUCE端的操作。
首先每个MAP会有一个100MB大小的缓冲区,数据从MAP函数输出时会先进入到缓冲区,当到达缓冲区的阈值(80%)时,一个后台线程开始把数据SPILL(溢出)到磁盘中,此时数据仍然会不断的写入到缓存中,若缓冲区已满,数据会阻塞等待数据从缓冲区写入磁盘,当缓冲区有剩余大小时,才继续写入到缓冲区。
在写入磁盘前,数据会根据REDUCE的数量并按照MAP函数输出的KEY值进行PARTITION分区,这样做的好处是可以将KEY值相同的数据最终汇聚到同一个REDUCE任务中,提高效率。在每个分区中,后台线程会对数据也按照KEY值进行排序,并且在排序后若存在COMBINER合并任务,会将数据按照合并的要求进行合并,其实COMBINER的过程也就是一个小型的REDUCER,这样做的好处是减少REDUCE端的操作。在讲数据写入磁盘的过程中对数据进行压缩,也是一个很好的选择,可以加快磁盘写入的效率,并且节约磁盘空间,从而减少传输到REDUCE任务端的数据量。
在REDUCE端会通过心跳机制向jobTracker来获取已经执行完毕的MAP任务,通过HTTP的方式来获得分区文件信息,REDUCE默认存在5个复制线程来对分区中的文件进行复制,在REDUCE端也会村在一个缓冲区,来处理复制过来的文件,若缓冲区同样达到阈值,会将处理的文件写入磁盘,在缓冲区处理文件时,若在MAP阶段对数据进行了压缩操作,在REDUCE端需要对数据进行解压缩才可以处理。最后数据就会进入REDUCE函数进行合并。最终输出到HDFS文件系统中。

六.Hadoop的调度机制
1.先入先出FIFO
Hadoop 中默认的调度器,它先按照作业的优先级高低,再按照到达时间的先后选择被执行的作业。

2.公平调度器(相当于时间片轮转调度)
为任务分配资源的方法,其目的是随着时间的推移,让提交的作业获取等量的集群共享资源,让用户公平地共享集群。具体做法是:当集群上只有一个任务在运行时,它将使用整个集群,当有其他作业提交时,系统会将TaskTracker节点空间的时间片分配给这些新的作业,并保证每个任务都得到大概等量的CPU时间。
配置公平调度器
3.容量调度器
支持多个队列,每个队列可配置一定的资源量,每个队列采用 FIFO 调度策略,为 了防止同一个用户的作业独占队列中的资源,该调度器会对同一用户提交的作业所占资源量进行限定。调度时,首先按以下策略选择一个合适队列:计算每个队列中正在运行的任务数与其应该分得的计算资源之间的比值,选择一个该比值最小的队列;然后按以下策略选择该队列中一个作业:按照作业优先级和提交时间顺序选择 ,同时考虑用户资源量限制和内存限制。但是不可剥夺式。

七.Hive中内部表与外部表的区别:
Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据。
八.Hadoop的二次排序
第一种方法是,Reducer将给定key的所有值都缓存起来,然后对它们再做一个Reducer内排序。但是,由于Reducer需要保存给定key的所有值,可能会导致出现内存耗尽的错误。

第二种方法是,将值的一部分或整个值加入原始key,生成一个组合key。这两种方法各有优势,第一种方法编写简单,但并发度小,数据量大的情况下速度慢(有内存耗尽的危险),

第二种方法则是将排序的任务交给MapReduce框架shuffle,更符合Hadoop/Reduce的设计思想。我们将编写一个Partitioner,确保拥有相同key(原始key,不包括添加的部分)的所有数据被发往同一个Reducer,还将编写一个Comparator,以便数据到达Reducer后即按原始key分组。

九.所有的hive任务都会有reducer的执行吗?

答:不是,由于当前hive的优化,使得一般简单的任务不会去用reducer任务;只有稍微复杂的任务才会有reducer任务

举例:使用select * from person ; 就不会有reducer

十.hive表关联查询,如何解决数据倾斜的问题?

倾斜原因:

map输出数据按key Hash的分配到reduce中,由于key分布不均匀、业务数据本身的特、建表时考虑不周、等原因造成的reduce 上的数据量差异过大。

1)、key分布不均匀;

2)、业务数据本身的特性;

3)、建表时考虑不周;

4)、某些SQL语句本身就有数据倾斜;

如何避免:对于key为空产生的数据倾斜,可以对其赋予一个随机值。

解决方案

1>.参数调节:

hive.map.aggr = true

hive.groupby.skewindata=true

有数据倾斜的时候进行负载均衡,当选项设定位true,生成的查询计划会有两个MR Job。第一个MR Job中,Map的输出结果集合会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MR Job再根据预处理的数据结果按照Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个Reduce中),最后完成最终的聚合操作。

2>.SQL 语句调节:

1)、选用join key分布最均匀的表作为驱动表。做好列裁剪和filter操作,以达到两表做join 的时候,数据量相对变小的效果。

2)、大小表Join:

使用map join让小的维度表(1000 条以下的记录条数)先进内存。在map端完成reduce.

4)、大表Join大表:

把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null 值关联不上,处理后并不影响最终结果。

5)、count distinct大量相同特殊值:

count distinct 时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。

十一. 请谈一下hive的特点是什么?
hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供完整的sql查询功能,可以将sql语句转换为MapReduce任务进行运行。其优点是学习成本低,可以通过类SQL语句快速实现简单的MapReduce统计,不必开发专门的MapReduce应用,十分适合数据仓库的统计分析。
十二. Kafka主要特点:

  1. 同时为发布和订阅提供高吞吐量。据了解,Kafka每秒可以生产约25万消息(50 MB),每秒处理55万消息(110 MB)。
  2. 可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如ETL,以及实时应用程序。通过将数据持久化到硬盘以及replication防止数据丢失。
  3. 分布式系统,易于向外扩展。所有的producer、broker和consumer都会有多个,均为分布式的。无需停机即可扩展机器。
  4. 消息被处理的状态是在consumer端维护,而不是由server端维护。当失败时能自动平衡。
  5. 支持online和offline的场景。
    十三. Spark性能优化主要有哪些手段?
    将默认调用的java序列化器改为kyro序列化器(减少序列化数据80%的空间占用(问:为何序列化可以减少存储空间占用(回答:对同类型的数据对象的头进行压缩合并:我感觉这个我回答错误了)));

由于Spark1.6.0的统一内存管理模型,若算法的数据量大,而计算逻辑较为简单,可以增大内存管理中cache块的比例(默认70%(我也一下子想不起来,感觉这个附近)),如果是数据量小而算法逻辑复杂,可以适当减少cache快的比例;

如果因对是集群CPU资源过分盈余,可以采用增加core的数目,但是core的数目增加到一定程度后,依旧无法完全利用CPU的计算资源,可以选择增加Executor的数目,通常环境下,一个Executor推荐设置5个Core的个数,超过5个推荐增加Executor的个数

十四.用mapreduce如何处理数据倾斜问题
数据倾泻产生原因:其次在reduce端默认的分区方式为按照map函数输出key的哈希值,根据哈希值将数据散列在Reduce中,这尽在数据分布比较均衡的情况下比较适用。
首先在MAP函数中增加记录数据倾斜的日志信息,可以通过代码实现,当某个key值超过预先设定的范围就打印该key值的日志
自定义分区方法,可以对原始数据进行抽样的结果来预设定分区的边界,totalOrderPartitioner中的范围分区器可以通过预设的分区值进行分区。另一个自定义分区方法是根据数据输出键的背景知识来自定义分区,假如map输出的键值来自于一本书,其中大部分则肯定是省略词,那么就可以自定义分区将这部分省略次分区到一个REDUCE中,将剩下的键值保存在其他的REDUCE中
执行两次MR的过程,第一次不按照KEY值进行分区,打散数据到REDUCE端,在进行一次MR的操作,这次按照KEY值分区,可以有效的减少数据倾斜的问题。

十五. 列举你知道的常用的hadoop管理和监控的命令
-ls -cat -text -cp -put -chmod -chown

   -du      -get   -copyFromLocal   -copyToLocal 

  -mv   -rm   - tail   -chgrp

十六. 在mr环节中,那些环节需要优化,如何优化,请详细说明。
1、 setNumReduceTasks 适当的设置reduce的数量,如果数据量比较大,那么可以增加reduce的数量

2、适当的时候使用 combine 函数,减少网络传输数据量

3、压缩map和reduce的输出数据

4、使用SequenceFile二进制文件。

5、通过application 的ui页面观察job的运行参数

6、太多小文件,造成map任务过多的问题,应该可以先合并小文件,或者有一个特定的map作为处理小文件的输入

7、map端效率低原因分析

源文件的大小远小于HDFS的块的大小。这意味着任务的开启和停止要耗费更多的时间,就没有足够的时间来读取并处理输入数据。
源文件无法分块。这导致需要通过网络IO从其他节点读取文件块。
一个节点的本地磁盘或磁盘控制器运行在降级模式中,读取写入性能都很差。这会影响某个节点,而不是全部节点。
源文件不来自于HDFS。则可能是Hadoop节点和数据源之间的延迟导致了性能低下。
Map任务从其他数据节点读取数据。可以从JobTracker的map任务细节信息和任务运行尝试中找到输入块的位置。如果输入块的位置不是任务执行的节点,那就不是本地数据了。

十七. 如何用MAPREDUCE实现两张表的连接
方案一:Reduce side join。在Map阶段读取两张表F1,F2,分别对读取的KEY VALUE进行TAG标签,然后输入到REDUCE端,REDUCE端得到数据后按照相同KEY值,对不同标签的相同KEY值进行JOIN。
方案二:由于在REDUCE端进行JOIN效率十分低,因为在MAP端有大量的数据进行SHUFFLE到REDUCE端。所以在以下情况下我们可以考虑在MAP端进行JOIN,若两张表中有一张表比较少,甚至可以放到内存中,那么我们可以利用HADOOP的中DISTRIBUTEDCATCH.ADDCATCHFILE()将文件加载到缓存中,他的参数是URL,在JOBTRACKER加载任务时,会将这个URL下的文件拷贝到各个TASKTRACKER上,并通过DISTRIBUTEDCATCH.GETLOCALCATCHFILE()获取这个文件,从而进行JOIN操作。

十八. fsimage和edit的区别?
Fsimage是在namenode节点中保存了文件系统所有的目录、文件信息,文件信息中包含数据块描述信息,修改时间,访问时间等。对于目录来说包括修改时间,访问权限信息。edit保存的是当对hdfs进行增删改时记录的操作,在HADOOP中存在secoundNamenode节点,当namenode中的小文件太多时,将两个文件发送到secoundNamenode中进行和并,并发送会Namenode来保证当前hdfs中信息的完整。

十九. synchronized和lock的区别
synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。

二十. 给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
这个问题在《编程珠玑》里有很好的描述,大家可以参考下面的思路,探讨一下:
又因为2^32为40亿多,所以给定一个数可能在,也可能不在其中;
这里我们把40亿个数中的每一个用32位的二进制来表示
假设这40亿个数开始放在一个文件中。
然后将这40亿个数分成两类:
1.最高位为0
2.最高位为1
并将这两类分别写入到两个文件中,其中一个文件中数的个数<=20亿,而另一个>=20亿(这相当于折半了);
与要查找的数的最高位比较并接着进入相应的文件再查找
再然后把这个文件为又分成两类:
1.次最高位为0
2.次最高位为1
并将这两类分别写入到两个文件中,其中一个文件中数的个数<=10亿,而另一个>=10亿(这相当于折半了);
与要查找的数的次最高位比较并接着进入相应的文件再查找。

二十一. HDFS存储大量的小文件会有什么问题,如何解决?
(1)HDFS不适合大量小文件的存储,因namenode将文件系统的元数据存放在内存中,因此存储的文件数目受限于 namenode的内存大小。HDFS中每个文件、目录、数据块占用150Bytes。如果存放的文件数目过多的话会占用很大的内存
(2)HDFS适用于高吞吐量,而不适合低时间延迟的访问。如果同时存入大量的小文件会花费很长的时间
(3) 流式读取的方式,不适合多用户写入,以及任意位置写入。如果访问小文件,则必须从一个datanode跳转到另外一个datanode,这样大大降低了读取性能。

二十二.Hbase的表设计原则
HBase表的设计 主要是 列族的设计 和 行键的设计

1.列族的设计
	在设计hbase表时候,列族不宜过多,越少越好,官方推荐hbase表的列族不宜超过3个。
	经常要在一起查询的数据最好放在一个列族中,尽量的减少跨列族的数据访问。	
	如果有多个列族 多个列族中的数据应该设计的比较均匀
2.行键的设计
	hbase表中行键是唯一标识一个表中行的字段,所以行键设计的好不好将会直接影响未来对hbase的查询的性能和查询的便利性
	所以hbase中的行键是需要进行设计的
	
	行键设计的基本原则:
		行键必须唯一
			必须唯一才能唯一标识数据
		行键必须有意义
			这样才能方便数据的查询
		行键最好是字符串类型
			因为数值类型在不同的系统中处理的方式可能不同
		行键最好具有固定的长度
			不同长度的数据可能会造成自然排序时排序的结果和预期不一致
		行键不宜过长
			行键最多可以达到64KB,但是最好是在10~100字节之间,最好不要超过16字节,越短越好,最好是8字节的整数倍。
	
	行键的最佳实践:
		散列原则:
			行键的设计将会影响数据在hbase表中的排序方式,这会影响region切分后的结果,要注意,在设计行键时应该让经常要查询的数据分散在不同的region中,防止某一个或某几个regionserver成为热点。
		有序原则:
			行键的设计将会影响数据在hbase表中的排序方式,所以一种策略是将经常连续查询的条件作为行键最前面的数据,这样一来可以方便批量查询

二十三.Hadoop的压缩算法
Hadoop 对于压缩格式的是自动识别。如果我们压缩的文件有相应压缩格式的扩展名(比如 lzo,gz,bzip2 等)。
Hadoop 会根据压缩格式的扩展名自动选择相对应的解码器来解压数据,此过程完全是 Hadoop 自动处理,我们只需要确保输入的压缩文件有扩展名。
Hadoop 对每个压缩格式的支持, 详细见下表:
压缩格式 工具 算法 扩展名 多文件 可分割性
DEFLATE 无 DEFLATE .deflate 不 不
GZIP gzip DEFLATE .gzp 不 不
ZIP zip DEFLATE .zip 是 是,在文件范围内
BZIP2 bzip2 BZIP2 .bz2 不 是
LZO lzop LZO .lzo 不 是
如果压缩的文件没有扩展名,则需要在执行 MapReduce 任务的时候指定输入格式。

  1. Bzip2 压缩效果明显是最好的,但是 bzip2 压缩速度慢,可分割。

  2. Gzip 压缩效果不如 Bzip2,但是压缩解压速度快,不支持分割。

  3. LZO 压缩效果不如 Bzip2 和 Gzip,但是压缩解压速度最快!并且支持分割!

1.gzip压缩
优点:
压缩率比较高,而且压缩/解压速度也比较快;
hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样;
有hadoop native库;
大部分linux系统都自带gzip命令,使用方便。
缺点:不支持split。
应用场景:
当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用gzip压缩格式。譬如说一天或者一个小时的日志压缩成一个gzip文件,运行mapreduce程序的时候通过多个gzip文件达到并发。
hive程序,streaming程序,和java写的mapreduce程序完全和文本处理一样,压缩之后原来的程序不需要做任何修改。
2.lzo压缩
优点:
压缩/解压速度也比较快,合理的压缩率;
支持split,是hadoop中最流行的压缩格式;
支持hadoop native库;
可以在linux系统下安装lzop命令,使用方便。
缺点:
压缩率比gzip要低一些;
hadoop本身不支持,需要安装;
在应用中对lzo格式的文件需要做一些特殊处理(为了支持split需要建索引,还需要指定inputformat为lzo格式)。
应用场景:
一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,lzo优点越明显。
3.snappy压缩
优点:
高速压缩速度和合理的压缩率;
支持hadoop native库。
缺点:
不支持split;
压缩率比gzip要低;
hadoop本身不支持,需要安装;
linux系统下没有对应的命令。
应用场景:
当mapreduce作业的map输出的数据比较大的时候,作为map到reduce的中间数据的压缩格式;
或者作为一个mapreduce作业的输出和另外一个mapreduce作业的输入。
4.bzip2压缩
优点:
支持split;
具有很高的压缩率,比gzip压缩率都高;
hadoop本身支持,但不支持native;
在linux系统下自带bzip2命令,使用方便。
缺点:
压缩/解压速度慢;
不支持native。
应用场景:
适合对速度要求不高,但需要较高的压缩率的时候,可以作为mapreduce作业的输出格式;
或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况;
或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持split,而且兼容之前的应用程序(即应用程序不需要修改)的情况。

二十四.算法思想题

  1. 给定a,b两个文件,各存放50亿个url,每个url各占64字节,内存限制4G,让你找出a,b文件共同的url。
    方案1:可以估计每个文件按大小为50G*64=320G,远远大于内存限制的4G,所以不可能完全加载到内存中处理,考虑采取分而治之的方法。
    S遍历文件a,对每个url求取hash%1000,然后根据所取得的值将rul分别存储到1000个小文件(a0,a1,a2….a999),这样每个文件大小约为300M。
    S遍历文件b,采取和a相同的方式将url存储到1000个小文件中,这样处理后,所有可能相同的url都在对应的小文件(a0—b0,a1—b1….),不对应的小文件不可能有相同的url,然后我们只要求出1000对小文件中相同的url即可。 s 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

  方案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
二十五.算法思想题二 
有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
  方案1:
  s 顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
  s 找一台内存在2G左右的机器,依次对用hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为)。
  s 对这10个文件进行归并排序(内排序与外排序相结合)。
  方案2:
  一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。
  方案3:
  与方案1类似,但在做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。
二十六.算法思想题三
有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

  方案1:顺序读文件中,对于每个词x,取,然后按照该值存到5000个小文件(记为)中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,知道分解得到的小文件的大小都不超过1M。对每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。

二十七.算法思想题四

一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到个数中的中数?

  方案1:先大体估计一下这些数的范围,比如这里假设这些数都是32位无符号整数(共有个)。我们把0到的整数划分为N个范围段,每个段包含个整数。比如,第一个段位0到,第二段为到,…,第N个段为到。然后,扫描每个机器上的N个数,把属于第一个区段的数放到第一个机器上,属于第二个区段的数放到第二个机器上,…,属于第N个区段的数放到第N个机器上。注意这个过程每个机器上存储的数应该是O(N)的。下面我们依次统计每个机器上数的个数,一次累加,直到找到第k个机器,在该机器上累加的数大于或等于,而在第k-1个机器上的累加数小于,并把这个数记为x。那么我们要找的中位数在第k个机器中,排在第位。然后我们对第k个机器的数排序,并找出第个数,即为所求的中位数。复杂度是的。

  方案2:先对每台机器上的数进行排序。排好序后,我们采用归并排序的思想,将这N个机器上的数归并起来得到最终的排序。找到第个便是所求。复杂度是的。

二十八.算法思想题五
将多个集合合并成没有交集的集合:给定一个字符串的集合,格式如:。要求将其中交集不为空的集合合并,要求合并完成的集合之间无交集,例如上例应输出。
  (1) 请描述你解决这个问题的思路;
  (2) 给出主要的处理流程,算法,以及算法的复杂度;
(3) 请描述可能的改进。

  方案1:采用并查集。首先所有的字符串都在单独的并查集中。然后依扫描每个集合,顺序合并将两个相邻元素合并。例如,对于,首先查看aaa和bbb是否在同一个并查集中,如果不在,那么把它们所在的并查集合并,然后再看bbb和ccc是否在同一个并查集中,如果不在,那么也把它们所在的并查集合并。接下来再扫描其他的集合,当所有的集合都扫描完了,并查集代表的集合便是所求。复杂度应该是O(NlgN)的。改进的话,首先可以记录每个节点的根结点,改进查询。合并的时候,可以把大的和小的进行合,这样也减少复杂度。

二十九.Hive UDF编写错误从哪方面调错?

首先查日志,UDF的日志是保存在Hadoop MR的日志文件中,
其次通过错误提示通过管道符grep |查找问题所在,并针对问题进行改进

在日志文件过大的情况下,可以通过head tail sed 等指令进行分段查找

三十.什么是tire树?
Trie树,又称单词查找树、字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.
三点特征:
1)根节点不包含字符,除根节点外每一个节点都只包含一个字符。  
2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 
3)每个节点的所有子节点包含的字符都不相同。

三十一. 500桶酒,其中1桶是毒酒;48小时后要举行酒会;毒酒喝下去会在之后的第23-24小时内毒死人;国王决定用囚犯来试酒,不介意囚犯死多少,只要求用最少的囚犯来测试出哪一桶是毒酒,问最少需要多少囚犯才能保证找出毒酒?请诸位自序们为乌有国王出个主意!

思路一
需要9个人
将500桶毒酒进行编号,并用二进制表示.得到000000000~111110100.
每个囚犯对应二进制的每一位,然后喝下对应位数为1的那个编号的毒酒
然后根据死亡的囚犯编号或者毒酒的二进制编码.
进而获得毒酒的编号.
如毒酒编号为350,那么二进制对应为101011110.难么囚犯234579会死亡.

思路二
需要两个人,因为23*23=529>500
将毒酒进行编号,并且每23桶作为一组,123;2446…..
囚犯一每小时喝下每一组酒
囚犯二每小时喝下每一组中对应的酒,如第一小时喝下每组的第一桶酒.第二小时喝下每一组对应的第二桶酒.
然后根据死亡时间进行交叉查找

根据2人死亡计时和行、列交叉点即可找到那桶毒酒了。
如囚犯一34小时死亡,代表(34-23)组酒有毒,即231~253有毒.
囚犯二38小时死亡,代表每组的(38-23)号=15号有问题.
综合后即为231+15-1=245号酒有问题.

简单地说把500桶分成23行22列:
第一个囚犯每小时喝一行;
第二个囚犯每小时喝一列。
根据2人死亡计时和行、列交叉点即可找到那桶毒酒了。

posted @ 2019-04-15 20:01  Striver。  阅读(1063)  评论(0编辑  收藏  举报