数据分析大数据面试题大杂烩02

Map端会处理输入数据并产生中间结果,这个中间结果会写到本地磁盘,每个Map的输出会先写到内存缓冲区中,当写入的数据达到设定的阈值时,系统将会启动一个线程将缓冲区的数据写到磁盘,这个过程叫做spill(spill写入之前,会先进行二次排序,首先根据数据所属的partition进行排序,然后每个partition中的数据再按key来排序 . partition的目是将记录划分到不同的Reducer上去,以期望能够达到负载均衡,以后的Reducer就会根据partition来读取自己对应的数据 . 接着运行combiner(如果设置了的话),combiner的本质也是一个Reducer,其目的是对将要写入到磁盘上的文件先进行一次处理,这样,写入到磁盘的数据量就会减少 . 最后将数据写到本地磁盘产生spill文件(spill文件保存在{mapred.local.dir}指定的目录中,Map任务结束后就会被删除))
Reducer在拷贝数据的时候只拷贝与自己对应的partition中的数据即可 . 每个Reducer会处理一个或者多个partition,接下来就是sort阶段,也成为merge阶段,因为这个阶段的主要工作是执行了归并排序 . 从Map端拷贝到Reduce端的数据都是有序的(指阶段),所以很适合归并排序 . 最终在Reduce端生成一个较大的文件作为Reduce的输入 . 
最后是Reduce过程,产生最终的输出结果将写到HDFS上

partition数量和返回值(决定去哪个reducer)一个概念
默认分区数量key.hash%reducetask的个数

reduceTask的数量由job提交时设置的numreducretask决定 . 

0的话就没有reducer


1map的数量通常是由hadoop集群的DFS块大小确定的,也就是输入文件的总块数,正常的map数量的并行规模大致是每一个Node是10~100个,对于CPU消耗较小的作业可以设置Map数量为300个左右,但是由于hadoop的没一个任务在初始化时需要一定的时间,因此比较合理的情况是每个map执行的时间至少超过1分钟 . 具体的数据分片是这样的,InputFormat在默认情况下会根据hadoop集群的DFS块大小进行分片,每一个分片会由一个map任务来进行处理,当然用户还是可以通过参数mapred.min.split.size参数在作业提交客户端进行自定义设置 . 还有一个重要参数就是mapred.map.tasks,这个参数设置的map数量仅仅是一个提示,只有当InputFormat 决定了map任务的个数比mapred.map.tasks值小时才起作用 . 
2Map任务的个数也能通过使用JobConf 的conf.setNumMapTasks(int num)方法来手动地设置 . 这个方法能够用来增加map任务的个数,但是不能设定任务的个数小于Hadoop系统通过分割输入数据得到的值 . 当然为了提高集群的并发效率,可以设置一个默认的map数量,当用户的map数量较小或者比本身自动分割的值还小时可以使用一个相对交大的默认值,从而提高整体hadoop集群的效率 . 
reduce在运行时往往需要从相关map端复制数据到reduce节点来处理,因此相比于map任务 . reduce节点资源是相对比较缺少的,同时相对运行较慢,正确的reduce任务的个数应该是0.95或者1.75 (节点数 ×mapred.tasktracker.tasks.maximum参数值) . 如果任务数是节点个数的0.95倍,那么所有的reduce任务能够在 map任务的输出传输结束后同时开始运行 . 如果任务数是节点个数的1.75倍,那么高速的节点会在完成他们第一批reduce任务计算之后开始计算第二批 reduce任务,这样的情况更有利于负载均衡 . 同时需要注意增加reduce的数量虽然会增加系统的资源开销,但是可以改善负载匀衡,降低任务失败带来的负面影响 . 同样,Reduce任务也能够与 map任务一样,通过设定JobConf 的conf.setNumReduceTasks(int num)方法来增加任务个数 . 
reduce数量为0
有些作业不需要进行归约进行处理,那么就可以设置reduce的数量为0来进行处理,这种情况下用户的作业运行速度相对较高,map的输出会直接写入到 SetOutputPath(path)设置的输出目录,而不是作为中间结果写到本地 . 同时Hadoop框架在写入文件系统前并不对之进行排序 . 

首先map task会从本地文件系统读取数据,转换成key-value形式的键值对集合,使用的是hadoop内置的数据类型,如Text,Longwritable等 . 
将键值对集合输入mapper进行业务处理过程,将其转化成需要的key-value再输出 . 
之后会进行一个partition分区操作,默认使用的是hashpartitioner,可以通过重写hashpartitioner的getPartition方法来自定义分区规则 . 
之后会对key进行sort排序,grouping分组操作将相同key的value合并分组输出,在这里可以使用自定义的数据类型,重写WritableComparator的Comparator方法来自定义排序规则,重写RawComparator的compara方法来自定义分组规则 . 
之后进行一个combiner归约操作,就是一个本地的reduce预处理,以减小shuffle,reducer的工作量 . 
Reduce task会用过网络将各个数据收集进行reduce处理,最后将数据保存或者显示,结束整个job . 

InputFormat会在map操作之前对数据进行两方面的预处理 . 
1.是getSplits,返回的是InputSplit数组,对数据进行Split分片,每片交给map操作一次 . 
2.是getRecordReader,返回的是RecordReader对象,对每个Split分片进行转换为key-value键值对格式传递给map,常用的InputFormat是TextInputFormat,使用的是LineRecordReader对每个分片进行键值对的转换,以行偏移量作为键,行内容作为值 . 
自定义类继承InputFormat接口,重写createRecordReader和isSplitable方法在createRecordReader中可以自定义分隔符 . 

两者都使用mr模型来进行并行计算,hadoop的一个作业称为job,job里面分为map task和reduce task,每个task都是在自己的进程中运行的,当task结束时,进程也会结束 . 
Spark用户提交的任务称为application,一个application对应一个SparkContext,app中存在多个job,每触发一个action操作就会产生一个job(线程) . 
这些job可以并行或者串行执行,每个job有多个stage,stage是shuffle过程中DAGSchaduler通过RDD之间的依赖关系划分job而来的,每个stage里面有多个task,组成taskset有TaskSchaduler分发到各个executor中执行,executor的生命周期是和application一样的,即使没有job运行也是存在的,所以task可以快速启动读取内存进行计算的 . 
Hadoop的job只有map和reduce操作,表达能力比较欠缺而且在mr过程中会重复的读写hdfs,造成大量的io操作,多个job需要自己管理关系 . 
Spark的迭代计算都是在内存中进行的,API中提供了大量的RDD操作join,groupby等,而且通过DAG图可以实现良好的容错 . 

为什么要用flume导入hdfs,hdfs的架构是怎样的?
Flume可以实时的导入数据到hdfs中,当hdfs上的文件达到一个指定大小的时候会形成一个文件,或者超时所指定时间的话也形成一个文件 . 
文件都是存储在datanode上的,namenode存储着datanode的元数据信息,而namenode的元数据信息是存在内存中的,所以当文件切片很小或者很多的时候会卡死 . 

比如说作业中大部分都完成了,但是总有几个reduce一直在运行 . 
这是因为这几个reduce中的处理的数据要远远大于其他的reduce,可能是对键值对任务划分的不均匀造成的数据倾斜 . 
解决的方法可以在分区的时候重新定义分区规则对于value数据很多的key可以进行拆分 均匀打散等处理,或者是在map端的combiner中进行数据预处理的操作 . 

简单说一下hadoop和spark的shuffle过程
Hadoop:map端保存分片数据,通过网络收集到reduce端 . 
Spark:spark的shuffles是在DAGSchedular划分Stage的时候产生的,TaskSchedular要分发task任务到各个worker的executor . 减少shuffle可以提高性能 . 

存的是和hdfs的映射关系,hive是逻辑上的数据仓库,实际操作的都是hdfs上的文件,HQL就是用SQL语法来写的MR程序 . 

Hive与关系型数据库的关系?
没有关系,hive是数据仓库,不能和数据库一样进行实时的CRUD操作 . 
是一次写入多次读取的操作,可以看成是ETL的工具 . 

Flume的工作机制是什么?
核心概念是agent,里面包括source,channel和sink三个组件 . 
Source运行在日志收集节点进行日志采集,之后临时存储在channel中,sink负责将channel中的数据发送到目的地 . 
只有发送成功channel中的数据才会被删除 . 
首先书写flume配置文件,定义agent source channel和sink然后将其组装,执行flume-ng命令 . 

Hbase行键列族的概念,物理模型,表的设计原则?
行键:是hbase表自带的,每个行键对应一条数据 . 
列族:是创建表时指定的,为列的集合,每个列族作为一个文件单独存储,存储的数据都是字节数组,其中数据可以有很多,通过时间戳来区分 . 
物理模型:整个hbase表会拆分成多个region,每个region记录着行键的起始点保存在不同的节点上,查询时就是对各个节点的并行查询,当region很大时使用.META表存储各个region的起始点,-ROOT又可以存储.META的起始点 . 
Rowkey的设计原则:各个列族数据平衡,长度原则 相邻原则,创建表的时候设置表放入regionserver缓存中,避免自动增长和时间,使用字节数组代替string,最大长度64kb,最好16字节以内,按天分表,两个字节散列,四个字节存储时分毫秒 . 
列族的设计原则:尽可能少(按照列族进行存储,按照region进行读取,不必要的io操作),经常和不经常使用的两类数据放入不同列族中,列族名字尽可能短 . 

请列出正常的hadoop集群中hadoop都分别需要启动 哪些进程,他们的作用分别都是什么,请尽量列的详细一些 . 
namenode:负责管理hdfs中文件块的元数据,响应客户端请求,管理datanode上文件block的均衡,维持副本数量
Secondname:主要负责做checkpoint操作;也可以做冷备(对一定范围内数据做快照性备份)
Datanode:存储数据块,负责客户端对数据块的io请求
Jobtracker :管理任务,并将任务分配给 tasktracker . 
Tasktracker:执行JobTracker分配的任务 . 
Resourcemanager Nodemanager Journalnode Zookeeper Zkfc

读:
找到要读数据的region所在的RegionServer,然后按照以下顺序进行读取:先去BlockCache读取,若BlockCache没有,则到Memstore读取,若Memstore中没有,则到HFile中去读 . 
写:
找到要写数据的region所在的RegionServer,然后先将数据写到WAL(Write-Ahead Logging,预写日志系统)中,然后再将数据写到Memstore等待刷新,回复客户端写入完成 . 

HBase的特点是什么?
(1)hbase是一个分布式的基于列式存储的数据库,基于hadoop的HDFS存储,zookeeper进行管理 . 
(2)hbase适合存储半结构化或非结构化数据,对于数据结构字段不够确定或者杂乱无章很难按一个概念去抽取的数据 . 
(3)hbase为null的记录不会被存储 . 
(4)基于的表包括rowkey,时间戳和列族 . 新写入数据时,时间戳更新,同时可以查询到以前的版本 . 
(5)hbase是主从结构 . Hmaster作为主节点,hregionserver作为从节点 . 

请描述如何解决Hbase中region太小和region太大带来的结果 . 
Region过大会发生多次compaction,将数据读一遍并写一遍到hdfs上,占用io,region过小会造成多次split,region会下线,影响访问服务


如何确定map个数


搭建伪分布式hadoop开发环境
1 Linux环境
2 Jdk安装
3  关闭防火墙
4 配置hadoop
5 格式化namenode(不需要重复)
6 启动hdfs 守护进程
7 Web 访问界面  50070
8 配置YARN任务调度
9 启动hdfs YARA进程
10 检查YARN状态
11 向YARN提交任务

搭建hadoop完全分布式简单步骤
1 虚拟机装备
2 网络配置完好
3 JDK安装
4 Ssh 配置
5 同步服务器时间
6 Hadoop集群配置
A:环境变量
B:hadoop文件配置
7 启动hadoop集群
8 Web端口访问 . 

 hive的安装和使用


提前根据查询结果来组织数据 . 每种业务都是不同的,要想查询得快,就要提前分析场景,在数据入库时,就提前根据查询结果来组织数据 . 这也是微博等应用的做法,根据显示结果提前存储数据 . 

分钟级

Spark生态圈也称为BDAS(伯克利数据分析栈),是伯克利APMLab实验室打造的,力图在算法(Algorithms) 机器(Machines) 人(People)之间通过大规模集成来展现大数据应用的一个平台 . 伯克利AMPLab运用大数据 云计算 通信等各种资源以及各种灵活的技术方案,对海量不透明的数据进行甄别并转化为有用的信息,以供人们更好的理解世界 . 该生态圈已经涉及到机器学习 数据挖掘 数据库 信息检索 自然语言处理和语音识别等多个领域 . 
Spark生态圈以Spark Core为核心,从HDFS Amazon S3和HBase等持久层读取数据,以MESS YARN和自身携带的Standalone为资源管理器调度Job完成Spark应用程序的计算 .  这些应用程序可以来自于不同的组件,如Spark Shell/Spark Submit的批处理 Spark Streaming的实时处理应用 Spark SQL的即席查询 BlinkDB的权衡查询 MLlib/MLbase的机器学习 GraphX的图处理和SparkR的数学计算等 . 


Spark Core
Spark内核架构:
l  提供了有向无环图(DAG)的分布式并行计算框架,并提供Cache机制来支持多次迭代计算或者数据共享,大大减少迭代计算之间读取数据局的开销,这对于需要进行多次迭代的数据挖掘和分析性能有很大提升
2  在Spark中引入了RDD (Resilient Distributed Dataset) 的抽象,它是分布在一组节点中的只读对象集合,这些集合是弹性的,如果数据集一部分丢失,则可以根据"血统"对它们进行重建,保证了数据的高容错性;
3  移动计算而非移动数据,RDD Partition可以就近读取分布式文件系统中的数据块到各个节点内存中进行计算
4  使用多线程池模型来减少task启动开稍
5  采用容错的 高可伸缩性的akka作为通讯框架

SparkStreaming
SparkStreaming是一个对实时数据流进行高通量 容错处理的流式处理系统,可以对多种数据源(如Kdfka Flume Twitter Zero和TCP 套接字)进行类似Map Reduce和Join等复杂操作,并将结果保存到外部文件系统 数据库或应用到实时仪表盘 . 
Spark Streaming构架:
l计算流程:Spark Streaming是将流式计算分解成一系列短小的批处理作业 . 这里的批处理引擎是Spark Core,也就是把Spark Streaming的输入数据按照batch size(如1秒)分成一段一段的数据(Discretized Stream),每一段数据都转换成Spark中的RDD(Resilient Distributed Dataset),然后将Spark Streaming中对DStream的Transformation操作变为针对Spark中对RDD的Transformation操作,将RDD经过操作变成中间结果保存在内存中 . 整个流式计算根据业务的需求可以对中间的结果进行叠加或者存储到外部设备 . 下图显示了Spark Streaming的整个流程 . 

容错性:对于流式计算来说,容错性至关重要 . 首先我们要明确一下Spark中RDD的容错机制 . 每一个RDD都是一个不可变的分布式可重算的数据集,其记录着确定性的操作继承关系(lineage),所以只要输入数据是可容错的,那么任意一个RDD的分区(Partition)出错或不可用,都是可以利用原始输入数据通过转换操作而重新算出的 .   
对于Spark Streaming来说,其RDD的传承关系如下图所示,图中的每一个椭圆形表示一个RDD,椭圆形中的每个圆形代表一个RDD中的一个Partition,图中的每一列的多个RDD表示一个DStream(图中有三个DStream),而每一行最后一个RDD则表示每一个Batch Size所产生的中间结果RDD . 我们可以看到图中的每一个RDD都是通过lineage相连接的,由于Spark Streaming输入数据可以来自于磁盘,例如HDFS(多份拷贝)或是来自于网络的数据流(Spark Streaming会将网络输入数据的每一个数据流拷贝两份到其他的机器)都能保证容错性,所以RDD中任意的Partition出错,都可以并行地在其他机器上将缺失的Partition计算出来 . 这个容错恢复方式比连续计算模型(如Storm)的效率更高 . 

实时性:对于实时性的讨论,会牵涉到流式处理框架的应用场景 . Spark Streaming将流式计算分解成多个Spark Job,对于每一段数据的处理都会经过Spark DAG图分解以及Spark的任务集的调度过程 . 对于目前版本的Spark Streaming而言,其最小的Batch Size的选取在0.5~2秒钟之间(Storm目前最小的延迟是100ms左右),所以Spark Streaming能够满足除对实时性要求非常高(如高频实时交易)之外的所有流式准实时计算场景 . 
扩展性与吞吐量:Spark目前在EC2上已能够线性扩展到100个节点(每个节点4Core),可以以数秒的延迟处理6GB/s的数据量(60M records/s),其吞吐量也比流行的Storm高2~5倍,Berkeley利用WordCount和Grep两个用例所做的测试,在Grep这个测试中,Spark Streaming中的每个节点的吞吐量是670k records/s,而Storm是115k records/s . 
2010 ,Spark正式对外开源

Spark SQL
Shark是SparkSQL的前身,那个时候Hive可以说是SQL on Hadoop的唯一选择,负责将SQL编译成可扩展的MapReduce作业,鉴于Hive的性能以及与Spark的兼容,Shark项目由此而生 . 
Shark即Hive on Spark,本质上是通过Hive的HQL解析,把HQL翻译成Spark上的RDD操作,然后通过Hive的metadata获取数据库里的表信息,实际HDFS上的数据和文件,会由Shark获取并放到Spark上运算 . Shark的最大特性就是快和与Hive的完全兼容,且可以在shell模式下使用rdd2sql()这样的API,把HQL得到的结果集,继续在scala环境下运算,支持自己编写简单的机器学习或简单分析处理函数,对HQL结果进一步分析计算 . 
在2014年7月1日的Spark Summit上,Databricks宣布终止对Shark的开发,将重点放到Spark SQL上 . Databricks表示,Spark SQL将涵盖Shark的所有特性,用户可以从Shark 0.9进行无缝的升级 . 在会议上,Databricks表示,Shark更多是对Hive的改造,替换了Hive的物理执行引擎,因此会有一个很快的速度 . 然而,不容忽视的是,Shark继承了大量的Hive代码,因此给优化和维护带来了大量的麻烦 . 随着性能优化和先进分析整合的进一步加深,基于MapReduce设计的部分无疑成为了整个项目的瓶颈 . 因此,为了更好的发展,给用户提供一个更好的体验,Databricks宣布终止Shark项目,从而将更多的精力放到Spark SQL上 . 
Spark SQL允许开发人员直接处理RDD,同时也可查询例如在 Apache Hive上存在的外部数据 . Spark SQL的一个重要特点是其能够统一处理关系表和RDD,使得开发人员可以轻松地使用SQL命令进行外部查询,同时进行更复杂的数据分析 . 除了Spark SQL外,Michael还谈到Catalyst优化框架,它允许Spark SQL自动修改查询方案,使SQL更有效地执行 . 
还有Shark的作者是来自中国的博士生辛湜(Reynold Xin),也是Spark的核心成员,具体信息可以看他的专访 http://www.csdn.net/article/2013-04-26/2815057-Spark-Reynold
Spark SQL的特点:
l引入了新的RDD类型SchemaRDD,可以象传统数据库定义表一样来定义SchemaRDD,SchemaRDD由定义了列数据类型的行对象构成 . SchemaRDD可以从RDD转换过来,也可以从Parquet文件读入,也可以使用HiveQL从Hive中获取 . 
2内嵌了Catalyst查询优化框架,在把SQL解析成逻辑执行计划之后,利用Catalyst包里的一些类和接口,执行了一些简单的执行计划优化,最后变成RDD的计算
3在应用程序中可以混合使用不同来源的数据,如可以将来自HiveQL的数据和来自SQL的数据进行Join操作 . 

Shark的出现使得SQL-on-Hadoop的性能比Hive有了10-100倍的提高,  那么,摆脱了Hive的限制,SparkSQL的性能又有怎么样的表现呢?虽然没有Shark相对于Hive那样瞩目地性能提升,但也表现得非常优异 . 
为什么sparkSQL的性能会得到怎么大的提升呢?主要sparkSQL在下面几点做了优化:
1. 内存列存储(In-Memory Columnar Storage) sparkSQL的表数据在内存中存储不是采用原生态的JVM对象存储方式,而是采用内存列存储;
2. 字节码生成技术(Bytecode Generation) Spark1.1.0在Catalyst模块的expressions增加了codegen模块,使用动态字节码生成技术,对匹配的表达式采用特定的代码动态编译 . 另外对SQL表达式都作了GC优化, GC优化的实现主要还是依靠Scala2.10的运行时放射机制(runtime reflection);
3. Scala代码优化 SparkSQL在使用Scala编写代码的时候,尽量避免低效的 容易GC的代码

BlinkDB
BlinkDB 是一个用于在海量数据上运行交互式 SQL 查询的大规模并行查询引擎,它允许用户通过权衡数据精度来提升查询响应时间,其数据的精度被控制在允许的误差范围内 . 为了达到这个目标,BlinkDB 使用两个核心思想:
l一个自适应优化框架,从原始数据随着时间的推移建立并维护一组多维样本;
2一个动态样本选择策略,选择一个适当大小的示例基于查询的准确性和(或)响应时间需求 . 
和传统关系型数据库不同,BlinkDB是一个很有意思的交互式查询系统,就像一个跷跷板,用户需要在查询精度和查询时间上做一权衡;如果用户想更快地获取查询结果,那么将牺牲查询结果的精度;同样的,用户如果想获取更高精度的查询结果,就需要牺牲查询响应时间 . 用户可以在查询的时候定义一个失误边界 . 


MLBase/MLlib
MLBase是Spark生态圈的一部分专注于机器学习,让机器学习的门槛更低,让一些可能并不了解机器学习的用户也能方便地使用MLbase . MLBase分为四部分:MLlib MLI ML Optimizer和MLRuntime . 
l  ML Optimizer会选择它认为最适合的已经在内部实现好了的机器学习算法和相关参数,来处理用户输入的数据,并返回模型或别的帮助分析的结果;
2  MLI 是一个进行特征抽取和高级ML编程抽象的算法实现的API或平台;
3  MLlib是Spark实现一些常见的机器学习算法和实用程序,包括分类 回归 聚类 协同过滤 降维以及底层优化,该算法可以进行可扩充; MLRuntime 基于Spark计算框架,将Spark的分布式计算应用到机器学习领域 . 
总的来说,MLBase的核心是他的优化器,把声明式的Task转化成复杂的学习计划,产出最优的模型和计算结果 . 与其他机器学习Weka和Mahout不同的是:
l  MLBase是分布式的,Weka是一个单机的系统;
2  MLBase是自动化的,Weka和Mahout都需要使用者具备机器学习技能,来选择自己想要的算法和参数来做处理;
3  MLBase提供了不同抽象程度的接口,让算法可以扩充
4  MLBase基于Spark这个平台

GraphX
GraphX是Spark中用于图(e.g., Web-Graphs and Social Networks)和图并行计算(e.g., PageRank and Collaborative Filtering)的API,可以认为是GraphLab(C++)和Pregel(C++)在Spark(Scala)上的重写及优化,跟其他分布式图计算框架相比,GraphX最大的贡献是,在Spark之上提供一栈式数据解决方案,可以方便且高效地完成图计算的一整套流水作业 . GraphX最先是伯克利AMPLAB的一个分布式图计算框架项目,后来整合到Spark中成为一个核心组件 . 
GraphX的核心抽象是Resilient Distributed Property Graph,一种点和边都带属性的有向多重图 . 它扩展了Spark RDD的抽象,有Table和Graph两种视图,而只需要一份物理存储 . 两种视图都有自己独有的操作符,从而获得了灵活操作和执行效率 . 如同Spark,GraphX的代码非常简洁 . GraphX的核心代码只有3千多行,而在此之上实现的Pregel模型,只要短短的20多行 . GraphX的代码结构整体下图所示,其中大部分的实现,都是围绕Partition的优化进行的 . 这在某种程度上说明了点分割的存储和相应的计算优化的确是图计算框架的重点和难点 . 

raphX的底层设计有以下几个关键点 . 
1.对Graph视图的所有操作,最终都会转换成其关联的Table视图的RDD操作来完成 . 这样对一个图的计算,最终在逻辑上,等价于一系列RDD的转换过程 . 因此,Graph最终具备了RDD的3个关键特性:Immutable Distributed和Fault-Tolerant . 其中最关键的是Immutable(不变性) . 逻辑上,所有图的转换和操作都产生了一个新图;物理上,GraphX会有一定程度的不变顶点和边的复用优化,对用户透明 . 
2.两种视图底层共用的物理数据,由RDD[Vertex-Partition]和RDD[EdgePartition]这两个RDD组成 . 点和边实际都不是以表Collection[tuple]的形式存储的,而是由VertexPartition/EdgePartition在内部存储一个带索引结构的分片数据块,以加速不同视图下的遍历速度 . 不变的索引结构在RDD转换过程中是共用的,降低了计算和存储开销 . 
3.图的分布式存储采用点分割模式,而且使用partitionBy方法,由用户指定不同的划分策略(PartitionStrategy) . 划分策略会将边分配到各个EdgePartition,顶点Master分配到各个VertexPartition,EdgePartition也会缓存本地边关联点的Ghost副本 . 划分策略的不同会影响到所需要缓存的Ghost副本数量,以及每个EdgePartition分配的边的均衡程度,需要根据图的结构特征选取最佳策略 . 目前有EdgePartition2d EdgePartition1d RandomVertexCut和CanonicalRandomVertexCut这四种策略 . 在淘宝大部分场景下,EdgePartition2d效果最好 . 

SparkR
SparkR是AMPLab发布的一个R开发包,使得R摆脱单机运行的命运,可以作为Spark的job运行在集群上,极大得扩展了R的数据处理能力 . 
SparkR的几个特性:
l  提供了Spark中弹性分布式数据集(RDD)的API,用户可以在集群上通过R shell交互性的运行Spark job . 
2  支持序化闭包功能,可以将用户定义函数中所引用到的变量自动序化发送到集群中其他的机器上 . 
3  SparkR还可以很容易地调用R开发包,只需要在集群上执行操作前用includePackage读取R开发包就可以了,当然集群上要安装R开发包 . 

