sqoopFIlnk

Sqoop 问题

sqoop是用来将数据在Hadoop和关系型数据库服务器之间传送的工具。

导入:mysql、oracle导入数据导hadoop的hdfs、hive、hbase等数据存储系统;

导出:从hadoop文件系统导出到关系型数据库

工作机制|原理:将导出或导入命令翻译成mapreduce程序来实现,使用代码生成工具可以查看sqoop生成的java代码,并可在此基础上进行深度定制开发。

  1. sqoop使用时有没有遇到什么问题?

    Hive中的null在底层是以“/N”来存储的,而mysql中的null在底层就是null,为了保证数据两端一致性,在导入导出数据时需要用参数来调整为一致,导出时加上参数--input-null-string,和--input-null-non-string,导入时--null-string和--null-non-string
    
  2. sqoop导出数据一致性问题:4个map任务,执行过程中,有2个任务失败,此时mysql中已经存储了另外2个map任务的数据,怎么办?

    加上参数设置--staging-table可以解决一致性问题,它指定了登台表来解决,该选项充当用于暂存导出数据的辅助表,分阶段数据最终在单个事务中移动到目标表。
    
  3. Sqoop底层运行的任务特点是:只有Map阶段,没有Reduce阶段的任务。

Flink优化问题

  1. 优化:

    1. Operator Chain:Flink会尽可能地将operator的subtask链接在一起(chain)形成task,这样可以优化很多性能。能减少线程之间的切换,减少消息的序列化/反序列化,减少数据在缓冲区的交换,最终减少延迟同时提高整体吞吐量。Chain的规则是:没有shuffle过程、上下游并行度一样、没有禁用chain……

    2. 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资源。

    3. Flink异步IO

      流式计算中,常常需要与外部系统进行交互,而往往一次连接中你那个获取连接等待通信的耗时会找比较高。下图是两种方式对比示例:

      img

      异步IO使网络等待时间极大地缩短,增加了吞吐、降低了延迟。

    4. 强大地CheckPoint机制

      作用:保证Exactly Once级别地数据恢复

      • 快照的存储引擎:hdfs、rocksdb、memory,做快照的数据大小较大时,考虑采用Rocksdb来作为存储
      • 资源设置:我们都知道checkpoint机制是在每个task上进行, 那么当总的状态数据大小不变的情况下,如何分配减少单个 task 所分的 checkpoint 数据变成了提升 checkpoint 执行效率的关键。
      • 增量快照。非增量快照下,每次 checkpoint 都包含了作业所有状态数据。而大部分场景下,前后 checkpoint 里,数据发生变更的部分相对很少,所以设置增量 checkpoint,仅会对上次 checkpoint 和本次 checkpoint 之间状态的差异进行存储计算,减少了 checkpoint 的耗时。
  2. 自己可以优化的地方:

    • 使用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其他问题

  1. Flink作业提交及各组件工作过程?
    standalone模式:img
    • 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交换数据。

    image-20200505154636207

    YARN模式

    image-20200505153147331

  • client上传Flink的Jar包和配置到HDFS中
  • client提交作业给ResourceManager,ResourceManager在一个nodeManager中启动ApplicationMaster,里面包含的是JobManager,JobManager加载Flink的Jar并配置构建环境;
  • ApplicationMaster向ResouceManager申请资源,并根据反馈启动TaskManager,TaskManager加载Flinkjar包等资源,并执行任务
  1. Flink时间类型有哪些,他们有什么区别?

    img

  • 事件时间(Event Time):事件时间是每个独立事件在设备上产生的时间,在进入Flink之前已经有了。
  • 接入时间( Ingestion Time):数据进入Flink系统的时间,这个时间依赖于Source Operator所在的主机系统时钟。
  • 处理时间(Processing Time):处理时间是指数据在操作算子计算过程中获取到的所在主机。优点:性能较高,延迟较低,缺点:不能处理乱序问题,尤其是每台机器时钟本身不同步,而数据不乱序,也会导致处理时数据乱序。
  1. 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());
      
  2. Flink里的状态?
    1. 状态:由一个任务维护,用来计算某个结果的所有数据,都属于这个任务的状态,可以认为是一个本地变量。

    2. 分两大类:OperatorState和KeyedState,使用最多的ValueState、ListState、MapState

    OperatorState(不常用):

    • 列表状态:将状态表示为一组数据的列表
    • 联合列表状态:也是一种数据列表,但发生故障或从保存点启动应用程序时如何恢复不同
    • 广播状态:如果一个算子有多项任务,而每项任务状态又都相同,那广播状态最适合

    KeyedState:

    • 值状态:将单个值维护成状态
    • 列表状态:将为一组数据的列表维护成状态
    • 映射状态(Map state):将一组key-value对维护成状态
    • 聚合状态(Reducing state & Aggregating State):将用于聚合操作的列表维护成状态
    1. 使用:键控状态的使用,在RichFunction处理函数中:
    image-20200505173119614

    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) )
        })
    
    1. 过期时间:类似于redis

      import org.apache.flink.api.common.state.StateTtlConfig;
      StateTtlConfig ttlConfig = StateTtlConfig
          .newBuilder(Time.seconds(1))
          .disableCleanupInBackground()
          .build();
      
    2. 状态后端(State Backends)

    定义:状态的存储、访问及维护,由一个可插入的组件决定的,这个组件就叫做状态后端

    MemoryStateBackend、FsStateBackend、RocksDBStateBackend

    env.enableCheckpointing(60000) //启动快照机制

    flink-statebackend-rocksdb_2.11

  3. 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)
    

    总结来说,

    1. 窗口的作用是为了周期性地获取数据;
    2. watermark是为了一定程度防止数据乱序(经常);
    3. allowLateness是将窗口关闭时间再延迟一段时间。AllowedLateness会在水位机制的基础上,再增加一个时间(这段时间内,第二个窗口已经打开),这段时间限制内的延迟数据依然可以改变窗口一的结果状态。如果这段时间之后还有延迟数据,则直接放在测流输出。
    4. sideOutPut是最后兜底操作,所有过期延迟数据,指定窗口已经彻底关闭了,就会把数据放到侧输出流
  4. Flink中异常数据如何修复
    1. 涉及到state,可以通过Queryable State来查询和修改state
    2. 通过指定历史时间戳回滚历史数据(删除或覆盖)
    3. 任务异常挂掉,可以通过checkpoint启动任务
  5. 流处理和批处理的区别,流处理的难点在哪里?

    不同:

    1. 批处理的数据源是固定存在的,如果作业失败,可以容易地重新运行作 失败部分来重新计算丢失的结果。但流处理却无法做到,因为数据是无穷尽,没有开始也没有结束。
    2. 批处理只有输出,而流处理还需要备份状态。

    流处理难点:

    1. 时间窗口的选择
    2. 容错机制
    3. 精确一次语义保证
  6. checkpoint流程

    checkpoint机制(一致性检查点):是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如异常退出)出现故障,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性。为应用程序提供精确一次的一致性。

    特性:保存某个时间点所有任务的状态(快照),这个时间点应该是所有任务都处理完一个相同输入数据的时候。

    image-20200505230806765

    说明:上图即为“5”这条数据所有环节都处理完之后的状态保存情况。在下一个保存点前,有数据计算出错,第一步就是重启应用,第二步是从checlpoint中读取状态,将读到的状态恢复到对应的位置,第三步就可以开始消费并处理检查点到故障之间的所有数据。如下:

    读到7时出错:image-20200505231529498

    重启:image-20200505231601592

