sqoopFIlnk
Sqoop 问题
sqoop是用来将数据在Hadoop和关系型数据库服务器之间传送的工具。
导入:mysql、oracle导入数据导hadoop的hdfs、hive、hbase等数据存储系统;
导出:从hadoop文件系统导出到关系型数据库
工作机制|原理:将导出或导入命令翻译成mapreduce程序来实现,使用代码生成工具可以查看sqoop生成的java代码,并可在此基础上进行深度定制开发。
-
sqoop使用时有没有遇到什么问题?
Hive中的null在底层是以“/N”来存储的,而mysql中的null在底层就是null,为了保证数据两端一致性,在导入导出数据时需要用参数来调整为一致,导出时加上参数--input-null-string,和--input-null-non-string,导入时--null-string和--null-non-string
-
sqoop导出数据一致性问题:4个map任务,执行过程中,有2个任务失败,此时mysql中已经存储了另外2个map任务的数据,怎么办?
加上参数设置--staging-table可以解决一致性问题,它指定了登台表来解决,该选项充当用于暂存导出数据的辅助表,分阶段数据最终在单个事务中移动到目标表。
-
Sqoop底层运行的任务特点是:只有Map阶段,没有Reduce阶段的任务。
Flink优化问题
-
优化:
-
Operator Chain:Flink会尽可能地将operator的subtask链接在一起(chain)形成task,这样可以优化很多性能。能减少线程之间的切换,减少消息的序列化/反序列化,减少数据在缓冲区的交换,最终减少延迟同时提高整体吞吐量。Chain的规则是:没有shuffle过程、上下游并行度一样、没有禁用chain……
-
slot sharing:来自同一个Job且拥有相同的slotSharingGroup(默认:default)名称的不同Task的SubTask之间可以共享一个Slot,这使得一个slot有机会持有Job的一整条Pipeline,这也是默认slotSharing条件下Job启动所需的Slot数和Job中Operator的最大parallelism相等的原因。通过slotSharing机制可以更进一步提高Job运行性能,在Solt数不变的情况下增加了Operator可设置的最大并行度。让消耗资源大的Task以最大的并行度分布在不同的TaskManager上,同时像map、filter这种比较简单的操作也不会独占slot资源。
-
Flink异步IO
流式计算中,常常需要与外部系统进行交互,而往往一次连接中你那个获取连接等待通信的耗时会找比较高。下图是两种方式对比示例:
异步IO使网络等待时间极大地缩短,增加了吞吐、降低了延迟。
-
强大地CheckPoint机制
作用:保证Exactly Once级别地数据恢复
- 快照的存储引擎:hdfs、rocksdb、memory,做快照的数据大小较大时,考虑采用Rocksdb来作为存储
- 资源设置:我们都知道checkpoint机制是在每个task上进行, 那么当总的状态数据大小不变的情况下,如何分配减少单个 task 所分的 checkpoint 数据变成了提升 checkpoint 执行效率的关键。
- 增量快照。非增量快照下,每次 checkpoint 都包含了作业所有状态数据。而大部分场景下,前后 checkpoint 里,数据发生变更的部分相对很少,所以设置增量 checkpoint,仅会对上次 checkpoint 和本次 checkpoint 之间状态的差异进行存储计算,减少了 checkpoint 的耗时。
-
-
自己可以优化的地方:
-
使用Flink Tuples:当使用groupBy、join、keyBy这些需要主键的操作时,可以用主键选择函数,也可以用POJO类型中指定字段名称,但用Flink tuple类型会更简单:直接用字段元组的位置即可指定;
-
复用Flink对象:不要每调一次函数都创建中间对象,辅助:value class:IntValue、LongValue、StringValue等等
-
使用注解功能:由于Flink不能解析和理解代码,所以可以提供一些有利于构建更有效执行计划的重要信息,可以使用以下三个注解:
- @ForwardedFields:指定输入值到输出值过程中哪些字段保持不变、用于输出的,或哪些位置不变值地移动到指定位置;
- @NotForwardedFields:指定在输出中未保留相同位置的字段
- @ReadFields:指定用来计算结果值的字段,指定的字段应该只在计算中使用,而不仅仅是复制到输出参数中
// Specify that the first element is copied without any changes @ForwardedFields("0") class MyFunction implements MapFunction<Tuple2<Long, Double>, Tuple2<Long, Double>> { @Override public Tuple2<Long, Double> map(Tuple2<Long, Double> value) { // Copy first field without change return new Tuple2<>(value.f0, value.f1 + 123); } } //这意味着输出元组的第一个元素没有被更改,它将返回到相同的位置
// 1st element goes into the 2nd position, and 2nd element goes into the 1st position @ForwardedFields("0->1; 1->0") class SwapArguments implements MapFunction<Tuple2<Long, Double>, Tuple2<Double, Long>> { @Override public Tuple2<Double, Long> map(Tuple2<Long, Double> value) { // Swap elements in a tuple return new Tuple2<>(value.f1, value.f0); } }
@ForwardedFields只能应用于只有一个输入参数的函数,例如map、flatMap,如果有两个输入参数,则可以使用@ForwardedFieldsFirst,@ForwardedFieldsSecond。
// Two fields from the input tuple are copied to the first and second positions of the output tuple @ForwardedFieldsFirst("0; 1") // The third field from the input tuple is copied to the third position of the output tuple @ForwardedFieldsSecond("2") class MyJoin implements JoinFunction<Tuple2<Integer,String>, Tuple2<Integer,Double>, Tuple3<Integer, String, Double>>() { @Override public Tuple3<Integer, String, Double> join(Tuple2<Integer, String> first, Tuple2<Integer, Double> second) throws Exception { return new Tuple3<>(first.f0, first.f1, second.f1); } })
-
细化Join类型:
- BROADCAST_HASH_FIRST :第一个数据集小得多
- BROADCAST_HASH_SECOND:第二个数据集小得多
- REPARTITION_HASH_FIRST :第一个数据集稍微小一点
- REPARTITION_HASH_SECOND:第二个数据集要小一点
- REPARTITION_SORT_MERGE:使用排序和合并策略对数据集进行重新分配
-
附:Join如何工作:
当Flink批量处理数据时,集群中的每台机器都存储了部分数据,要执行Join,Apache Flink需要找到满足连接条件的两个数据集,为了做到这一点,Flink必须先将两个数据集的项目放到同一台机器上,这里有两种策略:
- Repartition-分配策略:两个数据集都被各自的主键分离了,并通过网络发送,这意味着如果数据集很大可能需要大量时间才能通过网络完成复制。
- 广播转发策略:这种情况下,一个数据集不受影响,但第二个数据集被复制到集群中的每台机器上,他们都有第一个数据集的一部分。如果某个小数据集join到大数据集,那么可以使用广播转发策略。
-
Flink其他问题
-
Flink作业提交及各组件工作过程?
standalone模式:
- dispatcher分发器启动应用移交给一个JobManager
- JobManager控制一个应用程序执行的主进程,每个应用程序都会被一个不同的JobManager所控制。
- JobManager先接受到要执行的应用程序(包括作业图、逻辑数据流图、打包的所有类、库和其他资源jar包)JobManager会将JobGraph转换为一个物理层面的数据流图(执行图),包含所有并发执行的任务;
- JobManager像ResourceManager请求执行资源,即TaskManager上的Slot,一旦获得足够的资源,就会将执行图分发到真正运行他们的TaskManager上;
- 运行过程中,JobManager会负责所有需要中央协调的操作,比如检查点(checkpoints)的协调
- ResourceManager主要负责管理TaskManager的Slot,包括分配有空闲插槽的TaskManager给JobManager。如果资源不够,需要负责向资源平台发起会话,去启动新的TaskManager.
- TaskManager一般在Flink的工作进程中会有多个,每个TaskManager都包含一定数量的插槽slots,插槽的数据限制了TaskManager能够执行的任务数量,TaskManager启动之后,TaskManager会向ResourceManager注册他的插槽,收到ResourceManager的指令后,TaskManager像JobManager提供slots。得到tasks后执行任务,执行过程中可以跟其他运行同一任务的TaskManager交换数据。
YARN模式
- client上传Flink的Jar包和配置到HDFS中
- client提交作业给ResourceManager,ResourceManager在一个nodeManager中启动ApplicationMaster,里面包含的是JobManager,JobManager加载Flink的Jar并配置构建环境;
- ApplicationMaster向ResouceManager申请资源,并根据反馈启动TaskManager,TaskManager加载Flinkjar包等资源,并执行任务
-
Flink时间类型有哪些,他们有什么区别?
- 事件时间(Event Time):事件时间是每个独立事件在设备上产生的时间,在进入Flink之前已经有了。
- 接入时间( Ingestion Time):数据进入Flink系统的时间,这个时间依赖于Source Operator所在的主机系统时钟。
- 处理时间(Processing Time):处理时间是指数据在操作算子计算过程中获取到的所在主机。优点:性能较高,延迟较低,缺点:不能处理乱序问题,尤其是每台机器时钟本身不同步,而数据不乱序,也会导致处理时数据乱序。
-
Flink窗口类型?
-
滚动窗口(Tumbling window):固定时间间隔分配窗口,每个窗口间没有重叠;
//tumbling time windows(翻滚时间窗口) data.keyBy(1) .timeWindow(Time.minutes(1)) //tumbling time window 每分钟统计一次数量和 .sum(1);
-
滑动窗口(Sliding windows):固定相同间隔分配窗口,只不过窗口间有重叠
data.keyBy(1) .timeWindow(Time.minutes(1), Time.seconds(30)) //sliding time window 每隔 30s 统计过去一分钟的数量和 .sum(1);
-
会话窗口(session windows):根据活动的时间进行窗口化,他们通常不重叠,也没有一个固定的开始和结束。通常由“一段时间没有收到元素”而将窗口断开
// 静态间隔时间 WindowedStream<MovieRate, Integer, TimeWindow> Rates = rates .keyBy(MovieRate::getUserId) .window(EventTimeSessionWindows.withGap(Time.milliseconds(10))); // 动态时间 WindowedStream<MovieRate, Integer, TimeWindow> Rates = rates .keyBy(MovieRate::getUserId) .window(EventTimeSessionWindows.withDynamicGap(()));
-
全局窗口(Global windows):同keyed的元素分配到一个窗口里
WindowedStream<MovieRate, Integer, GlobalWindow> Rates = rates .keyBy(MovieRate::getUserId) .window(GlobalWindows.create());
-
-
Flink里的状态?
-
状态:由一个任务维护,用来计算某个结果的所有数据,都属于这个任务的状态,可以认为是一个本地变量。
-
分两大类:OperatorState和KeyedState,使用最多的ValueState、ListState、MapState
OperatorState(不常用):
- 列表状态:将状态表示为一组数据的列表
- 联合列表状态:也是一种数据列表,但发生故障或从保存点启动应用程序时如何恢复不同
- 广播状态:如果一个算子有多项任务,而每项任务状态又都相同,那广播状态最适合
KeyedState:
- 值状态:将单个值维护成状态
- 列表状态:将为一组数据的列表维护成状态
- 映射状态(Map state):将一组key-value对维护成状态
- 聚合状态(Reducing state & Aggregating State):将用于聚合操作的列表维护成状态
- 使用:键控状态的使用,在RichFunction处理函数中:
Scala DataStream API中的State
Scala支持mapWithState,即带状态的算子,用来简便使用状态:
val stream: DataStream[(String, Int)] = ... val counts: DataStream[(String, Int)] = stream .keyBy(_._1) .mapWithState((in: (String, Int), count: Option[Int]) => count match { case Some(c) => ( (in._1, c), Some(c + in._2) ) case None => ( (in._1, 0), Some(in._2) ) })
-
过期时间:类似于redis
import org.apache.flink.api.common.state.StateTtlConfig; StateTtlConfig ttlConfig = StateTtlConfig .newBuilder(Time.seconds(1)) .disableCleanupInBackground() .build();
-
状态后端(State Backends)
定义:状态的存储、访问及维护,由一个可插入的组件决定的,这个组件就叫做状态后端
MemoryStateBackend、FsStateBackend、RocksDBStateBackend
env.enableCheckpointing(60000) //启动快照机制
flink-statebackend-rocksdb_2.11
-
-
Flink如何处理延迟数据?
Flink窗口处理虽然提供了基础的EventTime的WaterMark机制,但只能在一定程度上解决数据乱序问题。某些极端情况数据延迟非常严重,WaterMark机制解也无法等到数据全部进入窗口。默认情况下这些严重迟到的数据会丢弃掉,但如果用户希望保留并处理,可以使用Allowed Lateness机制来对迟到的数据做额外处理。
- WaterMark机制是第一层防护
- AllowedLateness属于第二层防护,属于特定window operator的防护。
import org.apache.flink.streaming.api.TimeCharacteristic import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor import org.apache.flink.streaming.api.scala._ import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows import org.apache.flink.streaming.api.windowing.time.Time object demo1 { def main(args: Array[String]): Unit = { val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment env.setParallelism(1) env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime ) val inputStream: DataStream[String] = env.socketTextStream("hadoop102",7777) val outputTag = new OutputTag[SensorReading]("side") val dataStream = inputStream .map(data => { val dataArray = data.split(",") SensorReading(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble) }).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(2)) { override def extractTimestamp(element: SensorReading): Long = { element.timestamp*1000 //我的测试时间戳是s,flink要求ms } }) val minStream: DataStream[SensorReading] = dataStream.keyBy(_.id) // .window( SlidingEventTimeWindows.of(Time.seconds(10),Time.seconds(2))) .timeWindow(Time.seconds(10)) .allowedLateness(Time.seconds(4)) .sideOutputLateData(outputTag) .minBy("temperature") dataStream.print("data") minStream.print("min") minStream.getSideOutput(outputTag).print("slide") env.execute("demo1") } } case class SensorReading(id: String, timestamp: Long, temperature: Double)
总结来说,
- 窗口的作用是为了周期性地获取数据;
- watermark是为了一定程度防止数据乱序(经常);
- allowLateness是将窗口关闭时间再延迟一段时间。AllowedLateness会在水位机制的基础上,再增加一个时间(这段时间内,第二个窗口已经打开),这段时间限制内的延迟数据依然可以改变窗口一的结果状态。如果这段时间之后还有延迟数据,则直接放在测流输出。
- sideOutPut是最后兜底操作,所有过期延迟数据,指定窗口已经彻底关闭了,就会把数据放到侧输出流
-
Flink中异常数据如何修复
- 涉及到state,可以通过Queryable State来查询和修改state
- 通过指定历史时间戳回滚历史数据(删除或覆盖)
- 任务异常挂掉,可以通过checkpoint启动任务
-
流处理和批处理的区别,流处理的难点在哪里?
不同:
- 批处理的数据源是固定存在的,如果作业失败,可以容易地重新运行作 失败部分来重新计算丢失的结果。但流处理却无法做到,因为数据是无穷尽,没有开始也没有结束。
- 批处理只有输出,而流处理还需要备份状态。
流处理难点:
- 时间窗口的选择
- 容错机制
- 精确一次语义保证
-
checkpoint流程
checkpoint机制(一致性检查点):是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如异常退出)出现故障,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性。为应用程序提供精确一次的一致性。
特性:保存某个时间点所有任务的状态(快照),这个时间点应该是所有任务都处理完一个相同输入数据的时候。
说明:上图即为“5”这条数据所有环节都处理完之后的状态保存情况。在下一个保存点前,有数据计算出错,第一步就是重启应用,第二步是从checlpoint中读取状态,将读到的状态恢复到对应的位置,第三步就可以开始消费并处理检查点到故障之间的所有数据。如下:
读到7时出错:
重启:
恢复到上一个检查点:
注意,5是指的偏移量,状态重置包括source部分的偏移量重置,于是会从数据源偏移量为5那里那里重新获取数据。这样肯定要求数据源可以重置offset。
-
checkpoint底层算法
基于Chandy-Lamport算法的分布式快照,将检查点的保存和数据处理分离开,不暂停整个应用。
检查点分界线(Checkpoint Barrier):是一种标记在数据流间的某些点的记号,把数据流分成了不同段,每个标记都在告诉算子收到本标记后,就将当前状态做成新的快照存入backend。
- 假设现在是一个有两个输入流的应用程序,用并行的两个Source任务来读取
-
JobManager会向每个Source任务发送一条带有新检查点ID的消息,通过这种方式来启动检查点
-
首先,数据源将他们的offset状态写入检查点,并像下游广播一个检查点边界barrier
状态后端在状态存入检查点后,会返回通知给source任务,source任务就会向JobManager确认检查点完成
-
barrier向下游传递,sum任务会等待所有输入分区的barrier(蓝黄两个barrier)到达;
算子上如果某个分区的barrier已经到达,那该分区继续到达的数据将会被缓存(如下图蓝4)直到所有分区的barrier全部到达;
算子上如果某个分区的barrier尚未到达,数据将会被正常处理(如下图黄4在黄barrier2前面,所以不停顿直接被处理,结果为蓝2+黄2+黄4,为8)。
-
当收到所有输入分区的barrier时,任务就将其状态保存到状态后端的检查点中,然后将barrier继续向下游发送。
-
向下游转发检查点barrier后,任务继续正常处理
-
sink任务向JobManager确认状态保存到checkpoint完毕
当所有任务都确认已成功将状态保存到检查点时,检查点就是真正完成了。
-
Savepoints:保存点
原理checkpoint完全一样,只是多了一些元数据。但是savepoints不会自动执行,而是需要用户明确地触发创建操作,恢复时也需要指定从哪个savepoints恢复。
保存点的强大功能:除了故障恢复以外,还可以有计划地手动备份,更新应用程序,版本迁移,暂停和重启应用。
-
Flink的状态一致性分类
at-most-once:最多一次
at-least-once:至少一次
exactly-once:精确一次
首先在Flink内部,采用checkpoint机制可以保证精确一次语义。而端到端(end-to-end)的一致性保证,意味着结果的正确性贯穿整个流处理应用始终。
端到端exactly-once:
-
内部保证:checkpoint
-
source端:可重设数据的读取位置(偏移量),对kafka而言,只需要手动提交偏移量
-
sink端:从故障恢复时,数据不会重复写入外部系统
-
幂等写入(HashMap写入相同k-v就是幂等操作)
-
事务写入:
-
构建思想:构建的事务对应着checkpoint,等到checkpoint真正完成的时候才把所有对应的结果写入sink系统中;
-
实现方式:
预写日志(Write-Ahead-Log):把结果数据先当成状态保存,然后在JobManager收到checkpoint完成的通知时,一次性写入sink系统;模板类:GenericWriteAheadSink可以实现这种事务性sink。简单粗暴,相当于缓存后批处理。
两阶段提交(Two-Phase-Commit,2PC):对于每个checkpoint,sink任务会启动一个事务,并将接下来所有接收到的数据添加到事务里。然后将这些数据写入外部sink系统,但不提交它们,只是预提交,等checkpoint完成时,才正式提交事务,实现结果真正写入。真正实现exactly-once这种方式需要一个支持事务的外部sink系统。Flink提供了TwoPhaseCommitSinkFunction接口。
-
-
-
flink的水位机制
-
flink的并行度设置
-
flink窗口
flink双流join
flink反压,怎么发现,怎么解决
Hive问题
- hive工作原理:
-
1.用户提交查询等任务给driver
2.驱动器检查语法和生成查询计划,将HQL发送给编译器
3.①编译器compiler根据用户去元数据库获取需要的元数据信息,②得到元数据信息后对任务进行编译,将逻辑查询转为物理查询,③并提交给Driver,编译完成;
4.Driver将查询计划提交给ExecutionEngine去执行;
5.内部是job执行过程,查询执行是MapReduce工作;
6.执行引擎接受来自数据节点的结果,返给驱动程序,驱动程序返给hive接口。
2. 调优:
1. join:小表join大表
-
limit优化:
hive.limit.row.max.size=100000 hive.limit.optimize.limit.file=10 hive.limit.optimize.enable=false (如果limit较多时建议开启) hive.limit.optimize.fetch.max=50000
- 本地模式
hive.exec.mode.local.auto=false (建议打开) hive.exec.mode.local.auto.inputbytes.max=134217728 hive.exec.mode.local.auto.input.files.max=4
4. 并行执行
hive.exec.parallel=false (建议开启)
hive.exec.parallel.thread.number=8
```
5. 严格模式
```
hive.mapred.mode=strict
```
6. mapper和reducer的个数
```
不是mapper和redcuer个数越多越好,也不是越少越好。
将小文件合并处理(将输入类设置为:CombineTextInputFormat) 通过配置将小文件合并:
mapred.max.split.size=256000000
mapred.min.split.size.per.node=1
mapred.min.split.size.per.rack=1
cal(maxSize,Math.max(blockSize,minSize))
需求:增加mapTask数量,实际上就是修改分片大小变小,修改maxSize<BlockSize
需求:减少mapTask的数量,实际上就是修改分片大小变大,修改minSize>BlockSize
hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat
set mapred.map.tasks=2;
set mapred.reduce.tasks=10
hive.exec.reducers.max=1009
7. 配置JVM重用
mapreduce.job.jvm.numtasks=-1
mapred.job.reuse.jvm.num.tasks=1
```
8. 数据倾斜
```
数据倾斜:由于key分布不均匀造成的数据向一个方向偏离的现象。 本身数据就倾斜
join语句容易造成
count(distinct col) 很容易造成倾斜
group by 也可能会造成
找到造成数据倾斜的key,然后再通过hql语句避免。
hive.map.aggr=true
hive.groupby.skewindata=false (建议开启)
hive.optimize.skewjoin=false
```
9. 多用索引
3. ##### hive中数据倾斜怎么解决
- 当一个大表和一个小表join时
解决:小表驱动大表,或者用mapjoin将小表加载到内存中,然后对大表进行map;
- 需要join但关联字段由null,这些null会落到同一个节点
解决:1.子查询中过滤掉null的值,id为空的不进行关联
2.用case when给空值分配随即的key值(字符串+rand())
- 不同数据类型关联产生数据倾斜——某日志由字符串商品id,也有string类型的数字商品id,类型不同。关联时把字符串类型的id转为数字型id,
- 当hiveQL中包含count(distinct)时
如果数据量大,执行如select a,count(distinct b) from t group by a;类型的SQL时,会出现数据倾斜的问题。
改为:select a,sum(1) from (select a,b from t group by a,b) group by a;
4. sql求用户留存率
select *,
concat(round(100 * 次日留存用户数/活跃用户数, 2), '%') 次日留存率,
concat(round(100 * 三日留存用户数/活跃用户数, 2), '%') 三日留存率,
concat(round(100 * 七日留存用户数/活跃用户数, 2), '%') 七日留存率
from (
select a.dayno 日期,
count(distinct a.uid) 活跃用户数,
count(distinct b.uid) 次日留存用户数,
count(distinct c.uid) 三日留存用户数,
count(distinct d.uid) 七日留存用户数
from act_user_info a
left join act_user_info b on a.uid = b.uid and b.dayno = a.dayno + 1
left join act_user_info c on a.uid = c.uid and c.dayno = a.dayno + 3
left join act_user_info d on a.uid = d.uid and d.dayno = a.dayno + 7
group by a.dayno
) p;
-
行转列:
hive:explode(split)->case when
spark:pivot
-
实时流join:
明细类型关联:ds1.cogroup(ds2)
聚合类型:ds1.keyBy().intervalJoin(ds2.keyBy())
MR问题
-
MR流程
map阶段:
- 输入文件分片(split),每一片都由一个MapTask处理;
2. Map输出的中间结果会先环形缓冲区里(默认100M),当缓冲区中的内容达到80%时,会将缓冲区里的内容溢写到磁盘上。这时的内容是局部分好区、排好序的。这个阶段结束,生成几个小文件。如果定义了combiner也会进行combiner操作。 - 小文件合并成大文件(分区数量不变)
reduce阶段:
1. 各个ReduceTask从大文件抓取自己分区的数据,存入内存合并,如果数据量大,会排序后写入磁盘; 2. 如果溢写文件多,也会先合并,再进入reduce程序。 3. 经过reduce阶段计算后,存储到指定位置。
-
YARN工作流程
- 客户端向ResourceManager发送job请求,ResourceManager向客户端返回job资源提交路径和jobID
- 客户端将job相关资源提交到指定的共享文件夹下,随后向ResourceManager提交job;
- ResourceManager通过调度器在NodeManager中创建一个容器,并在容器中启用一个MRAppMaster;
- MRAppMaster
- 进程对作业进行初始化,
- 并从共享文件夹获得输入分片信息,
- 为每个分片创建map以及指定数量的reduce对象,
- 从ResourceManager申请资源并启动container,
- 把map和reduce对象发送到NodeManager,并监控任务运行
- 与客户端通信报告任务情况
5. NodeManager从共享文件系统中获取job相关资源,包括jar,配置文件等。 负责资源(容器)分配:调度器(FIFO,容量调度,公平调度器)
- 输入文件分片(split),每一片都由一个MapTask处理;
-
mr优化
1.mr的资源配置
mapred-site.xml文件:
1. mapreduce.map|reduce.memory.mb:一个Map Task可使用的总的资源上限(单位:MB),默认1024。如果Map Task实际使用的资源超过该值,将被强制杀死; 2. mapreduce.map|reduce.cpu.vcores:每个Map task可使用的最多的cpu core数目,默认1;
-
mapreduce.map|reduce.java.opts:Map Task的JVM参数,可以在此设置默认的java heap size参数,如:
-Xmx1024m -verbose:gc -Xloggc:/tmp/@taskid@.gc (@taskid@会被Hadoop框架自动换为相应的taskid),
默认值: ""
4.mapreduce.task.io.sort.factor: mr程序进行合并排序的时候,打开的文件数量,默认为10个.
4. mapreduce.task.io.sort.mb 100 //shuffle的环形缓冲区大小,默认100m
5. mapreduce.map.sort.spill.percent 0.8 //环形缓冲区溢出的阈值,默认80%
6. mapreduce.reduce.shuffle.parallelcopies:mr程序reducer copy数据的线程数,默认5。
7.mapreduce.reduce.shuffle.input.buffer.percent: reduce复制map数据的时候指定的内存堆大小百分比,默认为0.70,适当的增加该值可以减少map数据的磁盘溢出,能够提高系统性能。
8.mapreduce.reduce.shuffle.merge.percent:reduce进行shuffle的时候,用于启动合并输出和磁盘溢写的过程的阀值,默认为0.66。如果允许,适当增大其比例能够减少磁盘溢写次数,提高系统性能。同mapreduce.reduce.shuffle.input.buffer.percent一起使用。
9.combine调优:数据较多时,可以使用combiner在map环节就合并一部分数据。yarn-site.xml配置文件:
1.yarn.scheduler.minimum|maximum-allocation-mb 1024 给应用程序container分配的最小|大内存
2.yarn.scheduler.minimum|maximum-allocation-vcores 1|32
3.yarn.nodemanager.resource.memory-mb 8192 每台NodeManager最大可用内存
6.yarn.nodemanager.resource.cpu-vcores 8 每台NodeManager最大可用cpu核数##### 2.容错相关参数
-
mapreduce.map|reduce.maxattempts: 每个Map|reduce Task最大重试次数,一旦重试参数超过该值,则认为Map|reduce Task运行失败,默认值:4。
2. mapreduce.map|reduce.failures.maxpercent: 当失败的Map Task失败比例超过该值时,整个作业则失败,默认值为0. 如果你的应用程序允许丢弃部分输入数据,则该该值设为一个大于0的值,比如5,表示如果有低于5%的Map Task失败(如果一个MapTask重试次数超过mapreduce.map.maxattempts,则认为这个Map Task失败,其对应的输入数据将不会产生任何结果),整个作业仍认为成功。
3. mapreduce.task.timeout: Task超时时间,经常需要设置的一个参数,该参数表达的意思为:如果一个task在一定时间内没有任何进入,即不会读取新的数据,也没有输出数据,则认为该task处于block状态,可能是卡住了,也许永远会卡主,为了防止因为用户程序永远block住不退出,则强制设置了一个该超时时间(单位毫秒),默认是300000。如果你的程序对每条输入数据的处理时间过长(比如会访问数据库,通过网络拉取数据等),建议将该参数调大,该参数过小常出现的错误提示是“AttemptID:attempt_14267829456721_123456_m_000224_0 Timed out after 300 secsContainer killed by the ApplicationMaster.”。4. ##### hdfs调优:
1. dfs.datanode.failed.volumes.tolerated:允许发生磁盘错误的磁盘数量,默认为0
-
dfs.replication: 复制因子,默认3
- dfs.namenode.handler.count: namenode节点并发线程量,默认10
-
dfs.datanode.handler.count:datanode之间的并发线程量,默认10
5. dfs.datanode.max.transfer.threads:datanode提供的数据流操作的并发线程量,默认4096。
HDFS问题
-
-
读流程
读操作: - hdfs dfs -get /file02 ./file02
- hdfs dfs -copyToLocal /file02 ./file02
- FSDataInputStream fsis = fs.open(path);
fsis.read(byte[] a)
-fs.copyToLocal(path1,path2)
<img src="image.assets/20190117100039726.png" style="zoom:80%;" />
- FSDataInputStream fsis = fs.open(path);
1. 客户端通过调用FileSystem对象的open()方法打开想要的文件。对HDFS来说,这个对象是DistributedFileSystem,它通过远程过程调用(RPC)来访问namenode,以确定文件起始块的位置;
2. namenode返回存有该副本的datanode地址,并根据距离客户端的远近来排序;
3. DistributedFileSystem实例返回一个FSDataInputStream对象(支持文件定位功能)给客户端以便读取数据,接着客户端对这个流调用read方法;
4.FSDataInputStream随即连接距离最近的文件中第一个块所在的DataNode,通过对数据流反复调用read()方法,可以将数据从DataNode传输到客户端
5.当读取到块的末端时,FSInputStream关闭与该DataNode的连接,然后寻找下一个块的最佳DataNode
6.完成后close
2. ##### 写流程
```
写操作:
- hdfs dfs -put ./file02 /file02
- hdfs dfs -copyFromLocal ./file02 /file02
- FSDataOutputStream fsout = fs.create(path);
fsout.write(byte[])
- fs.copyFromLocal(path1,path2)
```
1. 客户端通过对DistributedFileSystem对象调用create()方法来新建文件
2. DistributedFileSystem对namenode创建一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中还没有相应的数据块
3. namenode执行各种不同的检查,以确保这个文件不存在以及客户端有新建该文件的权限。如果检查通过,namenode就会为创建新文件记录一条事务记录(否则,文件创建失败并向客户端抛出一个IOException异常)。DistributedFileSystem向客户端返回一个FSDataOuputStream对象,由此客户端可以开始写入数据,
4. 在客户端写入数据时,FSOutputStream将它分成一个个的数据包(packet),并写入一个内部队列,这个队列称为“数据队列”(data queue)。DataStreamer线程负责处理数据队列,它的责任是挑选出合适存储数据复本的一组datanode,并以此来要求namenode分配新的数据块。这一组datanode将构成一个管道,以默认复本3个为例,所以该管道中有3个节点.DataStreamer将数据包流式传输到管道中第一个datanode,该datanode存储数据包并将它发送到管道中的第2个datanode,同样,第2个datanode存储该数据包并且发送给管道中的第三个datanode。DataStreamer在将一个个packet流式传输到第一个Datanode节点后,还会将此packet从数据队列移动到另一个队列确认队列(ack queue)中。
5. datanode写入数据成功之后,会为ResponseProcessor线程发送一个写入成功的信息回执,当收到管道中所有的datanode确认信息后,ResponseProcessoer线程会将该数据包从确认队列中删除。
SQL问题
1. ##### 优化:
```
1.避免产生笛卡尔积;
2.避免使用*
3.用where代替having:having是在检索出所有记录后才过滤,这个处理需要排序总计等,where可以减少开销
4.用exists、not exists和in、not in相互替代:哪个子查询结果小就用哪个
5.exists代替distinct:
6.避免隐式转换
7.使用索引来避免排序
8.避免在索引列上使用IS NULL或者NOT
9.尽量使用前端匹配的模糊查询
10.不要在选择性较低的字段建立索引
11.避免对列的操作
12.尽量去掉"IN","OR"
Spark调优
三个方向:
- 提高硬件资源利用率;
- 减少网络传输开销
- 提高资源复用率
1. ##### 集群优化
- 数据本地性越好,网络传输越少,计算越快,所以要避免数据跨节点和跨机架传输;
- 存储格式选择orc或parquet(orc>parquet)
相关参数:
spark.sql.hive.convertCTAS默认false
spark.sql.source.default默认parquet
```
- 计算资源:内存和核数比例过大或过小都不合适,内存过大会出现内存瓶颈,小了又会失败;core太小导致并行度小,计算慢,太大会磁盘IO瓶颈(可以处理的数据太多,读写跟不上)
--executor-memory 默认1G
--executor-cores 默认1core
- 并行度:适当增大,可以减少数据倾斜
```
spark.sql.shuffle.partitions 默认200,调成600
```
- offheap内存:主要提供给系统内存和spark内部对象,当offheap太小时作业失败率很高,增大该内存可以提高作业执行效率和通过率
spark.executor.overhead.memory 默认是executor内存的10%
- 大小表join:适当提高广播小表的门槛(将小表广播到每一个executor,来达到降低网络传输开销优化目标)
```
spark.sql.autoBroadcastJoinThreshold 默认10M
```
- 谓词下推:对orc格式的表自动谓词下推,对有些语句能减少数据读取量,加快执行效率
```
spark.sql.orc.filterPushdown 默认false,配合orc格式使用
```
- shuffle过程调优:暂时认为和mr的shuffle一样
```
如果executor内存比较大,比如10G以上,提高
spark.shuffle.file.buffer(默认32k)、
spark.reducer.maxSizeInflight(默认48M)[从map中同时获取输出数据的上限,到内存]
spark.shuffle.memoryFraction会提高效率(默认0.2),
同时对于大作业,提高
spark.shuffle.io.maxRetries
spark.shuffle.io.retryWait能提高大作业稳定运行概率
```
- RDD复用
```
1.避免重复使用RDD;
2.如果有多次使用,尽量用cache
```
- 选择合适的算子
```
1.减少使用shuffle算子
-能避免则避免:reduceBykey、join、distinct、repartition等会进行shuffle的算子
-Broadcast小数据与map数据join,避免shuffle
2.使用高性能算子
-使用reduceByKey代替groupByKey(reduceByKey在map端聚合数据)
-使用mapPartitions代替map(减少重复函数调用的计算开销)
-使用foreachPartition代替foreach(原理同mapPartitions)
-使用treeReduce代替reduce(treeReduce的计算更多的是在executor而不是driver)
-使用filter之后使用coalesce(合并一些分区,减少task启动开销)
-使用repartitionAndSortWithinPartitions代替先repartition再sort操作
-使用broadcast广播大变量,如100M以上
- 数据倾斜:
-
spark中设置检查点的两个算子,有什么区别
-
spark数据倾斜应该如何解决
https://www.cnblogs.com/xiaodf/p/6055803.html#23
1.数据倾斜时发生的现象:绝大多数task都执行得很快,但个别task执行极慢;
2.数据倾斜发生的原理:在进行shuffle时,必须将各节点相同的key拉取到某个节点上的一个task来处理,比如按照key进行聚合或join等操作。此时如果某个key对应的数据量很大,就会发生数据倾斜(如大部分key10多条数据,个别key对应了100万条数据)
3.可能会触发shuffle的算子:distinct、groupBykey、 aggregateByKey、join、cogroup、repartition 等
4.解决:
①使用Hive ETL预处理数据——如果数据在Hive表处就倾斜,可以在Hive中对数据按照key进行聚合,或是预先和其他表join。相当于把shuffule提到hive阶段进行。 ②如果对计算影响不大,可以直接过滤掉导致倾斜的key ③提高shuffle的并行度,reduceByKey(1000),简单缓解 ④两阶段聚合:局部聚合+全局聚合。先给每个key加盐,聚合一次,再去掉前缀,再次聚合。(仅适用于聚合类的shuffle,不能解决join类的shuffle) ⑤采样倾斜key,并分拆join:将要join的两个rdd中倾斜的key都过滤出来,一个加盐,一个膨胀,用加盐的去join膨胀的。 ⑥将reduce join转为map join。将小RDD广播后+map算子来实现join(即map join,就不会shuffle)
-
kafka监控
# 3、sqoop的优化
1、-m于split-by的优化
小量数据时(200M左右):最好使用一个map,快且减少小文件。
大量数据时:要特别考虑数据的特征,对于split-by最完美的情况是有一个:均匀分布的数字(如自增列)或时间字段,且这个字段还有索引(最好字段是int、tinyint),这样在抽取时使得并发的每个sql处理相近的数据量,并且sqoop附加的where条件可以使用索引。
split-by id, -m 2 ,数据量1-100。 第一个mapper:(0,50] 第二个mapper:(50,100]
对于m要综合考虑数据量、IO、源数据库的性能、集群的资源等等。一种简单的考虑是最大不超过yarn上分配给这个用户的core个数,最小 “数据量/m”要够一个128MB的文件。如果条件允许可以先设置一个值跑着试试,然后观察源数据库负载、集群IO以及运行时长等,再进行相应调整。
例子(数据条数/m作为划分边界):
sqoop import --connect jdbc:mysql://hadoop01:3306/test \
--username root \
--password root \
--table u2 \
--driver com.mysql.jdbc.Driver \
--target-dir /user/hive/warehouse/sales_ods.db/sales_order5/dt=2019-12-30 \
--split-by id \
-m 2 \
--fields-terminated-by '\t' \
--null-string '\\N' \
--null-non-string '0'
2、--fetch-size n
一次去mysql中批量读取的数据条数。建议:
1、考虑一条数据的量。(如果2个字段和200个字段的--fetch-size不能一样)
2、考虑数据库的性能
3、考虑网络速度
4、最好的状态是一次--fetch-size能满足一个mapper
```
12.
13.
14.
大数据去重
az重跑前几天数据
数仓中缓慢变化
怎么提交flink作业去运行?
订单的不同阶段怎么处理?
不同项目用一个集群?
项目上线怎么上的?
- 使用DataSet API
RDD特点:
1.JVM对象组成的分布式数据集
2.可处理结构化和非结构化数据
3.函数式转换
RDD局限:
1.从不同的数据源读数据困难;
2.合并多个数据源困难;
3.没有schema类型信息
DataFrame特点:
1.Row对象组成分布式集合
2.可处理结构化数据
3.通过CataLyst自动化程序
4.Data source API
局限:
1.运行时才进行类型检查
2.函数式编程风格
DataSet特点:
1.扩展DataFrame
2.编译时类型检查
3.代码生成编译器,序列化更高效
问题:
kylin、clickhouse怎么用?