Tachyon
Tachyon是一个高容错的分布式文件系统,允许文件以内存的速度在集群框架中进行可靠的共享,就像Spark和 MapReduce那样 . 通过利用信息继承,内存侵入,Tachyon获得了高性能 . Tachyon工作集文件缓存在内存中,并且让不同的 Jobs/Queries以及框架都能内存的速度来访问缓存文件" . 因此,Tachyon可以减少那些需要经常使用的数据集通过访问磁盘来获得的次数 . Tachyon兼容Hadoop,现有的Spark和MR程序不需要任何修改而运行 . 
在2013年4月,AMPLab共享了其Tachyon 0.2.0 Alpha版本的Tachyon,其宣称性能为HDFS的300倍,继而受到了极大的关注 . Tachyon的几个特性如下:
lJAVA-Like File API
Tachyon提供类似JAVA File类的API,
2兼容性
Tachyon实现了HDFS接口,所以Spark和MR程序不需要任何修改即可运行 . 
3可插拔的底层文件系统
Tachyon是一个可插拔的底层文件系统,提供容错功能 . tachyon将内存数据记录在底层文件系统 . 它有一个通用的接口,使得可以很容易的插入到不同的底层文件系统 . 目前支持HDFS,S3,GlusterFS和单节点的本地文件系统,以后将支持更多的文件系统 . 

hadoop dfsadmin -report 
用这个命令可以快速定位出哪些节点down掉了,HDFS的容量以及使用了多少,以及每个节点的硬盘使用情况,但是并不能定位HDFS损坏块 . 

hadoop为各个守护进程(namenode,secondarynamenode,jobtracker,datanode,tasktracker)统一分配的内存在hadoop-env.sh中设置,参数为HADOOP_HEAPSIZE,默认为1000M . 

Hadoop Yarn 的三种资源调度器详解
1) 默认调度器FIFO
hadoop中默认的调度器,采用先进先出的原则
2) 计算能力调度器Capacity Scheduler
选择占用资源小,优先级高的先执行
3) 公平调度器Fair Scheduler
同一队列中的作业公平共享队列中所有资源

Hive中的元数据通常包括:表的名字,表的列和分区及其属性,表的属性(内部表和 外部表),表的数据所在目录 . Hive元存储(Metastore)管理参数默认储存在自带的Derby数据库中 . 缺点就是不适合多用户操作,并且数据存储目录不固定 . 数据库和Hive所在的机器绑定,极度不方便管理 . 通常将元数据存储在我们自己创建的MySQL数据库(本地或远程)当中 . 
元数据存储两种方法:
(1)derby数据库
默认内存数据库,一次只能打开一个会话,会话关闭metastore数据消失
(2)MySQL数据库
外部metastore存储引擎,可以让多个会话使用
即:
自带的Derby数据库
本地RDBMS数据库,即关系数据库管理系统(Relational Database Management System), 如MySQL
远程MySQL

flume采集日志时中间停了,怎么记录之前的日志?
当节点出现故障时,日志能够被传送到其他节点上而不会丢失
事件在通道中进行,该通道管理从故障中恢复 . Flume支持一个由本地文件系统支持的持久文件通道 . 还有一个内存通道,它只是将事件存储在内存中的队列中,这更快,但是当代理进程死亡时仍然留在内存通道中的任何事件都无法恢复 . 
a)Flume提供了三种级别的可靠性保障,从强到弱依次分别为
i.end-to-end(收到数据agent首先将event写到磁盘上,当数据传送成功后,再删除;如果数据发送失败,可以重新发送)
ii.Store on failure(这也是scribe采用的策略,当数据接收方crash时,将数据写到本地,待恢复后,继续发送)
iii.Best effort(数据发送到接收方后,不会进行确认)

在实际应用中还存在这样一个问题,比如导入数据的时候,Map Task 执行失败, 那么该 Map 任务会转移到另外一个节点执行重新运行,这时候之前导入的数据又要重新导入一份,造成数据重复导入 .  因为 Map Task 没有回滚策略,一旦运行失败,已经导入数据库中的数据就无法恢复 . 
Sqoop export 提供了一种机制能保证原子性, 使用--staging-table 选项指定临时导入的表 . 
Sqoop export 导出数据的时候会分为两步:
第一步,将数据导入数据库中的临时表,如果导入期间 Map Task 失败,会删除临时表数据重新导入;
第二步,确认所有 Map Task 任务成功后,会将临时表名称为指定的表名称 . 
sqoop export \--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \--username sqoop \--password sqoop \--table user \--staging-table staging_user

十个海量数据处理方法大总结
一 Bloom filter
适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集
基本原理及要点:
对于原理来说很简单,位数组+k个独立hash函数 . 将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的 . 同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字 . 所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了 . 
还有一个比较重要的问题,如何根据输入元素个数n,确定位数组m的大小及hash函数个数 . 当hash函数个数k=(ln2)(m/n)时错误率最小 . 在错误率不大于E的情况下,m至少要等于nlg(1/E)才能表示任意n个元素的集合 . 但m还应该更大些,因为还要保证bit数组里至少一半为0,则m应该>=nlg(1/E)lge 大概就是nlg(1/E)1.44倍(lg表示以2为底的对数) . 
举个例子我们假设错误率为0.01,则此时m应大概是n的13倍 . 这样k大概是8个 . 
注意这里m与n的单位不同,m是bit为单位,而n则是以元素个数为单位(准确的说是不同元素的个数) . 通常单个元素的长度都是有很多bit的 . 所以使用bloom filter内存上通常都是节省的 . 
扩展:
Bloom filter将集合中的元素映射到位数组中,用k(k为哈希函数个数)个映射位是否全1表示元素在不在这个集合中 . Counting bloom filter(CBF)将位数组中的每一位扩展为一个counter,从而支持了元素的删除操作 . Spectral Bloom Filter(SBF)将其与集合元素的出现次数关联 . SBF采用counter中的最小值来近似表示元素的出现频率 . 
问题实例:给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL . 如果是三个乃至n个文件呢?
根据这个问题我们来计算下内存的占用,4G=2^32大概是40亿8大概是340亿,n=50亿,如果按出错率0.01算需要的大概是650亿个bit . 现在可用的是340亿,相差并不多,这样可能会使出错率上升些 . 另外如果这些urlip是一一对应的,就可以转换成ip,则大大简单了 . 
二 Hashing
适用范围:快速查找,删除的基本数据结构,通常需要总数据量可以放入内存
基本原理及要点:
hash函数选择,针对字符串,整数,排列,具体相应的hash方法 . 
碰撞处理,一种是open hashing,也称为拉链法;另一种就是closed hashing,也称开地址法,opened addressing . 
扩展:
d-left hashing中的d是多个的意思,我们先简化这个问题,看一看2-left hashing . 2-left hashing指的是将一个哈希表分成长度相等的两半,分别叫做T1和T2,给T1和T2分别配备一个哈希函数,h1和h2 . 在存储一个新的key时,同时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key] . 这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,哪一个位置已经存储的(有碰撞的)key比较多,然后将新key存储在负载少的位置 . 如果两边一样多,比如两个位置都为空或者都存储了一个key,就把新key存储在左边的T1子表中,2-left也由此而来 . 在查找一个key时,必须进行两次hash,同时查找两个位置 . 
问题实例:
1).海量日志数据,提取出某日访问百度次数最多的那个IP . 
IP的数目还是有限的,最多2^32个,所以可以考虑使用hash将ip直接存入内存,然后进行统计 . 
三 bit-map
适用范围:可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下
基本原理及要点:使用bit数组来表示某些元素是否存在,比如8位电话号码
扩展:bloom filter可以看做是对bit-map的扩展
问题实例:
1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数 . 
8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可 . 
2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数 . 
将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上 . 或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map . 
四 堆
适用范围:海量数据前n大,并且n比较小,堆可以放入内存
基本原理及要点:最大堆求前n小,最小堆求前n大 . 方法,比如求前n小,我们比较当前元素与最大堆里的最大元素,如果它小于最大元素,则应该替换那个最大元素 . 这样最后得到的n个元素就是最小的n个 . 适合大数据量,求前n小,n的大小比较小的情况,这样可以扫描一遍即可得到所有的前n元素,效率很高 . 
扩展:双堆,一个最大堆与一个最小堆结合,可以用来维护中位数 . 
问题实例:
1)100w个数中找最大的前100个数 . 
用一个100个元素大小的最小堆即可 . 
五 双层桶划分----其实本质上就是[分而治之]的思想,重在分的技巧上!
适用范围:第k大,中位数,不重复或重复的数字
基本原理及要点:因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行 . 可以通过多次缩小,双层只是一个例子 . 
扩展:
问题实例:
1).2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数 . 
有点像鸽巢原理,整数个数为2^32,也就是,我们可以将这2^32个数,划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域,然后不同的区域在利用bitmap就可以直接解决了 . 也就是说只要有足够的磁盘空间,就可以很方便的解决 . 
2).5亿个int找它们的中位数 . 
这个例子比上面那个更明显 . 首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数 . 然后第二次扫描我们只统计落在这个区域中的那些数就可以了 . 
实际上,如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度 . 即可以先将int64分成2^24个区域,然后确定区域的第几大数,在将该区域分成2^20个子区域,然后确定是子区域的第几大数,然后子区域里的数的个数只有2^20,就可以直接利用direct addr table进行统计了 . 
六 数据库索引
适用范围:大数据量的增删改查
基本原理及要点:利用数据的设计实现方法,对海量数据的增删改查进行处理 . 
七 倒排索引(Inverted index)
适用范围:搜索引擎,关键字查询
基本原理及要点:为何叫倒排索引?一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射 . 
以英文为例,下面是要被索引的文本:T0 = "it is what it is" T1 = "what is it" T2 = "it is a banana"
我们就能得到下面的反向文件索引:
"a":{2} "banana":{2} "is":{0, 1, 2} "it":{0, 1, 2} "what":{0, 1}
检索的条件"what","is"和"it"将对应集合的交集 . 
正向索引开发出来用来存储每个文档的单词的列表 . 正向索引的查询往往满足每个文档有序频繁的全文查询和每个单词在校验文档中的验证这样的查询 . 在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列 . 也就是说文档指向了它包含的那些单词,而反向索引则是单词指向了包含它的文档,很容易看到这个反向的关系 . 
扩展:
问题实例:文档检索系统,查询那些文件包含了某单词,比如常见的学术论文的关键字搜索 . 
八 外排序
适用范围:大数据的排序,去重
基本原理及要点:外排序的归并方法,置换选择败者树原理,最优归并树
扩展:
问题实例:
1).有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M . 返回频数最高的100个词 . 
这个数据具有很明显的特点,词的大小为16个字节,但是内存只有1m做hash有些不够,所以可以用来排序 . 内存可以当输入缓冲区使用 . 
九 trie树
适用范围:数据量大,重复多,但是数据种类小可以放入内存
基本原理及要点:实现方式,节点孩子的表示方式
扩展:压缩实现 . 
问题实例:
1).有10个文件,每个文件1G,每个文件的每一行都存放的是用户的query,每个文件的query都可能重复 . 要你按照query的频度排序 . 
2).1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串 . 请问怎么设计和实现?
3).寻找热门查询:查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个,每个不超过255字节 . 
十 分布式处理 mapreduce
适用范围:数据量大,但是数据种类小可以放入内存
基本原理及要点:将数据交给不同的机器去处理,数据划分,结果归约 . 
扩展:
问题实例:
1).The canonical example application of MapReduce is a process to count the appearances ofeach different word in a set of documents:
2).海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10 . 
3).一共有N个机器,每个机器上有N个数 . 每个机器最多存O(N)个数并对它们操作 . 如何找到N^2个数的中数(median)?
经典问题分析
上千万or亿数据(有重复),统计其中出现次数最多的前N个数据,分两种情况:可一次读入内存,不可一次读入 . 
可用思路:trie树+堆,数据库索引,划分子集分别统计,hash,分布式计算,近似统计,外排序
所谓的是否能一次读入内存,实际上应该指去除重复后的数据量 . 如果去重后数据可以放入内存,我们可以为数据建立字典,比如通过 map,hashmap,trie,然后直接进行统计即可 . 当然在更新每条数据的出现次数的时候,我们可以利用一个堆来维护出现次数最多的前N个数据,当然这样导致维护次数增加,不如完全统计后在求前N大效率高 . 
如果数据无法放入内存 . 一方面我们可以考虑上面的字典方法能否被改进以适应这种情形,可以做的改变就是将字典存放到硬盘上,而不是内存,这可以参考数据库的存储方法 . 
当然还有更好的方法,就是可以采用分布式计算,基本上就是map-reduce过程,首先可以根据数据值或者把数据hash(md5)后的值,将数据按照范围划分到不同的机子,最好可以让数据划分后可以一次读入内存,这样不同的机子负责处理各种的数值范围,实际上就是map . 得到结果后,各个机子只需拿出各自的出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是reduce过程 . 
实际上可能想直接将数据均分到不同的机子上进行处理,这样是无法得到正确的解的 . 因为一个数据可能被均分到不同的机子上,而另一个则可能完全聚集到一个机子上,同时还可能存在具有相同数目的数据 . 比如我们要找出现次数最多的前100个,我们将1000万的数据分布到10台机器上,找到每台出现次数最多的前 100个,归并之后这样不能保证找到真正的第100个,因为比如出现次数最多的第100个可能有1万个,但是它被分到了10台机子,这样在每台上只有1千个,假设这些机子排名在1000个之前的那些都是单独分布在一台机子上的,比如有1001个,这样本来具有1万个的这个就会被淘汰,即使我们让每台机子选出出现次数最多的1000个再归并,仍然会出错,因为可能存在大量个数为1001个的发生聚集 . 因此不能将数据随便均分到不同机子上,而是要根据hash 后的值将它们映射到不同的机子上处理,让不同的机器处理一个数值范围 . 
而外排序的方法会消耗大量的IO,效率不会很高 . 而上面的分布式方法,也可以用于单机版本,也就是将总的数据根据值的范围,划分成多个不同的子文件,然后逐个处理 . 处理完毕之后再对这些单词的及其出现频率进行一个归并 . 实际上就可以利用一个外排序的归并过程 . 
另外还可以考虑近似计算,也就是我们可以通过结合自然语言属性,只将那些真正实际中出现最多的那些词作为一个字典,使得这个规模可以放入内存 . 

kafka的message包括哪些信息
一个Kafka的Message由一个固定长度的header和一个变长的消息体body组成
header部分由一个字节的magic(文件格式)和四个字节的CRC32(用于判断body消息体是否正常)构成 . 当magic的值为1的时候,会在magic和crc32之间多一个字节的数据:attributes(保存一些相关属性,比如是否压缩 压缩格式等等);如果magic的值为0,那么不存在attributes属性
body是由N个字节构成的一个消息体,包含了具体的key/value消息

怎么查看kafka的offset
0.9版本以上,可以用最新的Consumer client 客户端,有consumer.seekToEnd() / consumer.position() 可以用于得到当前最新的offset:

hadoop的shuffle过程
一 Map端的shuffle
  Map端会处理输入数据并产生中间结果,这个中间结果会写到本地磁盘,而不是HDFS . 每个Map的输出会先写到内存缓冲区中,当写入的数据达到设定的阈值时,系统将会启动一个线程将缓冲区的数据写到磁盘,这个过程叫做spill . 
  在spill写入之前,会先进行二次排序,首先根据数据所属的partition进行排序,然后每个partition中的数据再按key来排序 . partition的目是将记录划分到不同的Reducer上去,以期望能够达到负载均衡,以后的Reducer就会根据partition来读取自己对应的数据 . 接着运行combiner(如果设置了的话),combiner的本质也是一个Reducer,其目的是对将要写入到磁盘上的文件先进行一次处理,这样,写入到磁盘的数据量就会减少 . 最后将数据写到本地磁盘产生spill文件(spill文件保存在{mapred.local.dir}指定的目录中,Map任务结束后就会被删除) . 
  最后,每个Map任务可能产生多个spill文件,在每个Map任务完成前,会通过多路归并算法将这些spill文件归并成一个文件 . 至此,Map的shuffle过程就结束了 . 
二 Reduce端的shuffle
  Reduce端的shuffle主要包括两个阶段,copy sort(merge) . 
  首先要将Map端产生的输出文件拷贝到Reduce端,但每个Reducer如何知道自己应该处理哪些数据呢?因为Map端进行partition的时候,实际上就相当于指定了每个Reducer要处理的数据(partition就对应了Reducer),所以Reducer在拷贝数据的时候只需拷贝与自己对应的partition中的数据即可 . 每个Reducer会处理一个或者多个partition,但需要先将自己对应的partition中的数据从每个Map的输出结果中拷贝过来 . 
  接下来就是sort阶段,也成为merge阶段,因为这个阶段的主要工作是执行了归并排序 . 从Map端拷贝到Reduce端的数据都是有序的,所以很适合归并排序 . 最终在Reduce端生成一个较大的文件作为Reduce的输入 . 

spark集群运算的模式
Spark 有很多种模式,最简单就是单机本地模式,还有单机伪分布式模式,复杂的则运行在集群中,目前能很好的运行在 Yarn和 Mesos 中,当然 Spark 还有自带的 Standalone 模式,对于大多数情况 Standalone 模式就足够了,如果企业已经有 Yarn 或者 Mesos 环境,也是很方便部署的 . 
standalone(集群模式):典型的Mater/slave模式,不过也能看出Master是有单点故障的;Spark支持ZooKeeper来实现 HA
on yarn(集群模式):运行在 yarn 资源管理器框架之上,由 yarn 负责资源管理,Spark 负责任务调度和计算
on mesos(集群模式):运行在 mesos 资源管理器框架之上,由 mesos 负责资源管理,Spark 负责任务调度和计算
on cloud(集群模式):比如 AWS 的 EC2,使用这个模式能很方便的访问 Amazon的 S3;Spark 支持多种分布式存储系统:HDFS 和 S3


Yarn的基本思想是拆分资源管理的功能,作业调度/监控到单独的守护进程 . 
ResourceManager是全局的,负责对于系统中的所有资源有最高的支配权 . 
ApplicationMaster 每一个job有一个ApplicationMaster  . 
NodeManager,NodeManager是基本的计算框架 . 

spark2.0的了解
更简单:ANSI SQL与更合理的API
速度更快:用Spark作为编译器
更智能:Structured Streaming

rdd 怎么分区宽依赖和窄依赖
宽依赖:父RDD的分区被子RDD的多个分区使用   例如 groupByKey reduceByKey sortByKey等操作会产生宽依赖,会产生shuffle
窄依赖:父RDD的每个分区都只被子RDD的一个分区使用  例如map filter union等操作会产生窄依赖

spark streaming 读取kafka数据的两种方式
这两种方式分别是:
Receiver-base
使用Kafka的高层次Consumer API来实现 . receiver从Kafka中获取的数据都存储在Spark Executor的内存中,然后Spark Streaming启动的job会去处理那些数据 . 然而,在默认的配置下,这种方式可能会因为底层的失败而丢失数据 . 如果要启用高可靠机制,让数据零丢失,就必须启用Spark Streaming的预写日志机制(Write Ahead Log,WAL) . 该机制会同步地将接收到的Kafka数据写入分布式文件系统(比如HDFS)上的预写日志中 . 所以,即使底层节点出现了失败,也可以使用预写日志中的数据进行恢复 . 
Direct
Spark1.3中引入Direct方式,用来替代掉使用Receiver接收数据,这种方式会周期性地查询Kafka,获得每个topic+partition的最新的offset,从而定义每个batch的offset的范围 . 当处理数据的job启动时,就会使用Kafka的简单consumer api来获取Kafka指定offset范围的数据 . 

kafka的数据存在内存还是磁盘
Kafka最核心的思想是使用磁盘,而不是使用内存 . 
而且Linux对于磁盘的读写优化也比较多,包括read-ahead和write-behind,磁盘缓存等 . 如果在内存做这些操作的时候,一个是JAVA对象的内存开销很大,另一个是随着堆内存数据的增多,JAVA的GC时间会变得很长,使用磁盘操作有以下几个好处:
磁盘缓存由Linux系统维护,减少了程序员的不少工作 . 
磁盘顺序读写速度超过内存随机读写 . 
JVM的GC效率低,内存占用大 . 使用磁盘可以避免这一问题 . 
系统冷启动后,磁盘缓存依然可用 . 

怎么解决kafka的数据丢失
producer端:
宏观上看保证数据的可靠安全性,肯定是依据分区数做好数据备份,设立副本数 . 
broker端:
topic设置多分区,分区自适应所在机器,为了让各分区均匀分布在所在的broker中,分区数要大于broker数 . 
分区是kafka进行并行读写的单位,是提升kafka速度的关键 . 
Consumer端
consumer端丢失消息的情形比较简单:如果在消息处理完成前就提交了offset,那么就有可能造成数据的丢失 . 由于Kafka consumer默认是自动提交位移的,所以在后台提交位移前一定要保证消息被正常处理了,因此不建议采用很重的处理逻辑,如果处理耗时很长,则建议把逻辑放到另一个线程中去做 . 为了避免数据丢失,现给出两点建议:
enable.auto.commit=false  关闭自动提交位移
在消息被完整处理之后再手动提交位移

zookeeper 在 kafka 中起到什么作用
Controller 选举
Kafka集群中的其中一个 Broker 会被选举为Controller, 其负责维护所有 Partition 的 leader/follower 关系(Partition 管理和副本状态管理) . 当有 partition 的 leader 挂掉之后,controller 会从 follower 中选出一个 leader,也会执行类似于重分配 Partition 之类的管理任务 . 
==Zookeeper 负责从 Broker 中选举出一个作为 Controller, 并确保其唯一性 .  同时, 当Controller 宕机时, 选举一个新的 . ==
集群 membership
==记录集群中都有哪些活跃着的Broker . ==
Topic 配置
==记录有哪些Topic, Topic 都有哪些 Partition,Replica 存放在哪里, Leader 是谁 . ==
==在 consumer group 发生变化时进行 rebalance . ==
配额(0.9.0+)
记录每个客户能够读写的数据量 . 
ACLs(0.9.0+)
记录对Topic 的读写控制 . 

kafka 在 zookeeper 上创建的目录结构

注册的节点如下:
==consumers admin config controller brokers controller_epoch==
topic 注册信息
/brokers/topics/[topic]:存储某个 topic 的 partitions 所有分配信息
partition状态信息
/brokers/topics/[topic]/partitions/[0...N]  其中[0..N]表示partition索引号
/brokers/topics/[topic]/partitions/[partitionId]/state
Broker注册信息
/brokers/ids/[0...N]
每个broker的配置文件中都需要指定一个数字类型的id(全局不可重复),此节点为临时znode(EPHEMERAL)
Controller epoch
/controller_epoch -> int (epoch)
此值为一个数字,kafka集群中第一个broker第一次启动时为1,以后只要集群中center controller中央控制器所在broker变更或挂掉,就会重新选举新的center controller,每次center controller变更controller_epoch值就会 + 1
Controller注册信息
/controller -> int (broker id of the controller)  存储center     controller中央控制器所在kafka broker的信息 . 这个值默认是 1,当 controller 节点挂掉后重新选举 controller 后,值会 +1
Consumer注册信息
每个consumer都有一个唯一的ID(consumerId可以通过配置文件指定,也可以由系统生成),此id用来标记消费者信息
/consumers/[groupId]/ids/[consumerIdString]
Consumer owner
/consumers/[groupId]/owners/[topic]/[partitionId] -> consumerIdString + threadId索引编号

kafka consumer 均衡算法
当一个group中,有consumer加入或者离开时,会触发partitions均衡 . 均衡的最终目的,是提升topic的并发消费能力 . 

1假如 topic1 具有如下 partitions:P0,P1,P2,P3
2假如 group 中有如下 consumer:C0,C1
3首先根据 partition 索引号对 partitions 排序:P0,P1,P2,P3
4根据(consumer.id + '-'+ thread序号)排序:C0,C1
5计算倍数:M = [P0,P1,P2,P3].size / [C0,C1].size,本例值 M = 2 (向上取整)
6然后依次分配 partitions:C0 = [P0,P1],C1=[P2,P3],即 Ci = [P(i  M),P((i + 1)  M -1)]

kafka 数据高可用的原理是什么
一致性定义:若某条消息对Consumer可见,那么即使Leader宕机了,在新Leader上数据依然可以被读到
HighWaterMark简称HW:Partition的高水位,取一个partition对应的ISR中最小的LEO作为HW,消费者最多只能消费到HW所在的位置,另外每个replica都有highWatermark,leader和follower各自负责更新自己的highWatermark状态,highWatermark <= leader. LogEndOffset
对于Leader新写入的msg,Consumer不能立刻消费,Leader会等待该消息被所有ISR中的replica同步后,更新HW,此时该消息才能被Consumer消费,即Consumer最多只能消费到HW位置
这样就保证了如果Leader Broker失效,该消息仍然可以从新选举的Leader中获取 . 对于来自内部Broker的读取请求,没有HW的限制 . 同时,Follower也会维护一份自己的HW,Followr.HW = min(Leader.HW, Follower.offset)

kafka 的数据可靠性保证
当Producer向Leader发送数据时,可以通过acks参数设置数据可靠性的级别
0:不论写入是否成功,server不需要给Producer发送Response,如果发生异常,server会终止连接,触发Producer更新meta数据;
1:Leader写入成功后即发送Response,此种情况如果Leader fail,会丢失数据
-1:等待所有ISR接收到消息后再给Producer发送Response,这是最强保证
仅设置acks=-1也不能保证数据不丢失,当Isr列表中只有Leader时,同样有可能造成数据丢失 . 要保证数据不丢除了设置acks=-1, 还要保证ISR的大小大于等于2,具体参数设置:
1.request.required.acks:设置为-1 等待所有ISR列表中的Replica接收到消息后采算写成功;
2.min.insync.replicas:设置为大于等于2,保证ISR中至少有两个Replica
注意:Producer要在吞吐率和数据可靠性之间做一个权衡