恢复到上一个检查点:image-20200505231627229

注意,5是指的偏移量,状态重置包括source部分的偏移量重置,于是会从数据源偏移量为5那里那里重新获取数据。这样肯定要求数据源可以重置offset。

  1. checkpoint底层算法

    基于Chandy-Lamport算法的分布式快照,将检查点的保存和数据处理分离开,不暂停整个应用。

    检查点分界线(Checkpoint Barrier):是一种标记在数据流间的某些点的记号,把数据流分成了不同段,每个标记都在告诉算子收到本标记后,就将当前状态做成新的快照存入backend。

    1. 假设现在是一个有两个输入流的应用程序,用并行的两个Source任务来读取
    image-20200505235813082
    1. JobManager会向每个Source任务发送一条带有新检查点ID的消息,通过这种方式来启动检查点

      image-20200505235939044
    2. 首先,数据源将他们的offset状态写入检查点,并像下游广播一个检查点边界barrier

      状态后端在状态存入检查点后,会返回通知给source任务,source任务就会向JobManager确认检查点完成

      image-20200506000339039
    3. barrier向下游传递,sum任务会等待所有输入分区的barrier(蓝黄两个barrier)到达;

      算子上如果某个分区的barrier已经到达,那该分区继续到达的数据将会被缓存(如下图蓝4)直到所有分区的barrier全部到达;

      算子上如果某个分区的barrier尚未到达,数据将会被正常处理(如下图黄4在黄barrier2前面,所以不停顿直接被处理,结果为蓝2+黄2+黄4,为8)。

      image-20200506001344584
    4. 当收到所有输入分区的barrier时,任务就将其状态保存到状态后端的检查点中,然后将barrier继续向下游发送。

      image-20200506001658069
    5. 向下游转发检查点barrier后,任务继续正常处理image-20200506001814849

    6. sink任务向JobManager确认状态保存到checkpoint完毕

      image-20200506002009173

      当所有任务都确认已成功将状态保存到检查点时,检查点就是真正完成了。

  2. Savepoints:保存点

    原理checkpoint完全一样,只是多了一些元数据。但是savepoints不会自动执行,而是需要用户明确地触发创建操作,恢复时也需要指定从哪个savepoints恢复。

    保存点的强大功能:除了故障恢复以外,还可以有计划地手动备份,更新应用程序,版本迁移,暂停和重启应用。

  3. Flink的状态一致性分类

    at-most-once:最多一次

    at-least-once:至少一次

    exactly-once:精确一次

    首先在Flink内部,采用checkpoint机制可以保证精确一次语义。而端到端(end-to-end)的一致性保证,意味着结果的正确性贯穿整个流处理应用始终。

    端到端exactly-once:

    • 内部保证:checkpoint

    • source端:可重设数据的读取位置(偏移量),对kafka而言,只需要手动提交偏移量

    • sink端:从故障恢复时,数据不会重复写入外部系统

      • 幂等写入(HashMap写入相同k-v就是幂等操作)

      • 事务写入:

        1. 构建思想:构建的事务对应着checkpoint,等到checkpoint真正完成的时候才把所有对应的结果写入sink系统中;

        2. 实现方式:

          预写日志(Write-Ahead-Log):把结果数据先当成状态保存,然后在JobManager收到checkpoint完成的通知时,一次性写入sink系统;模板类:GenericWriteAheadSink可以实现这种事务性sink。简单粗暴,相当于缓存后批处理。

          两阶段提交(Two-Phase-Commit,2PC):对于每个checkpoint,sink任务会启动一个事务,并将接下来所有接收到的数据添加到事务里。然后将这些数据写入外部sink系统,但不提交它们,只是预提交,等checkpoint完成时,才正式提交事务,实现结果真正写入。真正实现exactly-once这种方式需要一个支持事务的外部sink系统。Flink提供了TwoPhaseCommitSinkFunction接口。

    1. flink的水位机制
    2. flink的并行度设置
    3. flink窗口

      flink双流join

      flink反压,怎么发现,怎么解决

    Hive问题

    1. hive工作原理:
    1.用户提交查询等任务给driver
    2.驱动器检查语法和生成查询计划,将HQL发送给编译器