kafka partition 分区的策略是什么
消息发送到哪个分区上,有两种基本的策略,一是采用 Key Hash 算法,一是采用 Round Robin 算法 . 另外创建分区时,最好是 broker 数量的整数倍,这样才能让一个 Topic 的分区均匀的分布在整个 Kafka 集群中 . 
默认情况下,Kafka 根据传递消息的 key 来进行分区的分配,即 hash(key) % numPartitions . 
如果发送消息时没有指定key,那么 Producer 将会把这条消息发送给随机的一个 Partition . 但是代码层面的逻辑并不完全是这样 . 首先看看Kafka有没有缓存的现成的分区Id,如果有的话直接使用这个分区Id . 如果没有的话,找出所有可用分区的leader所在的broker,从中随机挑一个并放到缓存中,下次就直接从缓存中拿这个 partition id . 注意这个缓存是每隔一段时间就会被清空的 . 这么做的目的是为了减少服务器端的sockets数 . 

Kafka Producer是如何动态感知Topic分区数变化
问题是,如果在 Kafka Producer 往 Kafka 的 Broker 发送消息的时候用户通过命令修改了改主题的分区数,Kafka Producer 能动态感知吗?答案是可以的 . 那是立刻就感知吗?不是,是过一定的时间(topic.metadata.refresh.interval.ms参数决定)才知道分区数改变的 . 

Spark 任务提交过程源码
Driver 程序的代码运行到 action 操作,触发了 SparkContext 的 runJob 方法
SparkContext 调用 DAGScheduler 的 runJob 函数,内部调用 DAGScheduler 的 submitJob 方法,返回一个 JobWaiter 对象 . 接着向 EventProcessLoop 的阻塞队列中 put 一个 JobSubmitted 事件 . 
这时候 DAGScheduler 的 onReceive 方法被调用,模式匹配,调用 handleJobSubmitted 方法,用来切分 stage . stage 的划分过程是递归调用,从前往后的划分 stage . 
根据 final stage 递归找到第一个 stage,然后将第一个 stage 提交 . 
由于 stage 的类型不同,这里会有两种不同类型的 task,ShuffleMapTask 和 ResultTask . 把 task 封装到 taskSet 里,把 Tasks 交给 TaskScheduler . (RPC 调用,向 Executor 提交 task)
Executor 将 task 封装到 TaskRunner 对象中,将 taskRunner 放入到 Executor 中的线程池中 . 
最后会调用 ShuffleMapTask 或 ResultTask 的 runTask 方法,执行业务逻辑 . 

Spark 部署的三种模式介绍
standalone模式,即独立模式,自带完整的服务,可单独部署到一个集群中,无需依赖任何其他资源管理系统 . 目前 Spark 在 standalone 模式下是没有任何单点故障问题的,这是借助zookeeper实现的,思想类似于Hbase master单点故障解决方案 . 
Spark On Mesos模式 . 这是很多公司采用的模式,官方推荐这种模式(当然,原因之一是血缘关系) . 正是由于Spark开发之初就考虑到支持Mesos,因此,目前而言,Spark运行在Mesos上会比运行在YARN上更加灵活,更加自然 . 目前在Spark On Mesos环境中,用户可选择两种调度模式之一运行自己的应用程序(可参考Andrew Xia的"Mesos Scheduling Mode on Spark"):
粗粒度模式(Coarse-grained Mode):每个应用程序的运行环境由一个Dirver和若干个Executor组成,其中,每个Executor占用若干资源,内部可运行多个Task(对应多少个"slot") . 应用程序的各个任务正式运行之前,需要将运行环境中的资源全部申请好,且运行过程中要一直占用这些资源,即使不用,最后程序运行结束后,回收这些资源 . 
细粒度模式(Fine-grained Mode):鉴于粗粒度模式会造成大量资源浪费,Spark On Mesos还提供了另外一种调度模式:细粒度模式,这种模式类似于现在的云计算,思想是按需分配 . 与粗粒度模式一样,应用程序启动时,先会启动executor,但每个executor占用资源仅仅是自己运行所需的资源,不需要考虑将来要运行的任务,之后,mesos会为每个executor动态分配资源,每分配一些,便可以运行一个新任务,单个Task运行完之后可以马上释放对应的资源 . 每个Task会汇报状态给Mesos slave和Mesos Master,便于更加细粒度管理和容错,这种调度模式类似于MapReduce调度模式,每个Task完全独立,优点是便于资源控制和隔离,但缺点也很明显,短作业运行延迟大 . 
Spark On YARN模式 . 这是一种最有前景的部署模式 . 但限于YARN自身的发展,目前仅支持粗粒度模式(Coarse-grained Mode) . 这是由于YARN上的Container资源是不可以动态伸缩的,一旦Container启动之后,可使用的资源不能再发生变化,不过这个已经在YARN计划中了 . 
spark on yarn 的支持两种模式:
1. yarn-cluster:适用于生产环境;
2. yarn-client:适用于交互 调试,希望立即看到app的输出
yarn-cluster 和 yarn-client 的区别在于 yarn appMaster,每个 yarn app 实例有一个 appMaster 进程,是为 app 启动的第一个 container;负责从 ResourceManager 请求资源,获取到资源后,告诉 NodeManager 为其启动 container . 
yarn-cluster和yarn-client模式的区别其实就是Application Master进程的区别,yarn-cluster模式下,driver运行在AM(Application Master)中,它负责向YARN申请资源,并监督作业的运行状况 . 当用户提交了作业之后,就可以关掉Client,作业会继续在YARN上运行 . 然而yarn-cluster模式不适合运行交互类型的作业 . 而yarn-client模式下,Application Master仅仅向YARN请求executor,client会和请求的container通信来调度他们工作,也就是说Client不能离开 . 

如果集群是spark专用的,可以用standalone,是和别的应用共享的建议用yarn

会产生 shuffle 的算子
combineByKey reduceByKey groupByKey cogroup join leftOutJoin rightOutJoin

Spark Streaming 和 Storm 的区别
处理模型,延迟
虽然这两个框架都提供可扩展性和容错性,它们根本的区别在于他们的处理模型 . 而 Storm 处理的是每次传入的一个事件,而 Spark Streaming 是处理某个时间段窗口内的事件流 . 因此,Storm 处理一个事件可以达到秒内的延迟,而 Spark Streaming 则有几秒钟的延迟 . 
容错 数据保证
在容错数据保证方面的权衡是,Spark Streaming 提供了更好的支持容错状态计算 . 在 Storm 中,每个单独的记录当它通过系统时必须被跟踪,所以 Storm 能够至少保证每个记录将被处理一次,但是在从错误中恢复过来时候允许出现重复记录 . 这意味着可变状态可能不正确地被更新两次 . 
另一方面,Spark Streaming 只需要在批级别进行跟踪处理,因此可以有效地保证每个 mini-batch 将完全被处理一次,即便一个节点发生故障 . (实际上 Storm 的 Trident library 库也提供了完全一次处理 . 但是,它依赖于事务更新状态,这比较慢,通常必须由用户实现) . 
总结
简而言之,如果你需要秒内的延迟,Storm 是一个不错的选择,而且没有数据丢失 . 如果你需要有状态的计算,而且要完全保证每个事件只被处理一次,Spark Streaming 则更好 . Spark Streaming 编程逻辑也可能更容易,因为它类似于批处理程序(Hadoop),特别是在你使用批次(尽管是很小的)时 . 

Tungsten-sort 实现了内存的自主管理,管理方式模拟了操作系统的方式,通过Page可以使得大量的record被顺序存储在内存,整个shuffle write 排序的过程只需要对指针进行运算(二进制排序),并且无需反序列化,整个过程非常高效,对于减少GC,提高内存访问效率,提高CPU使用效率确实带来了明显的提升 . 

spark shuffle 有:hash,sort,tungsten-sort . 
Spark的Shuffle总体而言就包含两个基本的过程:Shuffle write和Shuffle read . ShuffleMapTask的整个执行过程就是Shuffle write . hash-based机制就是在Shuffle的过程中写数据时不做排序操作,区别于MapReduce . 只是将数据根据Hash的结果,将各个Reduce分区的数据写到各自的磁盘文件中 . 
首先是将map的输出结果送到对于的缓冲区bucket里面,分配bucket的过程同样也是个hash进行分区的过程,在hashed-based下每一个bucket对应一个最终的reducer,在处理完之后bucket里的数据会自动划分到reducer的bucket里面 . 每个bucket里的文件会被写入本地磁盘文件ShuffleBlockFile中,形成一个FileSegment文件 . 
Shuffle read指的是reducer对属于自己的FileSegment文件进行fetch操作,这里采用的netty框架,效率明显好于Mapreduce的http传输 . fetch操作会等到所有的Shuffle Write过程结束后再进行,这也是因为ShuffleMapTask可能并不在一个stage里面,需要在父stage执行之后提交才会进行子stage的执行 . reducer通过fetch得到的FileSegment先放在缓冲区softBuffer中,默认大小48MB . Spark不要求Shuffle后的数据是全局有序的,所以没有必要等到shuffle read全部结束后再进行reduce,是可以并行处理的 . 

[Spark 版本]
val conf=new SparkConf().setAppName("wd").setMaster("local[2]")
val sc=new SparkContext(conf)
//加载
val lines=sc.textFile("tructField("name",DataTypes.StringType,true)")
val paris=lines.flatMap(line=>line.split("^A"))
val words=paris.map((_,1))
val result=words.reduceByKey(_+_).sortBy(x=>x._1,false)
//打印
result.foreach(
wds=>{
println("单词:"+wds._1+" 个数:"+wds._2)
}
)
sc.stop()

[spark sql版本]
val conf=new SparkConf().setAppName("sqlWd").setMaster("local[2]")
val sc=new SparkContext(conf)
val sqlContext=new SQLContext(sc)
//加载
val lines=sqlContext.textFile("E:\idea15\createRecommeder\data\words.txt")
val words=lines.flatMap(x=>x.split(" ")).map(y=>Row(y))
val structType=StructType(Array(StructField("name",DataTypes.StringType,true)))
val df=sqlContext.createDataFrame(words,structType)
df.registerTempTable("t_word_count")
sqlContext.udf.register("num_word",(name:String)=>1)
sqlContext.sql("select name,num_word(name) from t_word_count").groupBy(df.col("name")).count().show()
sc.stop()

Spark应用程序的运行是通过外部参数来控制的,参数的设置正确与否,好与坏会直接影响应用程序的性能,也就影响我们整个集群的性能 . 


hive的使用,内外部表的区别,分区作用,UDF和Hive优化
(1)hive使用:仓库 工具
(2)hive内外部表:内部表数据永久删除,外部表数据删除后 其他人依然可以访问
(3)分区作用:防止数据倾斜
(4)UDF函数:用户自定义的函数(主要解决格式,计算问题),需要继承UDF类
java代码实现
class TestUDFHive extends UDF {
public String evalute(String str){
try{
   return "hello"+str
}catch(Exception e){
   return str+"error"
}
}
}
(5)Hive优化:看做mapreduce处理
 a\排序优化:sort by 效率高于 order by
 b\分区:使用静态分区 (statu_date="20160516",location="beijin"),每个分区对应hdfs上的一个目录
 c\减少job和task数量:使用表链接操作
 d\解决groupby数据倾斜问题:设置hive.groupby.skewindata=true ,那么hive会自动负载均衡
 e\小文件合并成大文件:表连接操作
 f\使用UDF或UDAF函数

Hbase的rk设计,Hbase优化
  a\rowkey:hbase三维存储中的关键(rowkey:行键 ,columnKey(family+qualifier):列键  ,timestamp:时间戳)
   \rowkey字典排序 越短越好
   \使用id+时间:9527+20160517 \使用hash散列:dsakjkdfuwdsf+9527+20160518
   \应用中,rowkey 一般10~100bytes,8字节的整数倍,有利于提高操作系统性能
  b\Hbase优化
   \分区:RegionSplit()方法 \NUMREGIONS=9
   \column不超过3个
   \硬盘配置,便于regionServer管理和数据备份及恢复
   \分配合适的内存给regionserver
   其他:
   hbase查询
   (1)get
   (2)scan
       使用startRow和endRow限制   

java线程2种方式实现 设计模式 链表操作 排序
(1)2种线程实现
   a\Thread类继承
   TestCL th=new TestCL()//类继承Thread
   th.start()
   b\实现Runnable接口
   Thread th=new Thread(new Runnable(){
public void run(){
//实现
}
   })
   th.start()
设计模式,分为4类
  a\创建模式:如工厂模式 单例模式
  b\结构模式:代理模式
  c\行为模式:观察者模式
  d\线程池模式

工厂模式简单的讲就是用工厂方法代替了new的操作,
在通俗点就是说,你new一个对象的时候直接调用工厂方法就行了,
在编程时,需要定义一个工厂接口,由不同的的子类去实现,再定一些具体工厂类,定义一个产生实例的方法,我们通过这个方法来获得实例就行了

cdh集群监控
(1)数据库监控 (2)主机监控 (3)服务监控 (4)活动监控

计算机网络工作原理
将分散的机器通过数据通信原理连接起来,实现共享!

hadoop生态系统
hdfs\mapreduce\hive\hbase\zookeeper\flume

jvm运行机制及内存原理
运行:
I加载.class文件
II管理并且分配内存
III垃圾回收
内存原理:
IJVM装载环境和配置
II装载JVM.dll 并初始化JVM.dll
IV 处理class类

\hdfs yarn参数调优
mapreduce.job.jvm.num.tasks 
默认为1,设置为 -1,重用jvm,,也就是执行过一个task后就销毁,可以设置为-1, 一
直保持重用.

InputFormat在默认情况下会根据hadoop集群HDFS块大小进行分片,每一个分片会由一个map任务来进行处理,当然用户还是可以通过参数mapred.min.split.size参数在作业提交客户端进行自定义设置 . 还有一个重要参数就是mapred.map.tasks,这个参数设置的map数量仅仅是一个提示,只有当InputFormat决定了map任务的个数比mapred.map.tasks值小时才起作用 . 同样,Map任务的个数也能通过使用JobConf的conf.setNumMapTasks(int num)方法来手动地设置 . 这个方法能够用来增加map任务的个数,但是不能设定任务的个数小于Hadoop系统通过分割输入数据得到的值 . 
很多文档中描述,Mapper的数量在默认情况下不可直接控制干预,因为Mapper的数量由输入的大小和个数决定 . 在默认情况下,最终input占据了多少block,就应该启动多少个Mapper . 如果输入的文件数量巨大,但是每个文件的size都小于HDFS的blockSize,那么会造成启动的Mapper等于文件的数量(即每个文件都占据了一个block),那么很可能造成启动的Mapper数量超出限制而导致崩溃 . 这些逻辑确实是正确的,但都是在默认情况下的逻辑 . 其实如果进行一些客户化的设置,就可以控制了 . 

有可能使hadoop任务输出到多个目录中么?如果可以,怎么做?
答案:在1.X版本后使用MultipleOutputs.java类实现
源码:
MultipleOutputs.addNamedOutput(conf, "text2", TextOutputFormat.class, Long.class, String.class);
MultipleOutputs.addNamedOutput(conf, "text3", TextOutputFormat.class, Long.class, String.class);

如何为一个hadoop任务设置要创建的reducer的数量
答案:job.setNumReduceTask(int n)
或者调整hdfs-site.xml中的mapred.tasktracker.reduce.tasks.maximum(一个节点Reduce任务数量上限由mapreduce.tasktracker.reduce.tasks.maximum设置(默认2))

两个类TextInputFormat和KeyValueTextInputFormat的区别?
答案:
FileInputFormat的子类:
TextInputFormat(默认类型,键是LongWritable类型,值为Text类型,key为当前行在文件中的偏移量,value为当前行本身);
KeyValueTextInputFormat(适合文件自带key,value的情况,只要指定分隔符即可,比较实用,默认是\t分割);
源码:
   String sepStr =job.get("mapreduce.input.keyvaluelinerecordreader.key.value.separator","\t");
   注意:在自定义输入格式时,继承FileInputFormat父类

在一个运行的hadoop任务中,什么是InputSpilt?
答案:InputSplit是MapReduce对文件进行处理和运算的输入单位,只是一个逻辑概念,每个InputSplit并没有对文件实际的切割,只是记录了要处理的数据的位置(包括文件的path和hosts)和长度(由start和length决定),默认情况下与block一样大 . 

分别举例什么情况下使用combiner,什么情况下不会使用?
答案:Combiner适用于对记录汇总的场景(如求和),但是,求平均数的场景就不能使用Combiner了

Hadoop中job和Tasks之间的区别是什么?
答案:
job是工作的入口,负责控制 追踪 管理任务,是进程
    包含map task和reduce task
Tasks是map和reduce里面的步骤,主要用于完成任务,是线程

结果查看监控日志,得知产生这种现象的原因是数据倾斜问题
解决:
(1)解决的方法可以在分区的时候重新定义分区规则对于value数据很多的key可以进行拆分 均匀打散等处理,或者是在map端的combiner中进行数据预处理的操作 . 
(2)增加jvm
(3)适当地将reduce的数量变大

流API中的什么特性带来可以使map reduce任务可以以不同语言(如perl\ruby\awk等)实现的灵活性?
答案:用可执行文件作为Mapper和Reducer,接受的都是标准输入,输出的都是标准输出

参考下面的M/R系统的场景:
--HDFS块大小为64MB
--输入类型为FileInputFormat
--有3个文件的大小分别是:64k 65MB 127MB
Hadoop框架会把这些文件拆分为多少块?
答案:
64k------->一个block
65MB---->两个文件:64MB是一个block,1MB是一个block
127MB--->两个文件:64MB是一个block,63MB是一个block

Hadoop中的RecordReader的作用是什么?
答案:属于split和mapper之间的一个过程 
      将inputsplit输出的行为一个转换记录,成为key-value的记录形式提供给mapper

加入一个新的存储节点和删除一个计算节点,需要刷新集群状态命令,怎么操作
HDFS增加节点
方式1:静态添加datanode,停止namenode方式
1.停止namenode 
2.修改slaves文件,并更新到各个节点
3.启动namenode 
4.执行Hadoop balance命令 . (此项为balance集群使用,如果只是添加节点,则此步骤不需要)
方式2:动态添加datanode,不停namenode方式
1.修改slaves文件,添加需要增加的节点host或者ip,并将其更新到各个节点 
2.在datanode中启动执行启动datanode命令 . 命令:sh hadoop-daemon.sh start datanode 
3.可以通过web界面查看节点添加情况 . 或使用命令:sh hadoop dfsadmin -report 
4.执行hadoop balance命令 . (此项为balance集群使用,如果只是添加节点,则此步骤不需要)
针对第4点,start-balancer.sh可以执行-threshold参数 .  
-threshold参数是指定平衡的阈值 .  
-threshold的默认是10,即每个datanode节点的实际hdfs存储使用量/集群hdfs存储量(集群平衡的条件,datanode间磁盘使用率相差阈值,区间选择:0~100)
举例: 
datanode hdfs使用量1.2G; 
集群总hdfs存储量10G; 
则t值为1.2/10 = 0.12; 
当执行balance的-t参数0.1小于0.12时,集群进行balance; 
命令为:start-balancer.sh -threshold 0.1
这个命令中-t参数后面跟的是HDFS达到平衡状态的磁盘使用率偏差值 . 如果机器与机器之间磁盘使用率偏差小于10%,那么我们就认为HDFS集群已经达到了平衡的状态 . 
线上长时间运行的大规模Hadoop集群,各个datanode节点磁盘空间使用率经常会出现分布不均衡的情况,尤其在新增和下架节点 或者人为干预副本数量的时候 . 节点空间使用率不均匀会导致计算引擎频繁在跨节点拷贝数据(A节点上运行的task所需数据在其它节点上),引起不必要的耗时和带宽 . 另外,当部分节点空间使用率很高但未满(90%左右)时,分配在该节点上的task会存在任务失败的风险 . 
注: 
1. balance命令可以在namenode或者datanode上启动; 
可以随时停止balance命令 .  
balance的默认带宽是1M/s .  
2. slave文件是用于重启时使用 . 集群的start和stop需要读取slave文件 .  
启用datanode时只要在hdfs-site中配置了namenode位置,就可以将信息push给namenode .  
查看namenode的http管理界面,可查看节点添加情况 .  
HDFS删除节点
方式1:通过dead方式(namenode上):
1. sh hadoop dfsadmin  -refreshServiceAcl
说明:dead方式并未修改slave文件和hdfs-site文件 .  
所以在集群重启时,该节点不会被添加到namenode的管理中 .  
此次在namenode上进行,其他节点可另行实验 . ,该命令会将该节点状态置为dead .  
方式2:通过decommission方式:
a) 修改hdfs-site,添加exclude字段中的排除的节点 .  
b) 执行sh hadoop dfsadmin -refreshNodes,强制刷新 .  
c) 查看节点状态,该节点的状态为decommission . 
说明:decommission方式修改了hdfs-site文件,未修改slave文件 .  
所以集群重启时,该节点虽然会被启动为datanode,但是由于添加了exclude,所以namenode会将该节点置为decommission .  
此时namenode不会与该节点进行hdfs相关通信 . 也即exclude起到了一个防火墙的作用 . 
注: 
1. 如果在某个节点单独停止datanode,那么在namenode的统计中仍会出现该节点的datanode信息 .  
此时可通过dead或者decommission(退役)方式下线机器 . 

简述一下hdfs的数据压缩算法,工作中用的是那种算法,为什么?
1.在HDFS之上将数据压缩好后,再存储到HDFS
2.在HDFS内部支持数据压缩,这里又可以分为几种方法:
  2.1 压缩工作在DataNode上完成,这里又分两种方法:
    2.1.1 数据接收完后,再压缩
       这个方法对HDFS的改动最小,但效果最低,只需要在block文件close后,调用压缩工具,将block文件压缩一下,然后再打开block文件时解压一下即可,几行代码就可以搞定
    2.1.2 边接收数据边压缩,使用第三方提供的压缩库
        效率和复杂度折中方法,Hook住系统的write和read操作,在数据写入磁盘之前,先压缩一下,但write和read对外的接口行为不变,比如:原始大小为100KB的数据,压缩后大小为10KB,当写入100KB后,仍对调用者返回100KB,而不是10KB
  2.2 压缩工作交给DFSClient做,DataNode只接收和存储
        这个方法效果最好,压缩分散地推给了HDFS客户端,但DataNode需要知道什么时候一个block块接收完成了 . 
推荐最终实现采用2.2这个方法,该方法需要修改的HDFS代码量也不大,但效果最好 . 

Datanode在什么情况下不会备份?
hadoop保存的三个副本如果不算备份的话,那就是在正常运行的情况下不会备份,也是就是在设置副本为1的时候不会备份,说白了就是单台机器

三个 datanode,当有一个datanode出现错误会怎样?
第一不会给储存带来影响,因为有其他的副本保存着,不过建议尽快修复,第二会影响运算的效率,机器少了,reduce在保存数据时选择就少了,一个数据的块就大了所以就会慢 . 
Datanode以数据块作为容错单位 通常一个数据块会备份到三个datanode上,如果一个datanode出错,则回去其他备份数据块的datanode上读取,并且会把这个datanode上的数据块再复制一份 以达到备份的效果

1将本地的小文件合并,上传到HDFS
假设存放在本地的数据由很多个小文件组成,需要上传到HDFS . 一般的做法是在本地使用脚本 程序先把小文件合并后再上传 . 
HDFS中提供了一个很有用的命令 appendToFile,就可以解决这个问题 . 
假设本地有两个小文件1.txt和2.txt,里面内容如下:

使用下面的命令,可以将1.txt和2.txt合并,并上传到HDFS:

下载HDFS的小文件到本地,合并成一个大文件
假设在HDFS的/tmp/lxw1234/目录下,有两个小文件1.txt和2.txt
需要将它们下载到本地的一个文件中 . 


2 如果需要合并HDFS上的某个目录下有很多小文件,可以尝试使用下面的命令:

-put更宽松,可以把本地或者HDFS上的文件拷贝到HDFS中;而-copyFromLocal则更严格限制只能拷贝本地文件到HDFS中 . 

hdfs原理,以及各个模块的职责
Client:切分文件;访问或通过命令行管理HDFS;与NameNode交互,获取文件位置信息;与DataNode交互,读取和写入数据 .  
NameNode:Master节点,只有一个,管理HDFS的名称空间和数据块映射信息;配置副本策略;处理客户端请求 .  
DataNode:Slave节点,存储实际的数据;执行数据块的读写;汇报存储信息给NameNode . 
Secondary NameNode:定期合并fsimage和fsedits,推送给NameNode;

Hdfs文件读取
1.首先调用FileSystem对象的open方法,其实是一个DistributedFileSystem的实例 
2.DistributedFileSystem通过rpc获得文件的第一批个block的locations,同一block按照重复数会返回多个locations,这些locations按照hadoop拓扑结构排序,距离客户端近的排在前面. 
3.前两步会返回一个FSDataInputStream对象,该对象会被封装成DFSInputStream对象,DFSInputStream可以方便的管理datanode和namenode数据流 . 客户端调用read方法,DFSInputStream最会找出离客户端最近的datanode并连接 .  
4.数据从datanode源源不断的流向客户端 .  
5.如果第一块的数据读完了,就会关闭指向第一块的datanode连接,接着读取下一块 . 这些操作对客户端来说是透明的,客户端的角度看来只是读一个持续不断的流 .  
6.如果第一批block都读完了,DFSInputStream就会去namenode拿下一批blocks的location,然后继续读,如果所有的块都读完,这时就会关闭掉所有的流 . 

Hdfs的文件写入
1.客户端通过调用DistributedFileSystem的create方法创建新文件 
2.DistributedFileSystem通过RPC调用namenode去创建一个没有blocks关联的新文件,创建前,namenode会做各种校验,比如文件是否存在,客户端有无权限去创建等 . 如果校验通过,namenode就会记录下新文件,否则就会抛出IO异常. 
3.前两步结束后会返回FSDataOutputStream的对象,和读文件的时候相似,FSDataOutputStream被封装成DFSOutputStream,DFSOutputStream可以协调namenode和datanode . 客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列data quene .  
4.DataStreamer会去处理接受data quene,他先问询namenode这个新的block最适合存储的在哪几个datanode里,比如重复数是3,那么就找到3个最适合的datanode,把他们排成一个pipeline.DataStreamer把packet按队列输出到管道的第一个datanode中,第一个datanode又把packet输出到第二个datanode中,以此类推 .  
5.DFSOutputStream还有一个队列叫ack quene,也是有packet组成,等待datanode的收到响应,当pipeline中的所有datanode都表示已经收到的时候,这时akc quene才会把对应的packet包移除掉 .  
6.客户端完成写数据后调用close方法关闭写入流 
7.DataStreamer把剩余得包都刷到pipeline里然后等待ack信息,收到最后一个ack后,通知datanode把文件标示为已完成 . 

hdfs的体系结构
HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群是由一个NameNode和若干个DataNode组成的 . 其中NameNode作为主服务器,管理文件系统的命名空间(用来储存变量名和对象绑定关系的一个区域)和客户端对文件的访问操作;集群中的DataNode管理存储的数据 . HDFS允许用户以文件的形式存储数据 . 从内部来看,文件被分成若干个数据块,而且这若干个数据块存放在一组DataNode上 . NameNode执行文件系统的命名空间操作,比如打开 关闭 重命名文件或目录等,它也负责数据块到具体DataNode的映射 . DataNode负责处理文件系统客户端的文件读写请求,并在NameNode的统一调度下进行数据块的创建 删除和复制工作 . NameNode和DataNode都被设计成可以在普通商用计算机上运行 . 这些计算机通常运行的是GNU/Linux操作系统 . HDFS采用Java语言开发,因此任何支持Java的机器都可以部署NameNode和DataNode . 一个典型的部署场景是集群中的一台机器运行一个NameNode实例,其他机器分别运行一个DataNode实例 . 当然,并不排除一台机器运行多个DataNode实例的情况 . 集群中单一的NameNode的设计则大大简化了系统的架构 . NameNode是所有HDFS元数据的管理者,用户数据永远不会经过NameNode . 

hdfs 的 client 端,复制到第三个副本时宕机, hdfs 怎么恢复保证下次写第三副本
Datanode会定时上报block块的信息给namenode ,namenode就会得知副本缺失,然后namenode就会启动副本复制流程以保证数据块的备份!Client向NameNode发起文件写入的请求 . NameNode根据文件大小和文件块配置情况,返回给Client它所管理部分DataNode的信息 . Client将文件划分为多个Block,根据DataNode的地址信息,按顺序写入到每一个DataNode块中 . 

hdfs,replica如何定位
public static void getFileLocal() throws IOException{       
         //返回FileSystem对象 
         FileSystem fs = getFileSystem();   
         //文件路径  Path path =  new Path("hdfs://single.hadoop.dajiangtai.com:9000/middle/weibo/weibo.txt"); 
          
         //获取文件目录 
         FileStatus filestatus = fs.getFileStatus(path);  //获取文件块位置列表 
         BlockLocation[] blkLocations =  fs.getFileBlockLocations(filestatus, 0, filestatus.getLen()); 
         //循环输出块信息 
         for(int i=0;i< blkLocations.length;i++)
            {   
                String[] hosts = blkLocations[i].getHosts(); 
            }
         System.out.println("block_"+i+"_location:"+hosts[0]); 
         }

1st replica.如果写请求方所在机器是其中一个datanode,则直接存放在本地,否则随机在集群中选择一个datanode. 
2nd replica.第二个副本存放于不同第一个副本的所在的机架. 
3rd replica.第三个副本存放于第二个副本所在的机架,但是属于不同的节点.


HBase为筛选数据提供了一组过滤器,通过这个过滤器可以在HBase中的数据的多个维度(行,列,数据版本)上进行对数据的筛选操作,也就是说过滤器最终能够筛选的数据能够细化到具体的一个存储单元格上(由行键,列明,时间戳定位) . 通常来说,通过行键,值来筛选数据的应用场景较多 . 

简述一下 HBase 数据库架构组成部分 . 
HMaster HRegionServer HRegion Store MemStore StoreFile HFile HLog等 . 

怎么实现 hbase 的预分区 . 
首先就是要想明白数据的key是如何分布的,然后规划一下要分成多少region,每个region的startkey和endkey是多少,然后将规划的key写到一个文件中 . 比如,key的前几位字符串都是从0001~0010的数字,这样可以分成10个region . 
hbase shell中建分区表,指定分区文件:
create 'split_table_test', 'cf', {SPLITS_FILE => 'region_split_info.txt'}

Hbase 设计表的时候 rowkey 和分区考虑哪个?还是都考虑?
Hbase默认建表时有一个region,这个region的rowkey是没有边界的,即没有startkey和endkey,在数据写入时,所有数据都会写入这个默认的region,随着数据量的不断 增加,此region已经不能承受不断增长的数据量,会进行split,分成2个region . 在此过程中,会产生两个问题:1.数据往一个region上写,会有写热点问题 . 2.region split会消耗宝贵的集群I/O资源 . 基于此我们可以控制在建表的时候,创建多个空region,并确定每个region的起始和终止rowky,这样只要我们的rowkey设计能均匀的命中各个region,就不会存在写热点问题 . 自然split的几率也会大大降低 . 当然随着数据量的不断增长,该split的还是要进行split . 

检索habse的记录首先要通过row key来定位数据行 . 当大量的client访问hbase集群的一个或少数几个节点,造成少数region server的读/写请求过多 负载过大,而其他region server负载却很小,就造成了"热点"现象 . 
大量访问会使热点region所在的单个主机负载过大,引起性能下降甚至region不可用 . 
有大量连续编号的row key  ==>  大量row key相近的记录集中在个别region
 ==>  client检索记录时,对个别region访问过多  ==>  此region所在的主机过载
 ==>  热点

Hbase 过滤器实现原则 . 
 采用bloomfilter进行过滤,Bloom Filter是一种空间效率很高的随机数据结构
(1)BLOOMFILTER在HBase的作用
HBase利用 BLOOMFILTER来提供随机读(GET)的性能,对于顺序读(Scan),设置BLOOMFILTER是没有作用的 . 
(2)BLOOMFILTER在HBase的开销
BLOOMFILTER是一个列族级别的配置,如果你表中设置了BLOOMFILTER,那么HBase在生成StoreFile时候包含一份BLOOMFILTER的结构数据,称为MetaBlock;开启BLOOMFILTER会有一定的存储以及内存的开销 . 
(3)BLOOMFILTER如何提供随机读(GET)的性能
对于某个region的随机读,HBase会遍历读memstore及storefile(按照一定的顺序),将结果合并返回给客户端 . 如果你设置了bloomfilter,那么在遍历读storefile时,就可以利用bloomfilter,忽略某些storefile . 
(4)Region的StoreFile数目越多,BLOOMFILTER效果越好 . 
(5)Region下的storefile数目越少,HBase读性能越好 . 

Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构(很长的二进制向量和一系列随机映射函数),被用来检测一个元素是不是集合中的一个成员 . 
如果检测结果为是,该元素不一定在集合中;但如果检测结果为否,该元素一定不在集合中 . 因此Bloom filter具有100%的召回率 . 这样每个检测请求返回有"在集合内(可能错误)"和"不在集合内(绝对不在集合内)"两种情况,它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难 . 

如何提高 HBase 客户端的读写性能?请举例说明 . 
(1)开启bloomfilter过滤器,开启bloomfilter比没开启要快3 4倍 . 
(2)hbase对于内存有特别的嗜好,在硬件允许的情况下配足够多的内存给它通过修改hbase-env.sh中的:
export HBASE_HEAPSIZE=3000 #这里默认为1000m
(3)修改Java虚拟机属性
替换掉默认的垃圾回收器,因为默认的垃圾回收器在多线程环境下会有更多的wait等待:  
export HBASE_OPTS="-server -XX:NewSize=6m -XX:MaxNewSize=6m -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode"


(4)增大RPC数量
通过修改hbase-site.xml中的hbase.regionserver.handler.count属性,可以适当的放大 . 默认值为10有点小 . 
(5)做程序开发需要注意的地方
//需要判断所求的数据行是否存在时,尽量不要用下面这个方法
HTable.exists(final byte [] row)
//而用带列族的方法替代,比如:
HTable.exists(final byte [] row, final byte[]column)
//判断所求的数据是否存在不要使用
HTable.get(final byte [] row, final byte []column) == null
//而应该用下面的方法替代:
HTable.exists(final byte [] row, final byte[] column)


hive 跟 hbase 的区别是?
1.hbase与hive都是架构在hadoop之上的 . 都是用hadoop作为底层存储
2.Hive是建立在Hadoop之上为了减少MapReduce jobs编写工作的批处理系统,HBase是为了支持弥补Hadoop对实时操作的缺陷的项目  . 
3.想象你在操作RMDB数据库,如果是全表扫描,就用Hive+Hadoop,如果是索引访问,就用HBase+Hadoop  . 
4.Hive query就是MapReduce jobs可以从5分钟到数小时不止,HBase是非常高效的,肯定比Hive高效的多 . 
5.Hive本身不存储和计算数据,它完全依赖于HDFS和MapReduce,Hive中的表存逻辑 . 
6.hive借用hadoop的MapReduce来完成一些hive中的命令的执行
7.hbase是物理表,不是逻辑表,提供一个超大的内存hash表,搜索引擎通过它来存储索引,方便查询操作 . 
8.hbase是列存储 . 
9.hdfs作为底层存储,hdfs是存放文件的系统,而Hbase负责组织文件 . 
10.hive需要用到hdfs存储文件,需要用到MapReduce计算框架 . 

物理表 实际占用你磁盘空间的表
逻辑表 本身不是物理存在的表,而是从物理表上通过查询的映射关系创建的表 . 例如视图


物理结构与逻辑结构
1.逻辑结构:
所谓逻辑结构就是数据与数据之间的关联关系,准确的说是数据元素之间的关联关系 . 
注:所有的数据都是由数据元素构成,数据元素是数据的基本构成单位 . 而数据元素由多个数据项构成 . 
逻辑结构有四种基本类型:集合结构 线性结构 树状结构和网络结构 . 也可以统一的分为线性结构和非线性结构 . 
2.物理结构:
数据的物理结构就是数据存储在磁盘中的方式 . 官方语言为:数据结构在计算机中的表示(又称映像)称为数据的物理结构,或称存储结构 . 它所研究的是数据结构在计算机中的实现方法,包括数据结构中元素的表示及元素间关系的表示 . 
而物理结构一般有四种:顺序存储,链式存储,散列,索引
3.逻辑结构的物理表示:
线性表的顺序存储则可以分为静态和非静态:静态存储空间不可扩展,初始时就定义了存储空间的大小,故而容易造成内存问题 . 
线性表的链式存储:通过传递地址的方式存储数据 . 
单链表:节点存储下一个节点的地址-------------->单循环链表:尾节点存储头结点的地址
双链表:节点存储前一个和后一个节点的地址,存储两个地址 . ---------------->双循环链表:尾节点存储头结点的地址 . 
4.高级语言应用:
数组是顺序存储
指针则是链式存储

HBase 接收数据,如果短时间导入数量过多的话就会被锁,该怎么办?
通过调用Htable.setAutoFlush(false)方法可以将htable写客户端的自动flush关闭,这样可以批量写入到数据到hbase . 而不是有一条put 就执行一次更新,只有当put填满客户端写缓存时,才实际向Hbase 服务端发起请求 . 默认情况下auto flush 是开启的 . 

描述 HBase 搭建过程
1.1 下载和解压
1.2 配置集群并安装ZK集群
由于HBase最终存储数据到DataNode上,故需hadoop的hdfs-site.xml core-site.xml拷贝到hbase/conf下
1.3 修改hbase-env.sh配置文件和hbase-site.xml
修改hbase-env.sh 设置JAVA_HOME和修改HBASE_MANAGES_ZK=false,使用外部ZK.
修改hbase-site.xml 配置文件,指定hdfs存储路径和ZK
1.4 修改regionservers 配置文件
指定HRegionServer的节点,放在regionservers配置文件中
1.5 启动HBase
启动ZK
启动HDFS集群
启动HBase
需要注意的是:hbase-env.sh 配置文件,HBASE_MANAGES_ZK=false,设置外部HBase使用外部的ZK,默认情况下是true . 

hbase 写数据的原理是什么?
客户端读取信息流程
(1)client要读取信息,先查询下client 端的cache中是否存在数据,如果存在,直接返回数据 . 如果不存在,则进入到zookeeper,查找到里面的相应数据存在的Root表中的地址 . 
(2)BlockCache;设计用于读入内存频繁访问的数据,每个列族都有 . 
(3)通过数据存在ROOT表中地址找到.META,最终找到HRegion . 找到HRegion后,它会先访问MemStore中是否存在数据,如果存在,则直接读取 . 如果没有,就再到HFile中查找数据,并将数据放到MemStore . 
(4)最后数据返回到客户端显示 . 
存储数据流程
由于Hbase中默认的刷写方式是隐式刷写,所以你在put()数据时,它会自动保存到HRegion上,但当你批量处理数据时,它会将数据先保存到client端的cache中 . 当你关闭隐式刷写时,你put()的数据则会保存到client cache中,直到你调用刷写命令时,才会保存到HRegion中 . 
在HRegion部分的存储:要写入的数据会先写到HMemcache和Hlog中,HMemcache建立缓存,Hlog同步Hmemcache和Hstore的事务日志,发起Flush Cache时,数据持久化到Hstore中,并清空HMemecache . 
hbase正常写入数据时,会写入两个地方:预写式日志(WAL_or_Hlog)和Memstore(内存里的写入缓冲区), 首先写入cache,并记入WAL,然后才写入MemStore,(都写入才认为动作完成)保证数据的持久化,Hbase中的数据永久写入之前都在MemStore,当MemStore填满后,其中的数据就会写入硬盘生成HFile, 
HBase写数据,如果在写入HStore时发生系统异常,就可以从HLog中恢复数据,重新写 HStore中 . 
Hbase的删除不会立即删除内容,会先打删除标签,直到执行一次大合并(major compaction),被删除的空间才会被释放 
代码层次分析:HTable.put(put)
获取HTable对hTable->hTable.put(put)->put的数据存LinkedList<Row>->若AutoFlush=true,立即发送请求到服务器端,更新hbase;若AutoFlush=false,当缓冲区数据大于指定的HeadSize时,发送服务器更新hbase . 
实际底层是开启多个线程来执行更新数据的 . 


hbase 宕机如何处理?
HLog记录数据的所有变更,一旦region server 宕机,就可以从log中进行恢复 . 
HMaster有一个RegionServerTracker对象,监控zk上/hbase/rs目录下的结点,达到监控region server下线的目的 .
一个region server宕机后,zk上相应结点删除,触发RegionServerTracker的nodeDeleted(),方法调用ServerManager的expireServer逻辑,对于非meta region(0.96后只有一个meta region),提交一个ServerShutdownHandler的任务给内部线程池处理,
任务的处理逻辑在handler的process()中 . HBASE-7006引入了distributed log replay特性,这里以distributed log replay为例 . 
如果开启了distributed log replay特性,那么在zk上建立一系列结点/hbase/recovering-regions/regionEncodeName/serverName,其中regionEncodeName结点内容为该region的last flush sequence id,即这个sequence id之前的所有数据都已经flush到磁盘上产生了HFile文件,这部分数据不需要进行回放 . serverName结点的内容为宕机的region server上的last flushed sequence id,即所有region中最大的last flush sequence id . 将宕掉server上的region assign通过round robin的方式assign其他的活着的region server,然后提交一个LogReplayHandler的任务给内部线程池,这个任务内部就是进行split log的准备工作,将hdfs上该region server的log改名,加上-splitting后缀,变成hbase.rootdir/WALs/serverName-splitting,然后进入HMaster的SplitLogManager管理范围,在zk上建立节点,路径/hbase/splitWAL/对应上面改写后的log路径的encode . 然后HMaster等待log被其他region server上的SplitLogWorker split完成,然后将一开始建立的一系列节点/hbase/recovering-regions/regionEncodeName/serverName删掉,然后将-splitting目录删除.
regionserver上的SplitLogWorker会不断的去监控zk上的hbase.rootdir/WALs/serverName-splitting节点,并且试图own这个节点.成功后,则给SplitLogWorker内部线程池提交一个HLogSplitterHandler任务,任务逻辑在对象splitTaskExecutor中,任务内部主要调用HLogSplitter.splitLogFile(),从而进到HLogSplitter的boolean splitLogFile(FileStatus logfile, CancelableProgressable reporter) throws IOException. 该函数内部会读-splitting目录内部的hlog文件,然后将每条log entry加入到一个sink中,sink是一个抽象类,根据是否配置使用distributed log replay,使用不同的子类,对于distributed log replay来说,使用LogReplayOutputSink,否则使用LogRecoveredEditsOutputSink . 回到函数boolean splitLogFile(FileStatus logfile, CancelableProgressable reporter) throws IOException,函数的逻辑主要是从hlog中解析出一条条的log entry,如果log entry的sequence id比zk上相应的/hbase/recovering-regions/regionEncodeName 节点记录的sequence id小,那么说明这条log entry对应的内容已经持久化在HFile中,不需要进行回放,否则将这条日志append到成员EntryBuffers对象中,EntryBuffers内部会对log entry按照region进行分组,同一个Region的log entry记录在对象RegionEntryBuffer中 . 同时,会有一些写线程,不断的从EntryBuffers中取出RegionEntryBuffer对象,将其append到sink中,在这里,是LogReplayOutputSink . LogReplayOutputSink中积攒到一批日志,会调用WALEditsReplaySink的replayEntries()方法,通过ReplayServerCallable这个rpc发给这个region被assign后的新的region server让其回放,由于这里使用多个写线程给其他的region server发送日志,所以叫作distributed log replay . 
非distributed log replay的模式下,LogRecoveredEditsOutputSink的工作是直接按照region,把相对应的log写到hdfs的 hbase.rootdir/data/namespace(比如test)/table_name/region_encoded_name/recovered.edits下 . 后续region被其他region server open时,会来这看是不是有需要回放的hlog.
需要注意的是,在distributed log replay模式下,region是被open后,然后才replay ,可以看到open成功后,这个region可以提供写,但是不能提供读,因为数据不全 . 
以上是region server宕机后,触发的HMaster和其他region server的逻辑 . 
下面看看region server 收到HMaster的open region指令的逻辑 . 
Open Region
如果是distributed log replay,那么会去zk上找 /hbase/recovering-regions/regionEncodeName/serverName,将region记录在map recoveringRegions中 . 然后如果不是meta region,则提交一个OpenRegionHandler任务到内部线程池中,任何的逻辑主要是open region,代码在openHRegion(final CancelableProgressable reporter),最后主要代码在initializeRegionInternals()内部调用initializeRegionStores()方法中 . 这个方法主要就是从hdfs中load region对应的HFile文件,并且如果region目录下的recovered.edits有hlog文件需要回放,则进行回放(方法replayRecoveredEditsIfAny) . 
加载HFile过程中很重要的一点是,需要将这个HRegion下的所有的HStore(一个column family对应一个HStore,一个HStore下面有多个HFile和一个memstore)中最大的memstoreTS拿出来,加1后去初始化HRegion内部的mvcc对象,这个对象用于负责实现MVCC . 这个时间戳相当于事物ID,用来判断数据是否对某事务可见 . 


如果用HBase 直接将时间戳作为行健,在写入单个 region 时候会发生热点问题,为什么?
HBase的rowkey在底层是HFile存储数据的,以键值对存放到SortedMap中 . 并且region中的rowkey是有序存储,若时间比较集中 . 就会存储到一个region中,这样一个region的数据太多,加载数据就会很慢 . 直到region分裂可以解决 . 

怎样将mysql的数据导入到hbase中?
A 一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入hbase时,会按照region分区情况,在集群内做数据的负载均衡 . 
B hbase 里面有这样一个hfileoutputformat类,他的实现可以将数据转换成hfile格式,通过new一个这个类,进行相关配置,这样会在Hdfs下面产生一个文件,这个时候利用hbase提供的jruby的loadtable.rb脚本就可以进行批量导入 . 

hbase的快速查找建立在哪些因素的基础上?
有且仅有一个:rowkey(行键),rowkey是hbase的key-value存储中的key,通常使用用户要查询的字段作为rowkey,查询结果作为value . 可以通过设计满足几种不同的查询需求 . 

(1)数字rowkey的从大到小排序:原生hbase只支持从小到大的排序,这样就对于排行榜一类的查询需求很尴尬 . 那么采用rowkey = Integer.MAX_VALUE-rowkey的方式将rowkey进行转换,最大的变最小,最小的变最大 . 在应用层再转回来即可完成排序需求 . 
(2)rowkey的散列原则:如果rowkey是类似时间戳的方式递增的生成,建议不要使用正序直接写入rowkey,而是采用reverse的方式反转rowkey,使得rowkey大致均衡分布,这样设计有个好处是能将regionserver的负载均衡,否则容易产生所有新数据都在一个regionserver上堆积的现象,这一点还可以结合table的预切分一起设计 . 

简述 HBase 的瓶颈和oracle的瓶颈有什么不同?
HBase的瓶颈就是硬盘传输速度,Hbase 的操作,它可以往数据里面 insert,也可以update一些数据,但update 的实际上也是insert,只是插入一个新的时间戳的一行,delete数据,也是insert,只是insert一行带有delete标记的一行 . hbase的所有操作都是追加插入操作 . hbase是一种日志集数据库 . 它的存储方式,像是日志文件一样 . 它是批量大量的往硬盘中写,通常都是以文件形式的读写 . 这个读写速度,就取决于硬盘与机器之间的传输有多快 . 
而oracle的瓶颈是硬盘寻道时间 . 它经常的操作是随机读写 . 要update一个数据,先要在硬盘中找到这个block,然后把它读入内存,在内存中的缓存中修改,过段时间再回写回去 . 由于你寻找的block不同,这就存在一个随机的读 . 硬盘的寻道时间主要由转速来决定 . 而寻道时间,技术基本没有改变,这就形成了寻道时间瓶颈 . 

Hive 的 join 有几种方式,怎么实现 join 的?
3种join方式:
1)在reduce端进行join,最常用的join方式 .  
 Map端的主要工作:为来自不同表(文件)的key/value打标签以区别不同来源的记录 . 然后
用连接字段作为key,其余部分和新加的标志作为value,最后进行输出 . 
 reduce端的主要工作:在reduce端以连接字段作为key的分组已经完成,我们只需要在每一
个分组当中将那些来源于不同文件的记录(在map阶段已经打标志)分开,最后进行笛卡尔 . 

2)在map端进行join,使用场景:一张表非常小 一张表很大 . 
在提交作业的时候先将小表文件放到该作业的DistributedCache中,然后从DistributeCache中取出该小表进行join key / value解释分割放到内存中(可以放大Hash Map等等容器中) . 然后扫描大表,看大表中的每条记录的join key /value值是否能够在内存中找到相同join key的记录,如果有则直接输出结果
3)SemiJoin . 
semijoin就是左边连接是reducejoin的一种变种,在map端过滤掉一些数据,在网络传输过程中,只传输参与连接的数据,减少了shuffle的网络传输量,其他和reduce的思想是一样的 .  
实现:将小表中参与join的key单独抽取出来通过DistributeCache分发到相关节点,在map阶段扫描连接表,将join key不在内存hashset的纪录过滤掉,让参与join的纪录通过shuffle传输到reduce端进行join,其他和reduce join一样 . 

hive 内部表和外部表的区别?
Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变 . 在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据 . 这样外部表相对来说更加安全些,数据组织也更加灵活,方便共享源数据 . 

hive 是如何实现分区的?
建表语句: 
create table tablename (id) partitioned by (dt string) 
增加分区: 
alter table tablenname  add partition (dt = '2016-03-06') 
删除分区: 
alter table tablename  drop partition (dt = '2016-03-06')


hive 支持 not in 吗?
不支持,可以用left join 实现此功能 . 

Hive 有哪些方式保存元数据,各有哪些优缺点?
1.存储于内存数据库derby,此方法只能开启一个hive客户端,不推荐使用 . 
2.存储于mysql数据库,可以多客户端连接,推荐使用 . 
分为本地mysql数据库,远程mysql数据库,但是本地的mysql数据用的比较多,因为本地读写速度都比较快 . 

hive 如何优化?
1.join优化,尽量将小表放在join的左边,如果一个表很小可以采用mapjoin;
2.排序优化,order by 一个reduce效率低,distribute by +sort by 也可以实现全局排序;
3.使用分区,查询时可减少数据的检索,从而节省时间 . 
hive最终都会转化为mapreduce的job来运行,要想hive调优,实际上就是mapreduce调优,可以有下面几个方面的调优:解决收据倾斜问题,减少job数量,设置合理的map和reduce个数,对小文件进行合并,单个task最优不如整体最优,按照一定规则分区 . 


hive 如何权限控制?
Hive的权限需要在hive-site.xml文件中设置才会起作用,配置默认的是false . 需要把hive.security.authorization.enabled设置为true(开启Hive的身份认证功能,默认是false),并对不同的用户设置不同的权限,例如select ,drop等的操作 . 