3.①编译器compiler根据用户去元数据库获取需要的元数据信息,②得到元数据信息后对任务进行编译,将逻辑查询转为物理查询,③并提交给Driver,编译完成;
    4.Driver将查询计划提交给ExecutionEngine去执行;
    5.内部是job执行过程,查询执行是MapReduce工作;
    6.执行引擎接受来自数据节点的结果,返给驱动程序,驱动程序返给hive接口。
2. 调优:

   1. join:小表join大表
  1. 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
    
    1. 本地模式
    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;

  1. 行转列:

    hive:explode(split)->case when
    

    spark:pivot

  2. 实时流join:

    明细类型关联:ds1.cogroup(ds2)

    聚合类型:ds1.keyBy().intervalJoin(ds2.keyBy())
    

    MR问题

  3. MR流程 img
    map阶段:
    1. 输入文件分片(split),每一片都由一个MapTask处理;
      2. Map输出的中间结果会先环形缓冲区里(默认100M),当缓冲区中的内容达到80%时,会将缓冲区里的内容溢写到磁盘上。这时的内容是局部分好区、排好序的。这个阶段结束,生成几个小文件。如果定义了combiner也会进行combiner操作。
    2. 小文件合并成大文件(分区数量不变)
    reduce阶段:
    1. 各个ReduceTask从大文件抓取自己分区的数据,存入内存合并,如果数据量大,会排序后写入磁盘;
    2. 如果溢写文件多,也会先合并,再进入reduce程序。
    3. 经过reduce阶段计算后,存储到指定位置。
    
    1. YARN工作流程
      1. 客户端向ResourceManager发送job请求,ResourceManager向客户端返回job资源提交路径和jobID
      2. 客户端将job相关资源提交到指定的共享文件夹下,随后向ResourceManager提交job;
      3. ResourceManager通过调度器在NodeManager中创建一个容器,并在容器中启用一个MRAppMaster;
      4. MRAppMaster
        • 进程对作业进行初始化,
        • 并从共享文件夹获得输入分片信息,
    • 为每个分片创建map以及指定数量的reduce对象,
      • 从ResourceManager申请资源并启动container,
    • 把map和reduce对象发送到NodeManager,并监控任务运行
      • 与客户端通信报告任务情况
    5. NodeManager从共享文件系统中获取job相关资源,包括jar,配置文件等。
     
    负责资源(容器)分配:调度器(FIFO,容量调度,公平调度器)
    
  4. 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;
    
  5. 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.容错相关参数
    
    
  6. 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
    
    1. dfs.replication: 复制因子,默认3

      1. dfs.namenode.handler.count: namenode节点并发线程量,默认10
    2. dfs.datanode.handler.count:datanode之间的并发线程量,默认10
      5. dfs.datanode.max.transfer.threads:datanode提供的数据流操作的并发线程量,默认4096。

      
      

    HDFS问题

  7. 读流程
      读操作:
    - 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%;" />
    
    
    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以上

  • 数据倾斜:
  1. spark中设置检查点的两个算子,有什么区别
  2. 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)
    
  3. 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怎么用?







posted @ 2023-04-03 10:50  edclol  阅读(40)  评论(0编辑  收藏  举报