hive 能像关系数据库那样,建多个库吗?
可以建立多个库,多库多表都支持 . 

hive 中的压缩格式 RCFile  TextFile  SequenceFile 各有什么区别?
TextFile:默认格式,数据不做压缩,磁盘开销大,数据解析开销大 . 存储空间大,并且压缩的text 无法分割和合并,查询的效率最低,可以直接存储,加载数据的速度最高,可结合Gzip Bzip2 Snappy等使用(系统自动检查,执行查询时自动解压),hive不会对数据进行切分,从而无法对数据进行并行操作 . 
SequenceFile:Hadoop API提供的一种二进制文件支持,使用方便,可分割,可压缩,支持三种压缩,NONE,RECORD,BLOCK . 压缩的文件可以分割和合并,查询效率高,需要通过text文件转化来加载,RECORD压缩率低,一般建议使用BLOCK压缩 . 
RCFILE:是一种行列存储相结合的方式 . 首先,将数据按行分块,保证同一个record在同一个块上,避免读一个记录读取多个block . 其次,块数据列式存储,有利于数据压缩和快速的列存取 . 数据加载的时候性能消耗大,但具有较好的压缩比和查询响应 . 存储空间最小,查询的效率最高 ,需要通过text文件转化来加载,加载的速度最低 . 


Serde是 Serializer/Deserializer的简写 . hive使用Serde进行行对象的序列与反序列化 . 

Map的优化
   • 增加map的个数:
        set mapred.map.tasks=10;
mapred.job.map.capacity设置的是一台机器最大的map个数


    • 减少map的个数(合并小文件):
        set mapred.max.split.size=100000000;
        set mapred.min.split.size.per.node=100000000;
        set mapred.min.split.size.per.rack=100000000;
        set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    • Map端聚合(combiner):
        hive.map.aggr=true;


Reduce的优化
    • 设置reduce的个数:
        set mapred.reduce.tasks=10;
    • reduce任务处理的数据量
        set hive.exec.reducers.bytes.per.reducer=100000;
    • 避免使用可能启动mapreduce的查询语句
group by

order by(改用distribute by和sort by)

Join的优化
    • Join on的条件:
    SELECT a.val, b.val, c.val
    FROM a
    JOIN b ON (a.key = b.key1)
    JOIN c ON (a.key = c.key1)

    • Join的顺序:
    /+ STREAMTABLE(a) / :a被视为大表
    /+ MAPJOIN(b) /:b被视为小表 
    SELECT /+ STREAMTABLE(a) / a.val, b.val, c.val
    FROM a
    JOIN b ON (a.key = b.key1)
    JOIN c ON (c.key = b.key1);

数据倾斜的优化
    • 万能方法:
        hive.groupby.skewindata=true
    • 大小表关联:
        Small_table join big_table
    • 数据中有大量0或NULL:
        on case when (x.uid = '-' or x.uid = '0‘ or x.uid is null)
        then concat('dp_hive_search',rand()) else x.uid
        end = f.user_id;
    • 大大表关联:
        Select/+MAPJOIN(t12)/ 
        from dw_log t11
        join (
            select/+MAPJOIN(t)/ t1.
            from (
                select user_id from dw_log group by user_id
                ) t
                join dw_user t1
                on t.user_id=t1.user_id
        ) t12
        on t11.user_id=t12.user_id
    • count distinct时存在大量特殊值:
        select cast(count(distinct user_id)+1 as bigint) as user_cnt
        from tab_a
        where user_id is not null and user_id <> ''
    • 空间换时间:
        select day,
        count(case when type='session' then 1 else null end) as session_cnt,
        count(case when type='user' then 1 else null end) as user_cnt
        from (
            select day,session_id,type
                from (
                    select day,session_id,'session' as type
                    from log
                    union all
                    select day user_id,'user' as type
                    from log
                )
                group by day,session_id,type
            ) t1
        group by day

其他的优化
    • 分区裁剪(partition):
        Where中的分区条件,会提前生效,不必特意做子查询,直接Join和GroupBy
    • 笛卡尔积:
        Join的时候不加on条件或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积
• Union all:

    先做union all再做join或group by等操作可以有效减少MR过程,多个Select,也只需一个MR
    • Multi-insert & multi-group by:
    从一份基础表中按照不同的维度,一次组合出不同的数据
            FROM from_statement
            INSERT OVERWRITE TABLE table1 [PARTITION (partcol1=val1)] select_statement1 group by key1
            INSERT OVERWRITE TABLE table2 [PARTITION(partcol2=val2 )] select_statement2 group by key2
    • Automatic merge:
    当文件大小比阈值小时,hive会启动一个mr进行合并
            hive.merge.mapfiles = true 是否合并 Map 输出文件,默认为 True
            hive.merge.mapredfiles = false 是否合并 Reduce 输出文件,默认为 False
            hive.merge.size.per.task = 25610001000 合并文件的大小
    • Multi-Count Distinct:
    一份表中count多个参数(必须设置参数:set hive.groupby.skewindata=true;)
            select dt, count(distinct uniq_id), count(distinct ip)
            from ods_log where dt=20170301 group by dt
    • 并行实行:
        hive执行开启:set hive.exec.parallel=true

zookeeper的本质是什么?它解决了哪些问题?
ZooKeeper 本质上是一个为分布式应用提供一致性服务的软件 . 
它解决了下列问题:
1. 分布式系统的一致性问题
2. 分布式系统的容灾容错
3. 分布式系统的执行顺序问题
4. 分布式系统的事务性问题


spark的执行流程?
1.构建Spark Application的运行环境(启动SparkContext),SparkContext向资源管理器(可以是Standalone Mesos或YARN)注册并申请container资源;
2.资源管理器分配container资源并启动Executor,Executor的运行情况将随着心跳发送到资源管理器上;
3.SparkContext构建成DAG图,将DAG图分解成Stage,并把Taskset发送给Task Scheduler . Executor向SparkContext申请Task,Task Scheduler将Task发放给Executor运行 . 
4.Task在Executor上运行,运行完毕释放所有资源 . 

写一个简单的Wordcount并描述?
以Scala为例进行介绍,代码如下:
val result = sc.textFile(input_path).flatmap(line => line.strip().split(',')).map(word=>(word,1)).reduceByKey(_+_).map(x => x._1+'\t'+x._2).saveAsTextFile(output_path)
描述的时候大家可以结合自己的调优经验或者解决数据倾斜的经验进行补充,给面试官留下一个好印象 . 
strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列 . 
注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符 . 
str.strip([chars]);
chars -- 移除字符串头尾指定的字符序列 . 
返回移除字符串头尾指定的字符生成的新字符串 . 
和trim等同

kafka的partition设置多了或少了会有什么影响?
1 越多的分区可以提供更高的吞吐量,因为在kafka中,单个patition是kafka并行操作的最小单元,只允许单个partition的数据被一个consumer线程消费 . 
2 越多的分区需要打开更多地文件句柄,在kafka的数据日志文件目录中,每个日志数据段都会分配两个文件,一个索引文件和一个数据文件,分区越多,需要的句柄越多 . 
3 更多地分区会导致更高的不可用性,见kafka的容灾特点
4 越多的分区可能增加端对端的延迟,消息在所有in-sync副本列表同步复制完成之后才暴露 . 因此,in-sync副本复制所花时间将是kafka端对端延迟的最主要部分 . 
5 越多的partition意味着需要客户端需要更多的内存 . 在内部实现层面,producer按照每一个partition来缓存消息 . 如果partition的数量增加,众多的partition所消耗的内存汇集起来,有可能会超过设置的内容大小限制 . 

kafka的容灾是如何实现的?
Kafka通过多副本复制技术,实现kafka集群的高可用和稳定性 . 每个partition都会有多个数据副本,每个副本分别存在于不同的broker . 所有的数据副本中,有一个数据副本为Leader,其他的数据副本为follower . 在kafka集群内部,所有的数据副本皆采用自动化的方式进行管理,并且确保所有的数据副本的数据皆保持同步状态 . 不论是producer端还是consumer端发往partition的请求,皆通过leader数据副本所在的broker进行处理 . 
当broker发生故障时,对于leader数据副本在该broker的所有partition将会变得暂时不可用 . Kafka将会自动在其他数据副本中选择出一个leader(controller),用于接收客户端的请求 . 这个过程由kafka controller节点broker自动完成,主要是从Zookeeper读取和修改受影响partition的一些元数据信息 . 

shuffle manager的种类?
通过set spark.shuffle.manager = hash来设定 . 
Spark 1.5以后,有三个可选项:hash sort和tungsten-sort . Hash Shuffle Manager是Spark 1.2以前的默认选项,但是Spark 1.2以及之后的版本默认都是Sort Shuffle Manager了 . tungsten-sort与sort类似,但是使用了tungsten计划中的堆外内存管理机制,内存使用效率更高 . 
调优建议:由于SortShuffleManager默认会对数据进行排序,因此如果你的业务逻辑中需要该排序机制的话,则使用默认的SortShuffleManager就可以;而如果你的业务逻辑不需要对数据进行排序,设定为的HashShuffleManager来避免排序操作,同时提供较好的磁盘读写性能 . 

RDD中存的是什么?及Spark中Driver的作用是什么?
RDD中存的是数据块的集合,即partition的集合 . 
Driver的作用是:
1.创建spark的上下文
2.划分RDD并生成有向无环图(DAGScheduler)
3.与spark中的其他组进行协调,协调资源等等(SchedulerBackend)
4.生成并发送task到executor(taskScheduler)

列举出Hadoop中定义的最常用的InputFormats,那个是默认的?
TextInputFormat(默认)用于读取纯文本文件,key是每一行的位置偏移量,是LongWritable类型的,value是每一行的内容,为Text类型 
KeyValueTextInputFormat 同样用于读取文件,如果行被分隔符(缺省是tab)分割为两部分,第一部分为key,剩下的部分 为value;如果没 有分隔符,整行作为 key,value为空 
SequenceFileInputFormat 用于读取sequence file .  sequence file是Hadoop用于存储数据自定义格式的binary文件 . 它有 两个子类:SequenceFileAsBinaryInputFormat,将 key和value以BytesWritable的类型读出;SequenceFileAsTextInputFormat,将key和value以Text类型读出 . 

TextInputFormat和KeyValueInputFormat类不同之处在于哪里?
答:TextInputFormat读取文本文件中的所有行,提供了行的偏移作为Mapper的键,实际的行作为 mapper的值 .  KeyValueInputFormat读取文本文件,解析所有行到中,首个空格前的字符是mapper的key,行的其余部分则是mapper的值 . 

Hadoop中InputSplit是什么?
答:InputSplit是指分片,在MapReduce作业中,作为map task最小输入单位 . 分片是基于文件基础上出来的概念 . 在 MapTask 拿到这些分片后,会知道从哪开始读取数据 . 

Hadoop框架中文件拆分是如何被触发的?
答:通过运行输入格式类中的getInputSplit()方法 . 

一,数据输入格式(InputFormat)用于描述MapReduce的作业数据输入规范 . MapReduce框架依靠数据输入格式完成输入规范检查(比如输入文件的目录的检查),对数据文件进行输入分块(InputSplit),以及提供从输入分块中 将数据逐一读出,并转换为Map过程的输入键值对等功能 . 最常用的数据输入格式:TextInputFormat和KeyValueTextInputFormat
   1,TextInputFormat是系统默认的数据输入格式,可以将文本文件分块并逐行读入以便Map节点进行处理 . 读入一行时所产生的主键key就是当前在整个文本文件中的字节偏移位置,而value就是该行的内容 . 
   KeyValueTextInputFormat是可将一个按照<key,value>格式逐行存放的文本文件逐行读出,并自动解析生产相应的的key和value.
   对于一个数据输入格式(TextInputFormat和KeyValueTextInputFormat),都需要有一个对应的RecordReader方法 . RecordReader主要用于将一个文件中的数据记录分拆成具体的键值对,传送给Map过程作为键值对输入参数 . 每个数据输入格式都有输入格式有一个默认的RecordReader . 
   1,TextInputFormat的默认RecordReader是LineRecordReader . 
   2,KeyValueTextInputFormat的默认RecordReader是KeyValueLineRrcordReader
二,数据输出格式(OutputFormat)用于描述MapReduce作业的数据输出规范 . MapReduce框架依靠数据格式完成输出规范检查(如检查输出目录是否存在)以及提供作业数据输出等功能 . 
    最常用的数据输出格式是TextOutputFormat,也是系统默认的数据输出格式
    可以将计算结果以"key+\t+value"的形式输出到文本文件中 . 
    数据输出格式也提供一个对应的RecordWriter,以便系统明确输出结果写入到文件中的具体格式 . TextInputFormat的默认RecordWriter是lineRecordWriter也是将计算结果以"key+\t+value"的形式输出到文本文件中 . 

hadoop中的RecordReader的目的是什么?
(1)以怎样的方式从分片中读取一条记录,每读取一条记录都会调用RecordReader类;
(2)系统默认的RecordReader是LineRecordReader,如TextInputFormat;而SequenceFileInputFormat的RecordReader是SequenceFileRecordReader;
(3)LineRecordReader是用每行的偏移量作为map的key,每行的内容作为map的value;
(4)应用场景:自定义读取每一条记录的方式;自定义读入key的类型,如希望读取的key是文件的路径或名字而不是该行在文件中的偏移量 .  系统默认的LineRecordReader是按照每行的偏移量做为map输出时的key值,每行的内容作为map的value值,默认的分隔符是 回车和换行 .  现在要更改map对应的输入的值,key对应的文件的路径(或者是文件名),value对应的是文件的内容 (content) .  那么我们需要重写InputFormat和RecordReader,因为RecordReader是在InputFormat中调用的,当然重写RecordReader才是重点!

如果hadoop中没有定义定制分区,那么如何在输出到reduce前执行数据分区? 
Partitioner /override getPartition()
默认的分区器为各个键计算一个哈希值,并分配给基于这个结果的分区 . 

什么是Combiner?举个例子,什么时候使用combiner,什么时候不用?
答:当map生成的数据过大时,带宽就成了瓶颈,怎样精简压缩传给Reduce的数据,又不影响最终的结果呢 . 有一种方法就是使 用 Combiner,Combiner号称本地的Reduce,Reduce最终的输入,是Combiner的输出 Combiner的作用是把一个map产生的多个合并成一个新的,然后再将新的作为reduce的输入; 在map函数与reduce函数之间多了一个combine函数,目的是为了减少map输出的中间结果,这样减少了reduce复制map输出的数据,减少网络 传输负载; 并不是所有情况下都能使用Combiner,Combiner适用于对记录汇总的场景(如求和),但是,求平均数的场景就不能使用Combiner了 . 如果可以 使用Combiner,一般情况下,和我们的reduce函数是一致的 . 

什么是jobtracker? jobtracker有哪些特别的函数?
1, JobTracker是整个MapReduce计算框架中的主服务,相当于集群的"管理者",负责整个集群的作业控制和资源管理  
2, main()函数

什么是tasktracker?
TaskTracker是JobTracker和Task之间的桥梁:一方面,从JobTracker接收并执行各种命令:运行任务 提交任务 杀死任务 等;另一方面,将本地节点上各个任务的状态通过心跳周期性汇报给JobTracker . TaskTracker与JobTracker和Task之间采用了 RPC协议进行通信 . 

hadoop中job作业和task 关系?
 一 概述:
 (1)Hadoop MapReduce采用Master/Slave结构 . 
Master:是整个集群的唯一的全局管理者,功能包括:作业管理 状态监控和任务调度等,即MapReduce中的JobTracker . 
 Slave:负责任务的执行和任务状态的回报,即MapReduce中的TaskTracker . 
二 JobTracker剖析:
 (1)概述:JobTracker是一个后台服务进程,启动之后,会一直监听并接收来自各个TaskTracker发送的心跳信息,包括资源使用 情况和任务运行情况等信息 . 
 (2)JobTracker的主要功能:
 1.作业控制:在hadoop中每个应用程序被表示成一个作业,每个作业又被分成多个任务,JobTracker的作业控制模块则负责作业 的分解和状态监控 . 
 最重要的是状态监控:主要包括TaskTracker状态监控 作业状态监控和任务状态监控 . 主要作用:容错和为任务调度提供决 策依据 . 
2.资源管理 . 
 三 TaskTracker剖析:
 (1)TaskTracker概述:TaskTracker是JobTracker和Task之间的桥梁:一方面,从JobTracker接收并执行各种命令:运行任务 提交 任务 杀死任务等;另一方面,将本地节点上各个任务的状态通过心跳周期性汇报给JobTracker . TaskTracker与JobTracker和 Task之间采用了RPC协议进行通信 . 
(2)TaskTracker的功能:
1.汇报心跳:Tracker周期性将所有节点上各种信息通过心跳机制汇报给JobTracker . 这些信息包括两部分:
 机器级别信息:节点健康情况 资源使用情况等 . 
 任务级别信息:任务执行进度 任务运行状态等 . 
2.执行命令:JobTracker会给TaskTracker下达各种命令,主要包括:启动任务(LaunchTaskAction) 提交任务 (CommitTaskAction) 杀死任务(KillTaskAction) 杀死作业(KillJobAction)和重新初始化(TaskTrackerReinitAction) . 

假设hadoop一个job产生了100个task, 并且其中的一个task失败了,hadoop会如何处理?
答:hadoop本身的一个设计理念就是在普通的pc硬件上构建高可靠性的系统,任何failed task都不会引起整个job的失败,因为所有失败的任务 都会被重新执行(reschedule execution),只有当重新执行的次数超过4次,才会把这任务标记为失败,导致整个job的失败 . 

通过划分多个节点上任务,hadoop实现了并行处理,对少数慢节点可能会限制剩下其他程序的速率,并拖慢了整个程序 . hadoop提供了什么机制防止这种情况的发生? speculative execution . 举个简单的例子,如果某个job有2000个map task,已经完成了1999个,只剩下一个task由于硬件比较慢而成为拖尾任务,为了减少拖尾任务对整个job运行时间的影响,jobtracker会重新启动一个一模一样的duplicate task和原有的task并行的执行,这样有一 个task执行成功,整个map过程就会结束 . speculative execution(推测执行)只有个处理拖尾任务的优化策略,并不能提高系统的可靠性

hadoop推测执行是如何实现的?
答:Hadoop会为该task启动备份任务,让speculative task与原始task同时处理一份数据,哪个先运行完,则将谁的结果作为最终结果,并且在运行完成后Kill掉另外一个任务 . 

什么是hadoop streming?
答:Haoop支持用其他语言来编程,需要用到名为Streaming的通用API, Streaming主要用于编写简单,短小的MapReduce程序,可以通过脚本语言编程,开发更快捷,并充分利用非Java库

解释hadoop和hadoop生态系统两个概念
答:Hadoop是一个能够对大量数据进行分布式处理的软件框架,Hadoop的核心是HDFS和Mapreduce,hadoop2.0还包括YARN hadoop生态系统:包含 HDFS mapreduce hive hbase zookeeper sqoop flume pig mahout

说明hadoop2.0的基本构成
 HDFS
 MapReduce
 YARN

相比于HDFS1.0. 2.0最主要的改进?
引入一个新的资源管理系统YARN,HDFS单点故障得以解决

试使用步骤1,步骤2,步骤3.……说明YARN中运行应用程序的基本流程
步骤1 用户向YARN中提交应用程序,其中包括ApplicationMaster程序 启动ApplicationMaster的命令 用户程序等 . 
步骤2 ResourceManager为该应用程序分配第一个Container,并与对应的Node-Manager通信,要求它在这个Container中启动应用程序的 ApplicationMaster . 
步骤3 ApplicationMaster首先向ResourceManager注册,这样用户可以直接通过ResourceManage查看应用程序的运行状态,然后它将为各个任务申请资源,并监控它的运行状态,直到运行结束,即重复步骤4~7 . 
步骤4 ApplicationMaster采用轮询的方式通过RPC协议向ResourceManager申请和领取资源 . 
步骤5 一旦ApplicationMaster申请到资源后,便与对应的NodeManager通信,要求它启动任务Task . 
步骤6 NodeManager为任务Task设置好运行环境(包括环境变量 JAR包 二进制程序等)后,将任务启动命令写到一个脚本中,并通过 运行该脚本启动任务Task . 
步骤7 各个任务Task通过某个RPC协议向ApplicationMaster汇报自己的状态和进度,以让ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务 .  在应用程序运行过程中,用户可随时通过RPC向ApplicationMaster查询应用程序的当前运行状态 . 
步骤8 应用程序运行完成后,ApplicationMaster向ResourceManager注销并关闭自己 .  

什么是MRAppMaster?
我们知道,在MRv1中,JobTracker存在诸多问题,包括存在单点故障,扩展受限等,为了解决这些问题,Apache对MRv1进行了改进,提 出了YARN,YARN将JobTracker中的作业控制和资源管理两个功能分开,分别由两个不同的进程处理,进而解决了原有JobTracker存在的问题 . 经过架构调整之后,YARN已经完全不同于MRv1,它已经变成了一个资源管理平台,或者说应用程序管理框架 . 运行于YARN之上的计算框架不只限于MapReduce一种,也可以是其他流行计算框架,比如流式计算 迭代式计算等类型的计算框架 . 为了将一个计算框架运行于 YARN之上,用户需要开发一个组件—ApplicationMaster . 作为一个开始,YARN首先支持的计算框架是MapReduce,YARN为用户实现好了 MapReduce的ApplicationMaster,也就是\MRAppMaster . 

相比于JobTracker,MRAppMaster有什么不同?
既然MRAppMaster是由JobTracker衍化而来的,那么是否将JobTracker的代码稍加修改,就变成了MRAppMaster呢,答案是否定的 . 事实上,YARN仅重用了MRv1中的少许代码,基本可看做重写了MRAppMaster . 
YARN采用了新的软件设计思想,包括对象服务化 事件驱动的异步编程模型的 . 作为YARN的一部分,MRAppMaster的实现也采用了这 些设计思想 . 
下面简要介绍一下MRAppMaster的实现细节: 
在正式介绍MRAppMaster之前,我们先回顾一下MRv1的实现 . 我们都知道,MRv1主要由两种服务组成,即:JobTracker和TaskTracker, 而在YARN中,TaskTracker已经由NodeManager代替,因此,我们在此重点分析JobTracker . JobTracker包含资源管理和作业控制两个功能, 在YARN中,作业管理由ResourceManager实现,因此,只剩下作业控制这一个功能(由MRAppMaster实现) . MRv1中每个作业由一个 JobInProgress控制,每个任务由一个TaskInProgress控制,由于每个任务可能有多个运行实例,因此,TaskInProgress实际管理了多个运行实例Task Attempt,对于每个运行实例,可能运行了一个MapTask或者ReduceTask,另外,每个Map Task或者Reduce Task会通过RPC协议将状态 汇报给TaskTracker,再由TaskTracker进一步汇报给JobTracker 在MRAppMaster中,它只负责管理一个作业,包括该作业的资源申请 作业运行过程监控和作业容错等 . MRAppMaster使用服务模型和 事件驱动的异步编程模型对JobInProgress和TaskInProgress进行了重写(分别对应JobImpl和TaskImpl),并让Map Task和Reduce Task(Map Task和Reduce Task重用了MRv1中的代码)直接通过RPC将信息汇报给MRAppMaster . 此外,为了能够运行于YARN之上,MRAppMaster还要 与ResourceManager和NodeManager两个新的服务通信(用到两个新的RPC协议),以申请资源和启动任务,这些都使得MRAppMaster完全不同于JobTracker . 

NodeManager是运行在单个节点上的代理,它管理Hadoop集群中单个计算节点,功能包括与ResourceManager保持通信,管理Container的生命周期 监控每个Container的资源使用(内存 CPU等)情况 追踪节点健康状况 管理日志和不同应用程序用到的附属服务等 . 
NodeManager是YARN中单个节点的代理,它需要与应用程序的ApplicationMaster和集群管理者ResourceManager交互;它从ApplicationMaster上接收有关Container的命令并执行;向ResourceManager汇报各个Container运行状态和节点健康状况,并领取有关Container的命令 . 

job的运行流程(提交一个job的流程)
job的执行流程如下:dataInput- >split- >Mapper- >Combine- >(产出临时数据)-->Partition- >Sort- >Reducer- >最终数据


分桶将整个数据内容安装某列的hash值进行区分,如要安装name属性分为3个桶,就是对name的hash值对3取摸,按照取模结果对数据分桶 . 如取模结果为0的数据记录存放到一个文件,取模为1的数据存放到一个文件,取模为2的数据存放到一个文件 . 
分区在HDFS上的表现形式是一个目录, 分桶是一个单独的文件

一 索引
简介
Hive支持索引,但是Hive的索引与关系型数据库中的索引并不相同,比如,Hive不支持主键或者外键 . 
Hive索引可以建立在表中的某些列上,以提升一些操作的效率,例如减少MapReduce任务中需要读取的数据块的数量 . 
为什么要创建索引?
Hive的索引目的是提高Hive表指定列的查询速度 . 
没有索引时,类似'WHERE tab1.col1 = 10' 的查询,Hive会加载整张表或分区,然后处理所有的rows,
但是如果在字段col1上面存在索引时,那么只会加载和处理文件的一部分 . 
与其他传统数据库一样,增加索引在提升查询速度时,会消耗额外资源去创建索引表和需要更多的磁盘空间存储索引 . 
二 分区
简介
为了对表进行合理的管理以及提高查询效率,Hive可以将表组织成"分区" . 
分区是表的部分列的集合,可以为频繁使用的数据建立分区,这样查找分区中的数据时就不需要扫描全表,这对于提高查找效率很有帮助 . 
分区是一种根据"分区列"(partition column)的值对表进行粗略划分的机制 . Hive中每个分区对应着表很多的子目录,将所有的数据按照分区列放入到不同的子目录中去 . 
为什么要分区?
庞大的数据集可能需要耗费大量的时间去处理 . 在许多场景下,可以通过分区的方法减少每一次扫描总数据量,这种做法可以显著地改善性能 . 
数据会依照单个或多个列进行分区,通常按照时间 地域或者是商业维度进行分区 . 比如vido表,分区的依据可以是电影的种类和评级,另外,按照拍摄时间划分可能会得到更一致的结果 . 为了达到性能表现的一致性,对不同列的划分应该让数据尽可能均匀分布 . 最好的情况下,分区的划分条件总是能够对应where语句的部分查询条件 . 
Hive的分区使用HDFS的子目录功能实现 . 每一个子目录包含了分区对应的列名和每一列的值 . 但是由于HDFS并不支持大量的子目录,这也给分区的使用带来了限制 . 我们有必要对表中的分区数量进行预估,从而避免因为分区数量过大带来一系列问题 . 
Hive查询通常使用分区的列作为查询条件 . 这样的做法可以指定MapReduce任务在HDFS中指定的子目录下完成扫描的工作 . HDFS的文件目录结构可以像索引一样高效利用 . 
三 分桶(桶表)
简介
桶是通过对指定列进行哈希计算来实现的,通过哈希值将一个列名下的数据切分为一组桶,并使每个桶对应于该列名下的一个存储文件 . 
为什么要分桶?
在分区数量过于庞大以至于可能导致文件系统崩溃时,我们就需要使用分桶来解决问题了 . 
分区中的数据可以被进一步拆分成桶,不同于分区对列直接进行拆分,桶往往使用列的哈希值对数据打散,并分发到各个不同的桶中从而完成数据的分桶过程 . 
注意,hive使用对分桶所用的值进行hash,并用hash结果除以桶的个数做取余运算的方式来分桶,保证了每个桶中都有数据,但每个桶中的数据条数不一定相等 . 
哈希函数的选择依赖于桶操作所针对的列的数据类型 . 除了数据采样,桶操作也可以用来实现高效的Map端连接操作 . 
记住,在数据量足够大的情况下,分桶比分区,更高的查询效率 . 
四 总结
索引和分区最大的区别就是索引不分割数据库,分区分割数据库 . 
索引其实就是拿额外的存储空间换查询时间,但分区已经将整个大数据库按照分区列拆分成多个小数据库了 . 
分区和分桶最大的区别就是分桶随机分割数据库,分区是非随机分割数据库 . 
因为分桶是按照列的哈希函数进行分割的,相对比较平均;而分区是按照列的值来进行分割的,容易造成数据倾斜 . 
其次两者的另一个区别就是分桶是对应不同的文件(细粒度),分区是对应不同的文件夹(粗粒度) . 
注意:普通表(外部表 内部表) 分区表这三个都是对应HDFS上的目录,桶表对应是目录里的文件

你们的数据是用什么导入到数据库的?导入到什么数据库?
处理之前的导入:通过hadoop命令导入到hdfs文件系统 处理完成之后的导出:利用hive处理完成之后的数据,通过sqoop导出到mysql数据库中,以供报表使用

你们的业务数据量多大?有多少行数据?(面试三家,都问)
开发时使用的是部分数据,不是全量数据 . 有将近一亿行(8. 9千万吧, 全体不详, 一般开发不是特别关心这个问题)

Skewed Table可以提高有一个或多个列有倾斜值的表的性能,通过指定经常出现的值(严重倾斜),hive将会在元数据中记录这些倾斜的列名和值,在join时能够进行优化 . 若是指定了STORED AS DIRECTORIES,也就是使用列表桶(ListBucketing),hive会对倾斜的值建立子目录,查询会更加得到优化 . 

介绍一下HBase过滤器
 HBase为筛选数据提供了一组过滤器,通过这个过滤器可以在HBase中的数据的多个维度(行,列,数据版本)上进行对数据的筛选 作,也就是说过滤器最终能够筛选的数据能够细化到具体的一个存储单元格上(由行键,列明,时间戳定位) . 通常来说,通过行键,值 来筛选数据的应用场景较多 . 
RowFilter:筛选出匹配的所有的行,对于这个过滤器的应用场景,是非常直观的:使用BinaryComparator可以筛选出具有某个行键的行, 或者通过改变比较运算符(下面的例子中是CompareFilter.CompareOp.EQUAL)来筛选出符合某一条件的多条数据
PrefixFilter:筛选出具有特定前缀的行键的数据 . 这个过滤器所实现的功能其实也可以由RowFilter结合RegexComparator来实现
 3. KeyOnlyFilter:这个过滤器唯一的功能就是只返回每行的行键,值全部为空,这对于只关注于行键的应用场景来说非常合适,这样忽略 掉其值就可以减少传递到客户端的数据量,能起到一定的优化作用
4. RandomRowFilter:从名字上就可以看出其大概的用法,本过滤器的作用就是按照一定的几率(<=0会过滤掉所有的行,>=1会包含所有的 行)来返回随机的结果集,对于同样的数据集,多次使用同一个RandomRowFilter会返回不通的结果集,对于需要随机抽取一部分数据的应 用场景,可以使用此过滤器

hashmap的排序问题
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("d", 2);
map.put("c", 1);
map.put("b", 1);
map.put("a", 3);
List<Map.Entry<String, Integer>> infoIds =
    new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
//排序
Collections.sort(infoIds, new Comparator<Map.Entry<String, Integer>>() {   
    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {      
        //return (o2.getValue() - o1.getValue()); 
        return (o1.getKey()).toString().compareTo(o2.getKey());
    }
}); 

map也是集合接口

比如一列数,随机访问就是直接找到第i个数,顺序访问就是从1开始一直找,直到i

对磁盘来说


java中广义的异常是指Throwable接口,这个接口下有两个实现类:Error和Exception . Error表示严重的错误,一旦产生,则不做处理或者重写代码;如果是Exception,分为检查异常和运行异常 . 检查异常在出现时必须处理,或者抛出或者捕获;运行异常在出现时可以处理可以不处理 . 
多线程是指一个进程中多个任务的执行,会产生安全问题,为了解决这个问题,可以利用synchronized同步代码块或者lock()方法,但是也有可能造成死锁

Java String
String类是用final修饰的,因此不可被继承,其中重写了equals和hashCode方法,因此调用equals方法时比较的是两个字符串的值;String提供了比较多的构造方法,需要注意不同的构造方法所产生的对象的个数也不一样 . 用+连接运算拼接字符串的时候,调用的是StringBuilder中的append方法

string stringbuffer stringbuilder对比
这三个类都是final修饰的,都不可以被继承 . String这个类是线程不安全的,其中的+连接运算用的是StringBuilder中的append方法,每一次的+都会产生一个新的StringBuilder,concat方法是将字符串转化为字符数组之后再进行合并,然后转化为字符串 . StringBuilder和StringBuffer中的方法几乎一致,其不同之处在于StringBuilder是线程不安全的,StringBuffer是线程安全的 . 

hashmap(散列map)存储原理
HashMap 基于 hashing 原理,我们通过 put ()和 get ()方法储存和获取对象 . 当我们将键值对传递给 put ()方法时,它调用键对象的 hashCode ()方法来计算 hashcode,然后找到 bucket 位置来储存值对象 . 当获取对象时,通过键对象的 equals ()方法找到正确的键值对,然后返回值对象 . HashMap 使用 LinkedList 来解决碰撞问题,当发生碰撞了,对象将会储存在 LinkedList 的下一个节点中 .  HashMap 在每个 LinkedList 节点中储存键值对对象 . 
碰撞:HashMap运用put方法存储多个元素时,计算得出相同的hashCode,在put时出现冲突 . 
对象Hash的前提是实现equals()和hashCode()两个方法,那么HashCode()的作用就是保证对象返回唯一hash值,但当两个对象计算值一样时,这就发生了碰撞冲突 . 
处理:利用"拉链法"处理HashCode的碰撞问题;当我们将键值对传递给put方法时,他调用键对象的hashCode()方法来计算hashCode,然后找到bucket(哈希桶)位置来存储对象;当用get方法获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象 . HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中 . hashMap在每个链表节点存储键值对对象 . 当两个不同的键却有相同的hashCode时,他们会存储在同一个bucket位置的链表中 . 键对象的equals()来找到键值对 . 

string常量池
常量池是在方法区中的,用于存储计算机中的常量

Finally   final finalize
finally是异常中用于异常处理捕获后的一些后续操作的关键字,final可以用来修饰类 方法和属性 . 修饰类这个类不能被继承,修饰方法这个方法不能被重写可以被重载,final不能修饰抽象方法;修饰属性这个属性在被初始化之后就不能在被改变(对于基本类型的数据而言是值不可变,对于引用类型而言是地址不可改变)
finalize是gc中的方法,程序员可以调用这个方法通知进程进行内存的回收

wait notify
这两个方法都是设计在了Object类上,因此任何一个类产生的对象都可以调用这两个方法 . wait方法会使当前执行的对象转入冻结状态;notify方法使冻结的对象转入活跃状态

gc垃圾回收原理
  GC的工作原理:引用计数,标记复制
    "引用计数"是一种简单但速度很慢的垃圾回收技术.所有对象都有一个引用计数器,当有引用连接时计数器加1,当引用离开作用域时或者被置于NULL时,计数器-1,垃圾回收器会在所以包含对象引用的列表上进行遍历,当发现某个对象的引用计数为0时,就释放占用的空间.
"标记复制"的运行机制,垃圾回收器遍历包含所有引用的列表,当发现存活的对象引用时做上标记,这样当遍历完所有对象引用并做上标记的时候,执行垃圾回收,将没有标记的对象堆空间释放.

jvm内存管理机制
将内存空间划分为堆 JVM方法栈 方法区 本地方法栈 PC寄存器 . 
堆:堆用于存储对象实例及数组值,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中对象所占用的内存由GC进行回收,在32位操作系统上最大为2GB,在64位操作系统上则没有限制,其大小可通过-Xms和-Xmx来控制,-Xms为JVM启动时申请的最小Heap内存,默认为物理内存的1/64但小于1GB;-Xmx为JVM可申请的最大Heap内存,默认为物理内存的1/4但小于1GB,默认当空余堆内存小于40%时,JVM会增大Heap到-Xmx指定的大小,可通过-XX:MinHeapFreeRatio=来指定这个比例;当空余堆内存大于70%时,JVM会减小Heap的大小到-Xms指定的大小,可通过-XX:MaxHeapFreeRatio=来指定这个比例,对于运行系统而言,为避免在运行时频繁调整Heap 的大小,通常将-Xms和-Xmx的值设成一样 . 
JVM方法栈:为线程私有,其在内存分配上非常高效 . 当方法运行完毕时,其对应的栈帧所占用的内存也会自动释放 . 当JVM方法栈空间不足时,会抛出StackOverflowError的错误,在Sun JDK中可以通过-Xss来指定其大小 . 
方法区:要加载的类的信息(名称 修饰符等) 类中的静态变量 类中定义为final类型的常量 类中的Field信息 类中的方法信息 . 方法区域也是全局共享的,在一定条件下它也会被GC,当方法区域要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息 . 在Sun JDK中这块区域对应Permanet Generation,又称为持久代,默认最小值为16MB,最大值为64MB,可通过-XX:PermSize及-XX:MaxPermSize来指定最小值和最大值 . 
本地方法栈:用于支持native方法的执行,存储了每个native方法调用的状态 . 在Sun JDK的实现中,和JVM方法栈是同一个 . 
PC寄存器:占用的CPU寄存器或操作系统内存 . 

只读存储器(英语:Read-Only Memory,简称:ROM) . 只读内存,ROM所存数据,一般是装入整机前事先写好的,整机工作过程中只能读出,而不像随机存储器那样能快速地 方便地加以改写 . ROM所存数据稳定 ,断电后所存数据也不会改变;其结构较简单,读出较方便,因而常用于存储各种固定程序和数据 . 

基本数据类型,变量的引用

在Java的集合里比如ArrayList,LinkedList等都会说线程不安全,不同步的状况 .  
这里的不同步指的是 . 当使用线程和web的时候,对这个集合对象进行操作,那么不同的线程,和不同的web客户端所获取的这个集合对象是不同的 . 所以是说不同步,且不安全的 . 
比如说现在A线程 和B线程,要拿到这个集合对象,现在集合对象里有 [1,2,3] . 那么A在集合对象里添加了一些数字[1,2,3,4,5] . 那么这个时候B线程取拿这个集合对象的时候,还是[1,2,3]而不是A已经修改过的集合[1,2,3,4,5] . 所以说这里的集合不同步 . 

数组声明:数据类型[] 变量名     
int[] numbers =new int[5];    //定义了一个numbers变量,它是一个数组可以存放5个int类型的数据         
集合声明:List<数据类型>变量名;     
List<int> nums=new List<int>();
集合还可以任意添加 删除 修改元素

说出ArrayList,vector,LinkedList的存储性能和特性?
ArrayList是基于数组的,其内存空间是连续的,因此存储的时候更快一些,便于查询不便于增删;vector是基于数组的,其内存空间也是连续的,由于它线程的安全性,因此存储的时候相对较慢;LinkedList是基于指针(链表)的,因此内存空间是不连续的,其便于增删不便于查询

map的实现类有哪些
HashTable(线程安全),HashMap,LinkedHashMap,TreeMap

抽象类和接口的作用
实现接口或继承抽象类的子类必须实现接口的所有方法或抽象类的所有抽象方法
如果是抽象类要实现接口,可以实现部分或者一个都不实现就行,要是具体类就必须实现所有的方法
抽象类是将一些类中的共有属性和方法进行抽取,其中一些共有方法由于实现细节不一样,因此以抽象方法的形式存在 . 接口更类似于一个标准,定义实现这个接口的实例中的方法,但是具体的实现细节不用这个接口来管理 . 

java的反射
Class,Method,Constructor,Field这四个类是java实现反射的API . 在一些场景下无法直接通过new关键字来创建变量,例如配置文件中数据库的配置,那么这时候就需用反射来完成相关的操作

int和Integer所有区别?
int是基本数据类型,其变量是存储到栈内存中的,其上是没有任何方法的 . Integer是一个引用数据类型,是int的包装类,其中含有大量的方法 . 

&和&&的区别?
&可以是位运算也可以是逻辑运算,&&是逻辑运算,在运算的时候如果前面的表达式为false,则&&后的表达式就不在进行

程序中捕获异常的方法?
try-catch-finally代码块

用最有效的方法算出2乘以8等于几
2<<3


写一个方法,用一个for循环打印九九乘法表
for (int i = 1, j = 1; j <= 9; i++) {
   System.out.print(i + "" + j + "=" + i  j + " ");
   if (i == j) {
    i = 1;
    j++;
    System.out.println();
   }
  }

如何避免java多线程的死锁
减少共享资源,避免锁嵌套

两个对象值相同(x.equals(y)==true),但却可有不同的hashcode,这句话对不对?
对,如果重写equals和hashcode方法可能会出现这种情况

重载和重写的主要区别是什么?重载的方法能否根据返回类型进行区别?
重载是发生在了一个类中,只看同名方法中的参数列表不同,对修饰符和返回值类型没有限制;重写是放生在了父子类中,函数同名,参数列表一致,权限修饰符要大于等于父类(子类重写方法的访问权限不能低于父类中权限),返回值类型要小于等于父类(jdk1.5改变后的类型必须是修改前类型的子类型);重载是编译时多态,重写是运行时多态 . 


抽象类和接口有什么区别?
抽象类中允许存在实体方法,允许存在静态方法,有构造函数,有变量;接口中没有实体函数,没有静态函数,变量默认是静态常量

tcp和udp的区别
UDP:没有建立连接;不可靠的传输;底层是用流进行传输;传输速度快;封包传输
TCP:需要建立连接;底层用的也是流进行数据的传输;经过三次握手,是一个可靠的连接;不限制数据的大小;安全性比较高,但是传输速度比较

事务种类


大量数据处理方法
总体思路:分而治之 最终汇总
方式一:单机模式下,内存有限,可以将要处理的数据进行分块,依次加载入内存,进行处理,最终进行汇总,灵活利用分桶 分区的思想,对数据进行处理
方式二:利用多机处理,将数据分块后,并行在多台机器中进行处理,最终汇总,即分布式处理 . 目前有成熟的分布式大数据处理框架,如hadoop storm spark等 . 

sql关键字 例如trancate和delete的比较
Delete 删除数据,可以删除表中的部分行,也可以删除整表数据,删除时是真的去移除表中记录,所以删除整表数据时,如果数据量较大,效率会比较低 . 
Trancate删除数据,只能删除整表数据,原理是直接摧毁表,再重新创建表,效率会很高,但是只能删除整表数据,无法进行部分数据的删除 . 

where和having的区别
Where用来在分组之前过滤,having用来在分组之后过滤 . 
Where语句中不能出现聚合函数,having可以

乐观锁和悲观锁的区别
都是用来解决更新丢失问题的方案 . 
悲观锁通过在查询阶段加排他锁在在查询阶段就避免了更新丢失的可能
乐观锁通过版本字段进行检测,在修改阶段检查更新丢失,如果发现产生了更新丢失再进行补救 . 
根据其特点,如果查询多而修改少,则用乐观锁,如果查询少而更新多,则用悲观锁 . 

select有事务吗
看隔离级别,默认情况下非Serializable隔离级别下查询不加锁,Serializable隔离级别下查询加共享锁 . 也可以根据需要在查询阶段手动加锁 . 
共享锁[S锁]
又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁 . 这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改 . 
排他锁[X锁]
又称写锁 . 若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁 . 这保证了其他事务在T释放A上的锁之前不能再读取和修改A . 
共享锁就是允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有 . 
排它锁,也称作独占锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁 . 

如何防止sql注入
使用preparedstatment防止sql注入

写出学生表的建表语句
Create table student (sid int primary key auto_increment,sname varchar(20),sage int,ssex varchar(1));

hadoop和spark的应用场景?
Spark是一个基于内存计算的开源集群计算系统,目的是更快速的进行数据分析 . Spark由加州伯克利大学AMP实验室Matei为主的小团队使用Scala开发开发,类似于Hadoop MapReduce的通用并行计算框架,Spark基于Map Reduce算法实现的分布式计算,拥有Hadoop MapReduce所具有的优点,但不同于MapReduce的是Job中间输出和结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的Map Reduce的算法 . 
Spark的适用场景:
1)多次操作特定数据集的应用场合
Spark是基于内存的迭代计算框架,适用于需要多次操作特定数据集的应用场合 . 需要反复操作的次数越多,所需读取的数据量越大,受益越大,数据量小但是计算密集度较大的场合,受益就相对较小 . 
2)粗粒度更新状态的应用
由于RDD的特性,Spark不适用那种异步细粒度更新状态的应用,例如Web服务的存储或者是增量的Web爬虫和索引 . 就是对于那种增量修改的应用模型不适合 . 
总的来说Spark的适用面比较广泛且比较通用 . 
Hadoop是实现了MapReduce的思想,将数据切片计算来处理大量的离线数据数据 . Hadoop处理的数据必须是已经存放在HDFS上或者类似HBase的数据库中,所以Hadoop实现的时候是通过移动计算到这些存放数据的机器上来提高效率 . 
Hadoop的适用场景:
1)海量数据的离线分析处理
2)大规模Web信息搜索
3)数据密集型并行计算

数据处理都要经过哪些步骤?
数据采集 提取清洗 整合汇聚 存储 分析处理 可视化展示

hdfs工作原理.
简单来说,将大文件进行切块上传到hdfs集群中进行分布式存储,利用集群的力量提高海量数据处理时的吞吐能力 . 另外块在保存时会自动进行备份操作,默认3份,从而保证了集群中数据的可靠性 . 

zookeeper相关知识介绍
zookeeper是一个开放源码的分布式协调服务,是一种典型的分布式数据一致性解决方案 . 由雅虎创建,贡献给了apache . 
利用zookeeper可以实现数据发布订阅 负载均衡 命名服务 分布式协调/通知 集群管理 分布式锁 分布式队列等功能 . 
具有 顺序一致性 原子性 单一视图 可靠性 实时性的特点
采用选举 投票 过半同意 的机制保证了分布式数据协调时的数据一致性 . 


flume的扇入扇出
flume是分布式的,可靠的,用于从不同的来源有效收集 聚集 和 移动 大量的日志数据用以集中式的数据存储的系统 . 
可以通过自由配置agent来实现日志的收集汇聚传输存储 . 可以实现收集日志时多级传输 扇入 扇出等操作,并支持负载均衡失败恢复,实现日志的可靠传输 . 
扇入,即多个agent向一个agent发送日志,通常用来实现日志信息的汇聚 . 
扇出,即一个agent向多个agent发送日志,可以采用复制或路由方式工作,通常用来实现日志的分发 . 

ive的建表  hive如何存储数据
create table book (id bigint, name string) partitioned by (category string) row format delimited fields terminated by '\t';
hive中的数据以文件的形式保存在hdfs文件系统中 . Hdfs文件中的一行对应hive表中的一行数据,字段间通过指定分隔符进行分隔 . 

hive有几个模块
HiveQL:这是Hive的数据查询语言,与SQL非常类似 . Hive提供了这个数据查询语言与用户的接口,包括一个
shell的接口,可以进行用户的交互,以及网络接口与JDBC接口 . 
JDBC接口可以用于编程,与传统的数据库编程类似,使得程序可以直接使用Hive功能而无需更改
Driver:执行的驱动,用以将各个组成部分形成一个有机的执行系统,包括会话的处理,查询获取以及执行驱动
Compiler:Hive需要一个编译器,将HiveQL语言编译成中间表示,包括对于HiveQL语言的分析,执行计划的生成以及优化等工作
Execution Engine:执行引擎,在Driver的驱动下,具体完成执行操作,包括MapReduce执行,或者HDFS操作,或者元数据操作
Metastore:用以存储元数据:存储操作的数据对象的格式信息,在HDFS中的存储位置的信息以及其他的用于数据转换的信息SerDe等

Java nio  nio组成部分(channel buffer selector)
Java的非阻塞式io
Jdk5开始提供 . 
解决了传统java bio阻塞式工作,服务器无法在高并发场景下实现少量线程处理多客户端请求的问题 . 
由channel buffer selector组成
Channel称为通道,类比bio中的流,实现数据传输,但不同的是,为双向传输 . 
Buffer为缓冲区,channle面向缓冲区进行数据处理
Selector为选择器,多个通道可以注册到一个选择器中,通过少量线程从selector中选择就绪通道进行处理,从而实现少量线程处理多个客户端通道的效果 . 

kafka
1 实时数据流管道
2 实时数据流的转换
说到底主要还是来做数据实时采集的 . 
Kafka 可以跑在集群上的一个节点或多个节点上 .  Kafka的数据是存在topic上面的  .  每条记录包含 key, value, timestamp.

在卡夫卡中,客户端和服务器之间的通信是用一个简单的 高性能的 语言无关的TCP协议完成的 . 该协议是版本控制的,并且与旧版本保持向后兼容 . 我们为卡夫卡提供了一个Java客户端,但是客户端可以使用多种语言 . 

每个分区是有序的,并且被不断追加的 . 每条数据都有一个顺序的id,该id被称作offset,也就是偏移量 . 
kafka 集群保存所有数据,除非该数据超过了配置信息中的保存期限 . 比如,如果在配置文件中配置的过期时间是2天,那么数据在进入kafka之后的两天内是可以被消费的,超过两天后这些数据将会被删除已释放空间 . 但是长期保留数据对于kafka来说影响不大 . 

事实上,元数据这边主要保存的就是offset(consumer的offset应该如何保存呢,consumer把offset的保存放在了一个topic里面,为保证了消息不变性,为并发消费提供了线程安全的保证 . 每个consumer都保留自己的offset,互相之间不干扰,不存在线程安全问题) . Offset是由每个消费者自己控制的,所以消费者可以按照自己的喜好来消费这些数据 . 就比如说:消费者可以每次启动从头开始消费,可以设定某个offset重复消费某些数据,也可以跳过一下数据从最新的offset开始消费,比如:"now" . 
这种特性的组合带来的好处是什么?那就是独立性,消费者的独立性 . 比如,消费者A随意的消费topic内的数据而不用担心它是否会影响到消费者B 的消费 . 
有分区的原因:
第一 一个patition会有多个分区,每个分区都是互相独立的,他们的offset不会有任何羁绊,所以可以很大程度上的多并发 . 
第二 作为存储数据的最小单位,并且是并行的它们会做的比这个更多 . 

Distribution分布式
在kafka中,分区是有副本概念的 . 副本按照一定的算法分布在不同的节点上 . 
每个分区都会产生一个leader,该leader会有一些follower,follower除了提供容错之外,还会充当之后的leader选举 . 也就是说当leader挂了之后,这些follower会通过选举机制完成新leader的选举 . 所以说,kafka是高容错 负载均衡的 . 

Consumers 会有消费者组的概念,消费者组不需要提前定义 . 而是在使用消费者时随意制定消费者组,比如消费者A 可以指定为消费者组x . x 之前是不存在的 . 
同消费组中的消费者共享topic中的数据,不同消费者组之间是独立的 . 

kafka节点,一个topic有四个分区p0-p3 . 我们发现,该topic有两个消费者组,CGA和CGB . CGA 中有两个消费者,根据动态算法自动负载均衡的消费分区数据 . 我们注意到,同一个分区在同一时间只能被一个消费者消费;而两个消费组之间并没有产生羁绊 . 
对于CGA,如果C1当掉,那么p0,p3将会被C2接管 . 如果C 1又重新启动,那么P0和p3会重新分配到C1上 . 

kafka元数据放在zookeeper中保存


kafka和flume都是日志系统 . kafka是分布式消息中间件,自带存储,提供push和pull存取数据功能 . flume分为agent(数据采集器),collector(数据简单处理和写入,Collector的作用是将多个Agent的数据汇总后,加载到Storage中),storage(存储器)三部分,每一部分都是可以定制的 . 比如agent采用RPC(Thrift-RPC) text(文件)等,storage指定用hdfs做 . 
kafka做日志缓存应该是更为合适的,但是 flume的数据采集部分做的很好,可以定制很多数据源,减少开发量 . 所以比较流行flume+kafka模式,如果为了利用flume写hdfs的能力,也可以采用kafka+flume的方式 . 

Flume特点
1)  可靠性
当节点出现故障时,日志能够被传送到其他节点上而不会丢失 . Flume提供了三种级别的可靠性保障,从强到弱依次分别为:end-to-end(收到数据 agent首先将event写到磁盘上,当数据传送成功后,再删除;如果数据发送失败,可以重新发送),Store on failure(这也是scribe采用的策略,当数据接收方crash时,将数据写到本地,待恢复后,继续发送),Best effort(数据发送到接收方后,不会进行确认)
2)   可扩展性
Flume og采用了三层架构,分别问agent,collector和storage,每一层均可以水平扩展 . 其中,所有agent和collector由 master统一管理,这使得系统容易监控和维护,且master允许有多个(使用ZooKeeper进行管理和负载均衡),这就避免了单点故障问题 . 
3)   可管理性
所有agent和colletor由master统一管理,这使得系统便于维护 . 用户可以在master上查看各个数据源或者数据流执行情况,且可以对各个数据源配置和动态加载 . 
4)   功能可扩展性
用户可以根据需要添加自己的agent,colletor或者storage . 

Flume架构
    Flume采用了分层架构,由三层组成:agent,collector和storage . 其中,agent和collector均由两部分组成:source和sink,source是数据来源,sink是数据去向 . 
    Flume的核心是Agent进程,是一个运行在服务器节点的Java进程 . 
agent:将数据源的数据发送到collector
collector:将多个agent的数据汇总后,加载到storage . 它的source和sink与agent类似
storage:存储系统,可以是一个普通file,也可以是HDFS,Hive,HBase等 . 
source(数据源):用于收集各种数据
channel:临时存放数据,可以存放在memory jdbc file等
sink:把数据发送到目的地,如HDFS HBase等
Flume传输数据的基本单位是event,事务保证是在event级别进行的,event将传输的数据进行封装
只有在sink将channel中的数据成功发送出去之后,channel才会将临时数据进行删除,这种机制保证了数据传输的可靠性与安全性 . 

Flume的广义用法
Flume支持多级Flume的Agent,即sink可以将数据写到下一个Agent的source中,
且Flume支持扇入(source可以接受多个输入) 扇出(sink可以将数据输出多个目的地)

一个复杂的例子如下:有6个agent,3个collector,所有collector均将数据导入HDFS中 . agent A,B将数据发送给collector A,agent C,D将数据发送给collectorB,agent C,D将数据发送给collectorB . 同时,为每个agent添加end-to-end可靠性保障,如果collector A出现故障时,agent A和agent B会将数据分别发给collector B和collector C . 


Vagrant 是一个用来构建和管理虚拟机环境的工具 . Vagrant 有着易于使用的工作流,并且专注于自动化,降低了开发者搭建环境的时间,提高了生产力 . 解决了"在我的机器上可以工作"的问题 . 
Vagrant 是一款用来构建虚拟开发环境的工具,非常适合 php/python/ruby/java 这类语言开发 web 应用,"代码在我机子上运行没有问题"这种说辞将成为历史 . 
我们可以通过 Vagrant 封装一个 Linux 的开发环境,分发给团队成员 . 成员可以在自己喜欢的桌面系统(Mac/Windows/Linux)上开发程序,代码却能统一在封装好的环境里运行,非常霸气 . 


Flume会实时监控写入日志的磁盘,只要有新的日志写入,Flume就会将日志以消息的形式传递给Kafka,然后Spark Streaming实时消费消息传入Hive
那么Flume是什么呢,它为什么可以监控一个磁盘文件呢?简而言之,Flume是用来收集 汇聚并且移动大量日志文件的开源框架,所以很适合这种实时收集日志并且传递日志的场景
Kafka是一个消息系统,Flume收集的日志可以移动到Kafka消息队列中,然后就可以被多处消费了,而且可以保证不丢失数据
通过这套架构,收集到的日志可以及时被Flume发现传到Kafka,通过Kafka我们可以把日志用到各个地方,同一份日志可以存入Hdfs中,也可以离线进行分析,还可以实时计算,而且可以保证安全性,基本可以达到实时的要求
可能使用Flume+kafka架构相对只使用Kafka会多占用1-2台机器做Flume日志采集,但是为了方便以后日志数据处理方式的扩展,可以采用Flume+kafka架构 . 
①使用Flume作为Kafka的Producer;
②使用Kafka作为Flume的Sink;
一般使用Flume+Kafka架构都是希望完成实时流式的日志处理,后面再连接上Flink/Storm/Spark Streaming等流式实时处理技术,从而完成日志实时解析的目标 . 
如果Flume直接对接实时计算框架,当数据采集速度大于数据处理速度,很容易发生数据堆积或者数据丢失,而kafka可以当做一个消息缓存队列,从广义上理解,把它当做一个数据库,可以存放一段时间的数据 . 
Kafka属于中间件,一个明显的优势就是使各层解耦,使得出错时不会干扰其他组件 . 
因此数据从数据源到flume再到Kafka时,数据一方面可以同步到HDFS做离线计算,另一方面可以做实时计算,可实现数据多分发 . 

Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs而不是HBase客户端APIs来创建表,插入数据和对HBase数据进行查询 . 

队列和堆栈区别:--- 先进先出和先进后出
管道和消息对列都是"队列",但是,消息队列并不一定先进先出,一般消息队列的操作可以有选择地读出想要的消息,而管道则不同,一端送进的,另一端原样送出 . 
命名管道读的同时,写会阻塞命名管道读的同时,写会阻塞

我错了,消息队列只要有权限就可以读写,没什么方向性而言
消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:解耦 异步 削峰


UDF(UDF:用户定义(普通)函数,只对单行数值产生作用) 步骤:
1.必须继承org.apache.hadoop.hive.ql.exec.UDF
2.必须实现evaluate函数,evaluate函数支持重载
<span style="font-size:x-small;">package com.alibaba.hive.udf;  
import org.apache.hadoop.hive.ql.exec.UDF  
public class helloword extends UDF{  
     public String evaluate(){  
          return "hello world!";  
     }  
     public String evaluate(String str){  
          return "hello world:" + str;  
     }  
}</span>  
 
UDAF(UDAF:User- Defined Aggregation Funcation;用户定义聚合函数,可对多行数据产生作用;等同与SQL中常用的SUM(),AVG(),也是聚合函数) 步骤:
1.必须继承
     org.apache.hadoop.hive.ql.exec.UDAF(函数类继承)
     org.apache.hadoop.hive.ql.exec.UDAFEvaluator(内部类Evaluator实现UDAFEvaluator接口)
2.Evaluator需要实现 init iterate terminatePartial merge terminate这几个函数
     init():类似于构造函数,用于UDAF的初始化
     iterate():接收传入的参数,并进行内部的轮转 . 其返回类型为boolean
     terminatePartial():无参数,其为iterate函数轮转结束后,返回乱转数据,iterate和terminatePartial类似于hadoop的Combiner(iterate--mapper;terminatePartial--reducer)
     merge():接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean
     terminate():返回最终的聚集函数结果
<span style="font-size:x-small;">package com.alibaba.hive;  
import org.apache.hadoop.hive.ql.exec.UDAF;  
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;  
public class myAVG extends UDAF{  
     public static class avgScore{  
          private long pSum;  
          private double pCount;  
     }    
     public static class AvgEvaluator extends UDAFEvaluator{  
          avgScore score;  
          public AvgEvaluator(){  
               score = new avgScore();  
               init();  
          } 
          / 
          init函数类似于构造函数,用于UDAF的初始化 
          /  
          public void init(){  
               score.pSum = 0;  
               score.pCount = 0;  
          }  
          / 
          iterate接收传入的参数,并进行内部的轮转 . 其返回类型为boolean 
          类似Combiner中的mapper 
          /  
          public boolean iterate(Double in){  
               if(in != null){  
                    score.pSum += in;  
                    score.pCount ++;  
               }  
               return true;  
          }  
          / 
          terminatePartial无参数,其为iterate函数轮转结束后,返回轮转数据 
          类似Combiner中的reducer 
          /  
          public avgScore terminatePartial(){  
               return score.pCount == 0 ? null :score;  
          }  
          / 
          merge接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean 
          /  
          public boolean merge(avgScore in){  
               if(in != null){  
                    score.pSum += in.pSum;  
                    score.pCount += in.pCount;  
               }  
               return true;  
          }  
          / 
          terminate返回最终的聚集函数结果 
          /  
          public Double terminate(){  
               return score.pCount == 0 ? null :Double.valueof(score.pSum/score.pCount);  
          }  
     }  
}</span>  

UDTF步骤:
  1.必须继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
  2.实现initialize, process, close三个方法
  3.UDTF首先会
       a.调用initialize方法,此方法返回UDTF的返回行的信息(返回个数,类型)
       b.初始化完成后,会调用process方法,对传入的参数进行处理,可以通过forword()方法把结果返回
       c.最后close()方法调用,对需要清理的方法进行清理
UDTF:User-Defined Table-Generating Functions,用户定义表生成函数,用来解决输入一行输出多行;
<span style="font-size:x-small;"><span style="font-size:xx-small;">public class GenericUDTFExplode extends GenericUDTF {  
  private ListObjectInspector listOI = null;  
  @Override  
  public void close() throws HiveException {  
  }  
  @Override  
  public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException {  
    if (args.length != 1) {  
      throw new UDFArgumentException("explode() takes only one argument");  
    }  
    if (args[0].getCategory() != ObjectInspector.Category.LIST) {  
      throw new UDFArgumentException("explode() takes an array as a parameter");  
    }  
    listOI = (ListObjectInspector) args[0];  
    ArrayList<String> fieldNames = new ArrayList<String>();  
    ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>();  
    fieldNames.add("col");  
    fieldOIs.add(listOI.getListElementObjectInspector());  
    return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames,  
        fieldOIs);  
  }  
  private final Object[] forwardObj = new Object[1];  
  @Override  
  public void process(Object[] o) throws HiveException {  
    List<?> list = listOI.getList(o[0]);  
    if(list == null) {  
      return;  
    }  
    for (Object r :list) {  
      forwardObj[0] = r;  
      forward(forwardObj);  
    }  
  }  
  @Override  
  public String toString() {  
    return "explode";  
  }  
}</span></span> 


用flume从kafka导入数据到hive或者hdfs

UpdateStateByKey windows等有状态的操作时,自动进行checkpoint,必须设置checkpoint目录,数据保留一份在容错的文件系统中,一旦内存中的数据丢失,可以从文件系统中读取数据,不需要重新计算 . 
SparkStreaming.checkpoint("hdfs://ip:port/checkpoint")
第一次在创建和启动StreamingContext的时候,那么将持续不断的产生实时计算的元数据并写入检查点,如果driver节点挂掉,那么可以让Spark集群自动重启集群(必须使用yarn cluster模式,spark-submit --deploy-mode cluster --supervise ....),然后继续运行计算程序,没有数据丢失 . 
实现RDD高可用性,启动WAL预写日志机制
sparkStreaming从原理上说,是通过receiver来进行数据接收的,接收到时的数据,会被划分成一个个的block,block会被组合成batch,针对一个batch,会创建一个Rdd,启动一个job来执行定义的算子操作 . receiver主要接收到数据,那么就会立即将数据写入一份到时容错文件系统(比如hdfs)上的checkpoint目录中的,一份磁盘文件中去,作为数据的冗余副本 . 
  SparkConf conf = new SparkConf()
    .setMaster("local[2]")
    .setAppName("AdClickRealTimeStatSpark")
    .set("spark.streaming.receiver.writeAheadLog.enable","true");

1 通过Spark core 实现:

//读取测试数据保存为rdd

val rddtext = sc.textFile("file:///C:/Users/chunyuhe/Desktop/test1.txt")

//将数据转化为Row形式(为下面Spark SQL 生成临时表用)

val rowrdd = rddtext.map(m => Row(m.split(" ")(0), m.split(" ")(1), m.split(" ")(2).toInt))
/
 spark core 实现分组取topN
/

val classrdd = rddtext.map(x => {

                       //取到各数据并赋值给变量

val classname = x.split(" ")(0)
val name = x.split(" ")(1)

val grade = x.split(" ")(2)

                       //生成一个便于计算的元组

(classname, (name, grade.toInt))

}).groupByKey

                //根据key聚合分组得到


classrdd.foreach(x => println(x))
classrdd.map(m => {

val classname = m._1

                       //如上图将各班级同学信息转化为Array数组并且安装成绩进行降序排列取前三

                        val top3 = m._2.toArray.sortWith(_._2 > _._2).take(3)

(classname, top3)
}).foreach(m => {
println(m._1 + "班级的前3名的成绩为")
m._2.foreach(x => {
println(x)
})

})

输出结果为:

2 通过Spark sql 实现:

//隐式转换

import spark.implicits._

import spark.sql

                //生成数据表表结构

val schema = StructType(mutable.ArraySeq(
StructField("classname", StringType, true),
StructField("name", StringType, true),

StructField("grade", IntegerType, true)))

               //将表结构和表数据组合生成表

val tablerow = spark.createDataFrame(rowrdd, schema)

               //将生成的df转换为一个表并且命名

tablerow.createTempView("testtable")
val tetrow = sql("select  from testtable")

//tetrow.show()

               //运用Spark sql 开窗函数进行计算

               PARTITION BY 为需要开窗字段

               ORDER BY 为需要排序字段

val resultrow = sql("""
      select a.classname,a.name,a.grade from (select classname,name,grade,row_number() OVER (PARTITION BY classname ORDER BY grade DESC) rank from testtable) as a where a.rank <= 3
      """)

resultrow.show()

输出结果:


在Spark中存在两种对RDD进行排序的函数,分别是 sortBy和sortByKey函数 . sortBy是对标准的RDD进行排序,它是从Spark 0.9.0之后才引入的(可以参见SPARK-1063) . 而sortByKey函数是对PairRDD进行排序,也就是有Key和Value的RDD . 

rdd存储的是元数据

例子:
老王要在淘宝中购买一双鞋子,推荐什么样的鞋子给老王才能让老王满意,就是大数据推荐所要研究的内容 . 
数学方式描述:
老王要买一双怎样的鞋 , 为未知条件 . 求最接近老王需求的鞋 . 
解题思路:
老王:Personal标签,需求分类,人员维度,需求权重 
系统:鞋商品类别,标签,价格维度,其他维度,碰撞维度
解题障碍:
老王不喜欢红色,是否会讨厌红色鞋 
老王年收入500万,是否有地域 圈子等因素做为老王购买鞋子的干扰条件

大数据工程师工作内容取决于你工作在数据流的哪一个环节 . 
  从数据上游到数据下游,大致可以分为:
  数据采集 -> 数据清洗 -> 数据存储 -> 数据分析统计 -> 数据可视化 等几个方面
  工作内容当然就是使用工具组件(Spark Flume Kafka等)或者代码(Java Scala等)来实现上面几个方面的功能 . 
  数据采集:
  业务系统的埋点代码时刻会产生一些分散的原始日志,可以用Flume监控接收这些分散的日志,实现分散日志的聚合,即采集 . 
  数据清洗:
  原始的日志,数据是千奇百怪的
  一些字段可能会有异常取值,即脏数据 . 为了保证数据下游的"数据分析统计"能拿到比较高质量的数据,需要对这些记录进行过滤或者字段数据回填 . 
  一些日志的字段信息可能是多余的,下游不需要使用到这些字段做分析,同时也为了节省存储开销,需要删除这些多余的字段信息 . 
  一些日志的字段信息可能包含用户敏感信息,需要做脱敏处理 . 如用户姓名只保留姓,名字用''字符替换 . 
  数据存储:
  清洗后的数据可以落地入到数据仓库(Hive),供下游做离线分析 . 如果下游的"数据分析统计"对实时性要求比较高,则可以把日志记录入到kafka . 
  数据分析统计:
  数据分析是数据流的下游,消费来自上游的数据 . 其实就是从日志记录里头统计出各种各样的报表数据,简单的报表统计可以用sql在kylin或者hive统计,复杂的报表就需要在代码层面用Spark Storm做统计分析 . BI的岗位是专门做这一块的 . 
  数据可视化:
  用数据表格 数据图等直观的形式展示上游"数据分析统计"的数据 . 一般公司的某些决策会参考这些图表里头的数据~
当然,大数据平台(如CDH FusionInsight等)搭建与维护,也是大数据工程师工作内容的一部分

Hbase表设计原则
宽表指的是行少列多,如果一行数据量过大,可能造成一个HFile放不下 . 但宽表有行级原子性的优势 . 高表指的是行多列少,Hbase只能按行分片,因此高表更有优势 . 具体还是要根据业务场景综合考虑 . 
2) 最好不要定义过多的ColumnFamily,一般来说, 一张表一个ColumnFamily就好 . 因为Flushing和压缩是基于Region的 . 当一个ColumnFamily所存储的数据达到Flushing阀值时,该表中的其他ColumnFamily可能没存储多少数据,也要跟着进行Flushing操作,这将会带来很多不必要的IO消耗 . ColumFamily越多,对性能的影响也就越大 . 此外,同一个表中不同的ColumnFamily存储的数据量差别也不要太大,不然有些数据会分散在太多的Region上,会影响检索效率 . 

hbase一个表(table)会分割为n个region(在建表时可以指定多少个以及每个表的key range,同时也会在运行时split)
region会均匀分布在集群的regionserver上 . 
一个region(HRegion)下会有一定数量的column family(一个cf称为一个Store,包含一个MemStore)
hbase是按列存储,所以column family是其hdfs对应的最细粒度的文件夹,文件夹的名字即是cf的名字
里面躺着一定数量的hfile(称为StoreFile) . 


Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组 . 
存储时,数据按照Row key的字典序(byte order)排序存储 . 设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起 . (位置相关性)
注意:
字典序对int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99 . 要保持整形的自然序,行键必须用0作左填充 . 
行的一次读写是原子操作 (不论一次读写多少列) . 这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为 . 
原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行 . 

列族是hbase最小的存储单位,换句话说hbase底层数据都是以列族来进行组织的 . 
HBase中,Region是集群节点上最小的数据服务单元,用户数据表由一个或多个Region组成 . 在Region中每个ColumnFamily的数据组成一个Store . 每个Store由一个Memstore和多个HFile组成

LSM-tree 是专门为 key-value 存储系统设计的,key-value 类型的存储系统最主要的就两个个功能,put(k,v):写入一个(k,v),get(k):给定一个 k 查找 v . 
LSM-tree 最大的特点就是写入速度快,主要利用了磁盘的顺序写,pk掉了需要随机写入的 B-tree . 
HBase是基于LSM-Tree模型的,所有的数据更新插入操作都首先写入Memstore中(同时会顺序写到日志HLog中),达到指定大小之后再将这些修改操作批量写入磁盘,生成一个新的HFile文件,这种设计可以极大地提升HBase的写入性能;另外,HBase为了方便按照RowKey进行检索,要求HFile中数据都按照RowKey进行排序,Memstore数据在flush为HFile之前会进行一次排序,将数据有序化;还有,根据局部性原理,新写入的数据会更大概率被读取,因此HBase在读取数据的时候首先检查请求的数据是否在Memstore,写缓存未命中的话再到读缓存中查找,读缓存还未命中才会到HFile文件中查找,最终返回merged的一个结果给用户 . 

每个进程都需要启一个jvm
包括Android上的jvm,也是一个进程开一个
执行main方法就启动了一个虚拟机实例
必然可以启动多个JVM,甚至是不同厂商 不同版本的JVM .  .  . 
你连QQ都可以启动两三个
每个java程序都运行于某个具体的java虚拟机实现的实例上,当启动一个java程序的时候,一个虚拟机实例也就诞生了.不同的程序,将得到不同的虚拟机实例


宽窄依赖和划分算子种类无关

reduceByKey用于对每个key对应的多个value进行merge操作,最重要的是它能够在本地先进行merge操作,并且merge操作可以通过函数自定义 . 
groupByKey也是对每个key进行操作,但只生成一个sequence . 如果需要对sequence进行aggregation操作(注意,groupByKey本身不能自定义操作函数),那么,选择reduceByKey/aggregateByKey更好 . 这是因为groupByKey不能自定义函数,我们需要先用groupByKey生成RDD,然后才能对此RDD通过map进行自定义函数操作 . 


python中 c:lambda a:b什么意思
一个简单的函数定义,c是函数名,a是自变量,b是函数体

1阶段
scala闭包

声明周期

一个不返回任何东西的函数可以返回一个类似在Java中的void类型,并表示该函数不返回任何内容 .  在Scala中不返回任何东西的函数称为过程 . 
闭包是一个函数 . 
Spark的官方文档再三强调那些将要作用到RDD上的操作,不管它们是一个函数还是一段代码片段,它们都是"闭包",Spark会把这个闭包分发到各个worker节点上去执行,这里涉及到了一个容易被忽视的问题:闭包的"序列化" . 
在将一个简单的函数或者一段代码片段(就是闭包)传递给类似RDD.map这样的操作前,Spark需要检索闭包内所有的涉及到的变量(包括传递依赖的变量),正确地把这些变量序列化之后才能传递到worker节点并反序列化去执行 . 如果在涉及到的所有的变量中有任何不支持序列化或没有指明如何序列化自己时,你就会遇到这样的错误:
org.apache.spark.SparkException:Task not serializable
闭包是一个函数,然后,也是最本质的地方:这个函数内部会引用(依赖)到一些变量,这些变量既不是全局的也不是局部的,而是在定义在上下文中的(这种变量被称为"自由变量",我们会在稍后的例子中看到这种变量),闭包的"神奇"之处是它可以"cache"或者说是持续的"trace"它所引用的这些变量 . (从语言实现层面上解释就是:这些变量以及它们引用的对象不会被GC释放) . 同样是这件事情,换另一种说法就是:闭包是一个函数,但同时这个函数"背后"还自带了一个"隐式"的上下文保存了函数内部引用到的一些(自由)变量 . 
什么叫闭包:跨作用域访问函数变量 . 又指的一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分 . 

上述代码的行为是未定义的,并且不同模式下运行情况不同 . 为了执行作业,Spark将RDD操作的处理分解为tasks,每个task由Executor执行 . 在执行之前,Spark会计算task的闭包 . 闭包是Executor在RDD上进行计算的时候必须可见的那些变量和方法(在这种情况下是foreach()) . 闭包会被序列化并发送给每个Executor
发送给每个Executor的闭包中的变量是副本,因此,当foreach函数内引用计数器时,它不再是driver节点上的计数器 . driver节点的内存中仍有一个计数器,但该变量是Executor不可见的!执行者只能看到序列化闭包的副本 . 因此,计数器的最终值仍然为零,因为计数器上的所有操作都引用了序列化闭包内的值 . 
在本地模式下,在某些情况下,该foreach函数实际上将在与driver相同的JVM内执行,并且会引用相同的原始计数器,并可能实际更新它 . 
为了确保在这些场景中明确定义的行为,应该使用一个Accumulator . Spark中的累加器专门用于提供一种机制,用于在集群中的工作节点之间执行拆分时安全地更新变量 . 
打印RDD的元素
另一个常见的习惯用法是尝试使用rdd.foreach(println)或rdd.map(println)打印出RDD的元素 . 在单台机器上,这将产生预期的输出并打印所有RDD的元素 . 但是,在cluster模式下,由Executor执行输出写入的是Executor的stdout,而不是driver上的那个stdout,所以driver的stdout不会显示这些!要在driver中打印所有元素,可以使用该collect()方法首先将RDD数据带到driver节点:rdd.collect().foreach(println) . 但这可能会导致driver程序内存不足,因为collect()会将整个RDD数据提取到driver端; 如果您只需要打印RDD的一些元素,则更安全的方法是使用take():rdd.take(100).foreach(println) . 

jdk版本 1.7 1.8
mysql版本 

spark1.6.2兼容scala2.10.5下jdk1.7
spark2.x兼容scala2.11下jdk8
Apache Hadoop3.x需要java8
Apache Hadoop的2.7及更高版本需要Java 7
Apache Hadoop(2.6及更早版本)支持Java 6
Apache Hive从第一个release版本到Hive1.2.1的一系列功能都已被很多开发者熟知,国内不少厂商也使用到Hive1.1或1.2版本的特性 . 但是在2016年之后逐渐发布的Apache Hive2.x版本中,四个重大特性

亚秒,少于一秒
Hive2.1推出的LLAP是下一代分布式计算架构,它能够智能地将数据缓存到多台机器内存中,并允许所有客户端共享这些缓存的数据,同时保留了弹性伸缩能力 . 通过LLAP(Live Long and Process),Hive2.1进行了极大的性能优化 . 在Hive2.x开启LLAP与Apache Hive1.x进行对比测试,其性能提升约25倍 . 
同样基于Hive on Tez的TPC-DS查询,做"Hive1.x+Tez"和"Hive2.1+Tez+LLAP"的性能对比测试,结果是后者性能提升到前者的25倍了 . 


HIVE2.0增加了LLAP(低延迟分析处理),并在2.1版本进行了改进,相较于HIVE 1有了25倍的查询性能提升 . 
LLAP以多线程方式采用内存进行计算 . 智能地将数据缓存到多台机器内存中,并允许所有客户端共享这些缓存的数据,同时保留了弹性伸缩能力 . 


impala中一些查询运行时间超过30分钟,而HIVE中不到1分钟 . 
在30秒内impala稍微具有优势,但随时间的增加,HIVE的查询完成数量始终保持领先 . 这表明Impala在不太复杂的查询中表现良好,但随着查询复杂性的增加而变得困难 . 另一方面,随着LLAP的引入,Hive在简单查询中获得了良好的性能,同时保留了在复杂查询方面表现良好的能力 . 
\
hive 的更新操作一直是大数据仓库头痛的问题,在3.x之前也支持update,但是速度太慢,还需要进行分桶,现在hive 支持全新ACID,并且底层采用TEZ 和内存进行查询,性能是hive2的50倍 . 生产建议升级到hive3.1.1版本 . 


批处理和交互式工作负载的HDP 3.0查询执行体系结构:


gc 

垃圾回收主要发生在线程共享的方法区和java堆中
Java将堆内存分为3大部分:新生代 老年代和永久代,其中新生代又进一步划分为Eden S0 S1(Survivor)三个区 . 

年老代(Tenured)
持久代(Perm)

方法区里存放着类的版本,字段,方法,接口和常量池 . 常量池里存储着字面量和符号引用 . 
简单地讲,一个Native Method就是一个java调用非java代码的接口 . 一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C . 这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数 . 
可以认为是 Native 方法相当于 C/C++ 暴露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 方法 . 当线程调用 Java 方法时,虚拟机会创建一个栈帧并压入 Java 虚拟机栈 . 然而当它调用的是 native 方法时,虚拟机会保持 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简单地动态连接并直接调用指定的 native 方法 . 


Jvm为每个新创建的线程都分配一个堆栈 . 堆栈以帧为单位保存线程的状态 . jvm对堆栈只进行两种操作:以帧为单位的压栈和出栈操作 . 


Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError:PermGen space错误的 .  所以在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了 . 为什么要移动,Perm 区域太小是一个主要原因,当然据消息称 jdk8 已经直接取消了 Perm 区域,而新建立了一个元区域 . 应该是 jdk 开发者认为 Perm 区域已经不适合现在 JAVA 的发展了 . 
常量池,分为class常量池和运行时常量池,运行时的常量池是属于方法区的一部分,而Class常量池是Class文件中的 . 

方法区也称非堆(Non-Heap),又是一个被线程共享的内存区域 . 其中主要存储加载的类字节码 class/method/field等元数据对象 static-final常量 static变量 jit编译器编译后的代码等数据 . 另外,方法区包含了一个特殊的区域"运行时常量池"

jdk6中的常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的,而 new 出来的 String 对象是放在 JAVA Heap 区域 . 所以拿一个 JAVA Heap 区域的对象地址和字符串常量池的对象地址进行比较肯定是不相同的,即使调用String.intern方法也是没有任何关系的 . 
jdk7中的字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了
jdk8中取消了Perm区,元空间取代了Perm区 . 当使用intern()时堆内存中存在对象内容是"某字符串",但此时常量池中是没有 "某字符串"对象,常量池中不需要再存储一份对象了,可以直接存储堆中的引用 . (元空间仍然与堆不相连,但与堆共享物理内存, jdk8开始的元空间没有逻辑上可认为是在堆中,它也可以指定最大最小值,默认会一直增长下去 . )
注意:在jdk7后字符串常量池在Java Heap中,但是在规范中划分在方法区中


String类型的常量池比较特殊 . 它的主要使用方法有两种:

java常量用final修饰


堆是用来存放对象而栈是用来执行程序的 . 
对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去配对delete/free代码,不容易出现内存泄露和内存溢出问题 . 但是,也正是因为Java把内存控制权交给了虚拟机,一旦出现内存泄露和内存溢出的问题,就难以排查,因此一个好的Java程序员应该去了解虚拟机的内存区域以及会引起内存泄露和内存溢出的场景 . 

学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆 栈以及静态数据区 . 

首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行 . 在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存 . 因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间) . 
    CMS 
    G1         HBASE

不会的时候部署的时候没仔细看只管用
笔试编程
    一个有序的数组构成平衡的二叉树


    给定一个字符串 求最大的回文长度

    给定两个有序数组 合并一个有序的数组 java
题目:有两个有序数组a[]和b[],将它们合并成数组c[],需要c[]也是有序数组 . 
思路:新建一个以两个集合长度之和为长度的新数组,从两数组最左边开始比起,把小的放入新集合,并用变量标记后一位置,
每次比较都是比较的最左边未比较过的元素(通过变量),循环比较,直至其中有一个集合遍历结束,将另一个集合剩余部分加入新集合中

2阶段
大数据生态圈压缩哪几种 区别 什么场景用


文件格式哪几种 区别 什么场景用

HDFS读写流程 output input


HDFS默认强一致性

HDFS的NN的内存在生厂商如何规划(从元数据思考)


INode继承关系图解 


当用户读取文件时,是从NameSpace中读取吗?还是直接读BlockMap?
回复:先从NameSpace中获取到与INodeFile关联的Blocks,然后在BlockMap中通过BlockId快速定位到具体的BlockInfo . 


存在内存中的元数据支持客户端的读操作,也是最完整的元数据 . 
当客户端对 HDFS 中的文件进行新增或者修改操作,操作记录首先被记入 edits 日志文件中,当客户端操作成功后,相应的元数据会更新到内存元数据中 . 因为 fsimage 文件一般都很大(GB 级别的很常见),如果所有的更新操作都往 fsimage 文件中添加,这样会导致系统运行的十分缓慢 . 
一是内存中数据更新 查询快,极大缩短了操作响应时间;
二是内存中元数据丢失风险颇高(断电等),因此辅佐元数据镜像文件(fsimage)+编辑日志文件(edits)的备份机制进行确保元数据的安全 . 
dfs.namenode.name.dir 属性可以配置多个目录,各个目录存储的文件结构和内容都完全一样,相当于备份,这样做的好处是当其中一个目录损坏了,也不会影响到 Hadoop 的元数据,特别是当其中一个目录是 NFS(网络文件系统 Network File System,NFS),即使你这台机器损坏了,元数据也得到保存 . 

$dfs.namenode.name.dir/current目录下在format的同时也会生成fsimage和edits文件,及其 对应的md5校验文件 . 
所以定期及时的备份fsimage edits和seen_txid文件非常重要(建议直接备份current整个目录)!即使存在HA的架构建议也备份下,多一份备份多一分安全 . 
SecondaryNameNode 的职责是合并 NameNode 的 edit logs 到 fsimage 文件中 . 

首先,checkpoint 之前要先进入安全模式 . 进入安全模式后,执行saveNamespace命令,他会把a-nn的fsimage 与 大于fsimage txid的editlog(包括finalized 与 in_progress的)合并成新的fsimage并落盘,然后新生成一个editlog . 

fsimage 镜像文件:是元数据的一个持久化的检查点,包含 Hadoop 文件系统中的所有目录和文件元数据信息,但不包含文件块位置的信息 . 文件块位置信息只存储在内存中,是在 datanode 加入集群的时候,namenode 询问 datanode 得到的,并且间断的更新 . 
Edits 编辑日志:存放的是 Hadoop 文件系统的所有更改操作(文件创建,删除或修改)的日志,文件系统客户端执行的更改操作首先会被记录到 edits 文件中 . 
fsimage 和 edits 文件都是经过序列化的,在 NameNode 启动的时候,它会将 fsimage 文件中的内容加载到内存中,之后再执行 edits 文件中的各项操作,使得内存中的元数据和实际的同步,存在内存中的元数据支持客户端的读操作,也是最完整的元数据 . 

如果 NameNode 中的 fsimage 真的出问题了,还是可以用 SecondaryNamenode 中的 fsimage 替换一下 NameNode 上的 fsimage,虽然已经不是最新的 fsimage,但是我们可以将损失减小到最少!
小文件过多 什么危害 如何规避
小于或者等于30M的文件
小文件不仅是指文件比较小,如果Hadoop集群中的大量文件略大于block size,同样也会存在小文件问题 . 
1. 现在我们越来越多的将Hadoop用于(准)实时计算,在做数据抽取时处理的频率可能是每小时,每天,每周等,每次可能就只生成一个不到10MB的文件 . 
2.数据源有大量小文件,未做处理直接拷贝到Hadoop集群 . 
3.MapReduce作业的配置未设置合理的reducer或者未做限制,每个reduce都会生成一个独立的文件 . 另外如果数据倾斜,导致大量的数据都shuffle到一个reduce,然后其他的reduce都会处理较小的数据量并输出小文件 . 
每个对象在内存中大概占用150个字节
一个文件130M % 128M=1…2m
结果是1个块128m,1个块2m
一碗水130ml 一个瓶子规格容量128ml
只能2个瓶子:第1个装满128ml 第二个装不满,实为2ml
接上个例子130m的文件
10m的10个文件 10块
30m的1个文件 1块
共有11个文件:11块
系统维护一般轻量级会比较好,本身可以2个块去维护,但现在需要11个块维护 ,如果数量级比较大有可能会把nn撑爆
NN的大小是4G大概是42亿字节
1个小文件(阈值<=30m):nn节点维护的字节大约150字节
1亿个小文件 150b1亿=150亿字节=15G
反而如果1亿个小文件合并100万个大文件:1.5亿字节=0.15G则不会被撑爆
(1)HDFS不适合大量小文件的存储,因namenode将文件系统的元数据存放在内存中,因此存储的文件数目受限于 namenode的内存大小 . HDFS中每个文件 目录 数据块占用150Bytes . 如果存放的文件数目过多的话会占用很大的内存甚至撑爆内存
(2)HDFS适用于高吞吐量,而不适合低时间延迟的访问 . 如果同时存入大量的小文件会花费很长的时间
(3) 流式读取的方式,不适合多用户写入,以及任意位置写入 . 如果访问小文件,则必须从一个datanode跳转到另外一个datanode,这样大大降低了读取性能 . (
如果集群中有大量小文件,会降低MapReduce的处理性能,无论是Hive,Pig还是Java MapReduce,当然其实其他计算引擎比如Spark,Impala也会受到影响 . 
第一个原因是大量小文件意味着大量的随机磁盘IO . 磁盘IO通常是MapReduce性能的最大瓶颈之一,在HDFS中对于相同数量的数据,一次大的顺序读取往往优于几次随机读取的性能 . 如果可以将数据存储在较少,而更大的一些block中,可以降低磁盘IO的性能影响 . 
性能下降的第二个原因当MapReduce任务启动时,每个数据block会被分配为一个map任务 . HDFS中的每个文件至少是一个block . 如果你有10000个文件,而且每个文件10MB,那么这个MapReduce作业会被分配为10000个map任务 . 一般来说每个Hadoop的map任务会运行在自己的JVM中,所以会带来10000个JVM的启动和关闭的资源开销 . 
集群的资源是有限的,为了方便计算,假设我们在YARN的配置中为每个NodeManager配置20个vcore,那么为了同时运行10000个mapper,你需要500台节点 . 大多数Hadoop集群都小于这个规模,所以一般情况下大量map任务可能只能排队等待ResourceManager来分配资源 . 如果你只有10台机器,那么总共只有200个vcore,这个排队的队列会较大,相应的处理时间也会变的较长 . 另外,往往你的集群中可能不止这一个作业 . 
如果10000个10MB文件换成800个128MB的文件,那么你就只需要800个map任务 . 相当于减少了一个数量级的JVM维护时间,同时也优化了磁盘IO . 尽管一个单独的map任务处理一个128MB的文件比一个10MB的文件时间要慢,但是整个作业的总运行时间肯定可以降低一个数量级 . 
)
1.当NameNode重启时,它都需要从本地磁盘读取每个文件的元数据,意味着你要读取300GB数据到内存中,不可避免导致NameNode启动时间较长 . 
2.一般来说,NameNode会不断跟踪并检查每个数据块的存储位置 . 这是通过DataNode的定时心跳上报其数据块来实现的 . 数据节点需要上报的block越多,则也会消耗越多的网络带宽/时延 . 即使节点之间是高速网络(万兆/光纤),但不可避免的会带来一些不好的影响 . 
3.NameNode本身使用300G内存,相当于JVM你需要配置300GB的heap,对于JVM来说本来就存在稳定性的风险,比如GC时间较长 . 

所以优化和解决小文件问题基本是必须的 . 如果可以减少集群上的小文件数,则可以减少NameNode的内存占用,启动时间以及网络影响 . 
解决方案
生产上首先需要设置小文件的阈值,到达这个值对小文件进行合并 . 对于这个合并,一种是在HDFS存储之前就进行合并,还有一种就是计算完之后根据业务周期来进行合并 . 后一种需要在计算时格外对小文件进行调整 . 
如果nn撑爆,在大数据中,大数据的结构基本上都是主从架构,主节点的nn起到非常重要的作用,nn挂了,所有对外的服务,读写的流程将不能运行 . 
生产上:,
对小文件阈值进行估算(
解决小文件问题的最简单方法就是在生成阶段就进行杜绝 . 如果是由数据源产生大量小文件并直接拷贝到Hadoop,可以调研了解数据源是否能生成一些大文件,或者从数据源到HDFS的数据抽取过程中进行数据处理合并小文件 . 如果每小时只抽取10MB的数据,考虑是否改为每天一次,这样创建1个240MB的文件而不是24个10MB的文件 . 但是,你可能无法控制数据源的改动配合或业务对数据抽取间隔的需求,这样小文件问题无法避免,这时可能需要考虑其他的解决方案 . 
)
2.合并小文件,数据未落地到hdfs之前合并或者数据已经落到hdfs,用spark service服务每天调度去合并 -15天
具体情况根据公司的业务周期进行合并,比如今天是15号则合并1号的文件,16号合并2号的
(当产生小文件是不可避免时,文件合并是常见的解决方案 . 使用这种方法,你可以定期运行一个MapReduce任务,读取某一个文件夹中的所有小文件,并将它们重写为较少数量的大文件 . 比如一个文件夹中有1000个文件,你可以在一个MapReduce任务中指定reduce的数量为5,这样1000个输入文件会被合并为5个文件 . 随后进行一些简单的HDFS文件/文件夹操作(将新文件覆盖回原目录),则可以将NameNode的内存使用减少到200分之1,并且可以提高以后MapReduce或其他计算引擎对同一数据处理的性能
检查所有文件夹并确认哪些文件夹中的小文件需要合并,目前主要是通过自定义的脚本或程序,当然一些商业工具也能做,比如Pentaho可以迭代HDFS中的一组文件夹,找到最小合并要求的文件夹 . 这里还推荐另外一个开源工具File Crush
https://github.com/edwardcapriolo/filecrush/
File Crush没有专业支持,所以无法保证它可以与Hadoop的后续版本一直保持兼容 . 
批量合并文件的方法无法保留原始文件名,如果原始文件名对于你了解数据来源非常重要,则批量合并文件的方法也不适用 . 但一般来说,我们一般只会设计HDFS的各级目录的文件名,而不会细化到每个文件的名字,所以理论来说这种方法问题也不大 . 
)
3使用CombineFileInputFormat(在mapper中将多个文件合成一个split作为输入,CombineFileInputFormat满足我们的需求)
CombineFileInputFormat是Hadoop提供的抽象类,它在MapReduce读取时合并小文件 . 合并的文件不会持久化到磁盘,它是在一个map任务中合并读取到的这些小文件 . 好处是MapReduce可以不用为每个小文件启动一个map任务,而且因为是自带的实现类,你不用额外将小文件先提前合并 . 这解决了MapReduce作业启动太多map任务的问题,但是因为作业仍然在读取多个小文件,随机磁盘IO依旧是一个问题 . 另外,CombineFileInputFormat大多数情况下都不会考虑data locality,往往会通过网络从其他节点拉取数据 . 
为了实现这个,需要为不同的文件类型编写Java代码扩展CombineFileInputFormat类 . 这样实现一个自定义的类后,就可以配置最大的split大小,然后单个map任务会读取小文件并进行合并直到满足这个大小 . 
如果是Hive作业有简单的方式,直接配置以下参数即可:
hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat
set mapreduce.input.fileinputformat.split.maxsize=1073741824  
set mapreduce.input.fileinputformat.split.minsize=1073741824
以上是以Hive的单个map作业合并小文件到1GB为示例 . 
注意以上无论是MapReduce代码实现方式还是Hive,因为合并的文件并不会持久化保存到磁盘,因此CombineFileInputFormat方式并不会缓解NameNode内存管理问题 . 只是提高MapReduce或者Hive作业的性能 . 
4通过Hive合并小文件
如果你在使用Hive时因为"create table as"或"insert overwrite"语句输出了小文件,你可以通过设置一些参数来缓解 . 通过设置这些参数 . Hive会在本身的SQL作业执行完毕后会单独起一个MapReduce任务来合并输出的小文件 . 
注意这个设置仅对Hive创建的文件生效,比如你使用Sqoop导数到Hive表,或者直接抽数到HDFS等,该方法都不会起作用 . 
5 Sequence文件
当需要维护原始文件名时,常见的方法是使用Sequence文件 .  在此解决方案中,文件名作为key保存在sequence文件中,然后文件内容会作为value保存 . 下图给出将一些小文件存储为sequence文件的示例:
如果一个sequence文件包含10000个小文件,则同时会包含10000个key在一个文件中 . sequence文件支持块压缩,并且是可被拆分的 . 这样MapReduce作业在处理这个sequence文件时,只需要为每个128MB的block启动一个map任务,而不是每个小文件启动一个map任务 . 当你在同时抽取数百个或者数千个小文件,并且需要保留原始文件名时,这是非常不错的方案 . 
但是,如果你一次仅抽取少量的小文件到HDFS,则sequence文件的方法也不太可行,因为sequence文件是不可变的,无法追加 . 比如3个10MB文件将产生1个30MB的sequence文件,根据本文前面的定义,这仍然是一个小文件 . 另外一个问题是如果需要检索sequence文件中的文件名列表则需要遍历整个文件 . 
另外一个问题是Hive并不能较好的处理由该方法合并出来的sequence文件 . Hive将value中的所有数据视为单行 . 这样会导致Hive查看这些数据不方便,因为以前小文件中的一行的所有数据也是Hive中的单行,即相当于只有一个字段 . 同时,Hive没办法访问这种sequence的key,即文件名 . 当然你可以自定义Hive serde来实现,不过这个超过了本文需要讨论的范围 . 
6 解决小文件问题,除了HDFS存储外,当然还可以考虑HBase列式存储 . 使用HBase可以将数据抽取过程从生成大量小HDFS文件更改为以逐条记录写入到HBase表 . 如果你对数据访问的需求主要是随机查找或者叫点查,则HBase是最好的选择 . HBase在架构上就是为快速插入,存储大量数据,单个记录的快速查找以及流式数据处理而设计的 . 但如果你对数据访问的需求主要是全表扫描,则HBase不是最适合的 . 
可以基于HBase的表的数据创建Hive表,但是查询这种Hive表对于不同的查询类型性能会不一样 . 当查询单行或者范围查找时,Hive on HBase会表现不错,但是如果是全表扫描则效率比较低下,大多数分析查询比如带group by的语句都是全表扫描 . 
使用HBase,可以较好的应对实时数据写入以及实时查询的场景 . 但是如何分配和平衡HBase与集群上其他的组件的资源使用,以及HBase本身运维都会带来额外的运维管理成本 . 另外,HBase的性能主要取决于你的数据访问方式,所以在选择HBase解决小文件问题之前,应该进行仔细调研和设计 . 
7 DistCp (如果使用Amazon EMR)
此解决方案仅适用于Amazon EMR的用户,当然你在AWS中使用CDH也一样 . Amazon EMR集群一般设计为短期存储,而在S3中持久化保存数据 . 即使使用S3,依旧存在小文件问题,所以这时需要选择S3DistCp . 
S3DistCp是由Amazon提供的一个工具,用于分布式将S3中的数据拷贝到临时的HDFS或其他S3 bucket . 这个工具可以通过配置groupBy和targetSize参数来将文件合并到一起 . 如果S3中存储了数千个EMR需要处理的小文件时,这个工具是一个不错的选择 . S3DistCp通过连接许多小文件并导入到HDFS中,据报道,该方式的性能也非常优秀 . 
S3DistCp这个工具跟之前文章提到的批量合并文件的方法其实是类似的,只是说Amazon给你提供了一个现成的工具 . 
8
(
常见的有两种解决方案,减少集群的NameNode中的对象数量,或者以某种方式让NameNode使用更多的"内存"但不会导致较长的启动时间,这就是Hadoop Archive(HAR)文件和NameNode联邦 . 
1.hadoop archive files通过将许多小文件打包到更大的HAR文件中来缓解NameNode内存问题,类似于Linux上的TAR文件 . 这样可以让NameNode只处理单个HAR文件,而不是数十个或数百个小文件 . 可以使用har://前缀而不是hdfs://来访问HAR文件中的文件 . HAR文件是基于HDFS中已有的文件创建的 . 因此,HAR文件不仅可以合并从数据源抽取到HDFS中的数据,也可以合并通过正常的MapReduce处理创建的数据 . HAR文件可以独立的用于解决小文件问题,除了HDFS,没有其他的依赖 . 
虽然HAR文件减少了NameNode中小文件对内存的占用,但访问HAR文件内容性能可能会更低 . HAR文件仍然随机存储在磁盘上,并且读取HAR内的文件需要访问两个索引 - 一个用于NameNode找到HAR文件本身,一个用于在HAR文件内找到小文件的位置 . 在HAR中读取文件实际上可能比读取存储在HDFS上的相同文件慢 . MapReduce作业的性能同样会受到影响,因为它仍旧会为每个HAR文件中的每个文件启动一个map任务 . 
所以这里我们需要有一个权衡(trade-off),HAR文件可以解决NameNode内存问题,但同时会降低读取性能 . 如果你的小文件主要用于存档,并且不经常访问,那么HAR文件是一个很好的解决方案 . 如果小文件经常要被读取或者处理,那么可能需要重新考虑解决方案 . 
2.NameNode联邦允许你在一个集群中拥有多个NameNode,每个NameNode都存储元数据对象的子集 . 这样可以让所有的元数据对象都不止存储在单个机器上,也消除了单个节点的内存限制,因为你可以扩容 . 这听上去是一个很美丽的方案,但其实它也有局限性 . 
NameNode联邦隔离了元数据对象 - 仅仅只有某一个NameNode知道某一个特定的元数据对象在哪里,意思就是说如果你想找到某个文件,你必须知道它是保存在哪个NameNode上的 . 如果你的集群中有多个租户和/或隔离的应用程序,那使用NameNode联邦是挺不错的,你可以通过租户或者应用程序来隔离元数据对象 . 但是,如果要在所有的应用程序之间共享数据,则该方法其实也并不是完美的 . 
由于NameNode联邦并不会改变集群中对象或者块的数量,所以它并没有解决MapReduce的性能问题 . 相反,联邦会增加Hadoop集群安装和维护的复杂度 . 所以我们说联邦可以解决小文件问题,倒不如说它提供了一种办法让你"隐藏"小文件 . 
)


YARN工作流程
YARN调度器哪几种 区别 CDH动态资源池
YARN生产上调优参数那些?如何调优规划(让内存最大优化利用)VCORE(yarn上的core)swap(内存不够用,但是有集群关闭为什么)
hive内部表外部表区别
hive外部表有 静态 动态区别是什么
UDF函数 永久生效()和临时生效(add)
sortby orderby clusterby distributeby
sqoop如何增量抽取到hive对应hive表如何设计
hbase的rowkey如何设计?如果设计不当会有什么问题,请举例
hbase的读写流程经过master吗?加入不经过 那么什么流程经过master
put scan filter过滤
hbck命令有了解吗 哪些故障 哪些命令请举例
钉钉 获取其他的消息通知关闭掉 微信步数和钉钉步数关闭
车联网
驾驶行为评估 操作行为报警 车速报警
车辆信息表100M
上报数据表 每天1T 总量达到PB级别
phoenix
flume如何抽取数据 记录pos点 挂了怎么办 pos点用哪一个source taildrsource能支持递归吗(不支持)
flume源代码 有没有做过二次开发
kafka ack有哪几种 生产选择哪个 1
kafka offset有绝对和相对的说法吗 请解释一下
kafka如何如何根据offset寻找数据 二分法稀疏表
kafka生产者和消费者生产上如何做监控,看数据是否及时消费
saprk主 flink()次
第三阶段
spark数据倾斜的解决方案
    数据打散
    大key抽出来单独跑
美团的两篇数据清洗哦的文章
spark源代码阅读过没?贡献
sparkstreaming+kafka
spark2.4.0之下bug
找不到partition
sparkreducebykey和groupbykey区别
repartition coalesce区别 这一块没注意到,以后排查
sparkstreaming+kafka版本多少?官网1:1看过没?offset如何管理,消费语义选择哪种,多少秒一个批次,一个批次多少数据量,一个批次处理时间多少
怎样监控job的是否有堆积消息 batch未处理?
加入mysql--》kafka-- 有序数据,请问如何保证spark streaming消费全局有序
sparksql外部数据源有哪几种?源代码看过没?如何自定义外部数据源
structured streaming的流(结构化流) 的join流做过没
结构化流的水印 watermark了解没
flink如何保证exactly-once(消费语义) state

CDH日志如何查看 x.log role log(web界面角色日志)
CDH监控 预警
CDH动态资源池 多租户 放置规则


考虑压缩比1:5~10
几百g左右数据,10台生产集群,3台测试集群

flume断点续传,多目录

+.3

kafka里数据保存7天
多少张表多少个topic
topic一般双副本增加io传输速度
通常3-10个分区影响消费能力并发度


2-50
2×(50×2/100)+1 = 3台

zookeeper里没有producer信息


数据量越大越有优势,pb级
几百g用ES
elsearch:实时的分布式索引,分布式引擎
elsearch是基于apache luence的开源搜索引擎,封装了luence的检索细节,只是走restfulapi . 
快的原因是底层存储索引,安装查询即可

猜你喜欢

转载自blog.csdn.net/qq_34387470/article/details/114587924