Spark总结

                                              Spark总结

  • Spark介绍
  • Spark与MapReduce、storm的区别
  • RDD
  1.  RDD介绍
  2.  RDD五大特性
  3.  RDD创建
  4.  RDD转换和动作
  5.  RDD持久化
  6.  RDD序列化
  7.  RDD共享变量
  • Spark运行机制
  • SparkSQL
  • SparkStreaming

官网:http://spark.apache.org/

一 .Spark介绍

Spark是用于大数据处理的集群计算框架,没有使用MapReduce作为执行引擎,而是使用了自研的分布式运行环境(DAG引擎)在集群上执行工作。Spark可以在YARN上运行,并支持Hadoop文件及HDFS。

Spark最突出的表现在于它能将作业与作业之间产生的大规模的工作数据集存储在内存中,在性能上要优于等效的MapReduce工作流,通常可以高出一个数量级。因为MapReduce的数据集始终需要从磁盘上加载。

二.Spark与MapReduce、Storm的区别

     1.Spark与MapReduce的区别

  • spark把中间计算结果存放在内存中,减少迭代过程中的数据落地,能够实现数据高效共享,迭代运算效率高。mapreduce中的计算中间结果是保存在磁盘上的,这样必然影响整体运行速度。
  • spark容错性高。spark支持DAG图的分布式并行计算(简单介绍以下spark DAG:即有向无环图,描述了任务间的先后依赖关系,spark中RDD经过若干次transform操作,由于transform操作是lazy的,因此,当RDD进行action操作时,RDD间的转换关系也会被提交上去,得到RDD内部的依赖关系,进而根据依赖,划分出不同的stage。),它引进RDD弹性分布式数据集的概念,它是分布在一组节点中的只读对象集合,如果数据集一部分数据丢失,则可以根据血统来对它们进行重建;另外在RDD计算时可以通过checkpoint来实现容错,checkpoint有两种方式,即checkpiont data 和logging the updates。
  • spark更加通用。hadoop只提供了map和reduce两种操作,spark提供的操作类型有很多,大致分为转换和行动操作两大类。转换操作包括:map,filter,flatmap,sample等多种操作,行动操作包括:collect,reduce,lookup和save等操作。

     2.Spark于Storm的区别

对比点

Storm

Spark Streaming

实时计算模型

纯实时,来一条数据,处理一条数据

准实时,对一个时间段内的数据收集起来,作为一个RDD,再处理

实时计算延迟度

毫秒级

秒级

吞吐量

事务机制

支持完善

支持,但不够完善

健壮性 / 容错性

ZooKeeper,Acker,非常强

Checkpoint,WAL,一般

动态调整并行度

支持

不支持

 Spark Streaming与Storm的应用场景

对于Storm来说:
1、建议在那种需要纯实时,不能忍受1秒以上延迟的场景下使用,比如实时金融系统,要求纯实时进行金融交易和分析
2、此外,如果对于实时计算的功能中,要求可靠的事务机制和可靠性机制,即数据的处理完全精准,一条也不能多,一条也不能少,也可以考虑使用Storm
3、如果还需要针对高峰低峰时间段,动态调整实时计算程序的并行度,以最大限度利用集群资源(通常是在小型公司,集群资源紧张的情况),也可以考虑用Storm
4、如果一个大数据应用系统,它就是纯粹的实时计算,不需要在中间执行SQL交互式查询、复杂的transformation算子等,那么用Storm是比较好的选择

对于Spark Streaming来说:
1、如果对上述适用于Storm的三点,一条都不满足的实时场景,即,不要求纯实时,不要求强大可靠的事务机制,不要求动态调整并行度,那么可以考虑使用Spark Streaming
2、考虑使用Spark Streaming最主要的一个因素,应该是针对整个项目进行宏观的考虑,即,如果一个项目除了实时计算之外,还包括了离线批处理、交互式查询等业务功能,而且实时计算中,可能还会牵扯到高延迟批处理、交互式查询等功能,那么就应该首选Spark生态,用Spark Core开发离线批处理,用Spark SQL开发交互式查询,用Spark Streaming开发实时计算,三者可以无缝整合,给系统提供非常高的可扩展性

 Spark Streaming与Storm的优劣分析

事实上,Spark Streaming绝对谈不上比Storm优秀。这两个框架在实时计算领域中,都很优秀,只是擅长的细分场景并不相同。

Spark Streaming仅仅在吞吐量上比Storm要优秀,而吞吐量这一点,也是历来挺Spark Streaming,贬Storm的人着重强调的。但是问题是,是不是在所有的实时计算场景下,都那么注重吞吐量?不尽然。因此,通过吞吐量说Spark Streaming强于Storm,不靠谱。

事实上,Storm在实时延迟度上,比Spark Streaming就好多了,前者是纯实时,后者是准实时。而且,Storm的事务机制、健壮性 / 容错性、动态调整并行度等特性,都要比Spark Streaming更加优秀。

Spark Streaming,有一点是Storm绝对比不上的,就是:它位于Spark生态技术栈中,因此Spark Streaming可以和Spark Core、Spark SQL无缝整合,也就意味着,我们可以对实时处理出来的中间数据,立即在程序中无缝进行延迟批处理、交互式查询等操作。这个特点大大增强了Spark Streaming的优势和功能。

三.RDD

    1.RDD介绍

        RDD是Spark最核心的概念,它是在集群中跨多个机器分区存储的一个只读的对象集合。在典型的Spark程序中,首先要加载一个或多个RDD,作为输入再通过一系列转换得到一组目标RDD,然后对这些目标RDD执行一个动作,如计算出结果或者写入持久存储器。

        “弹性分布式数据集”中的“弹性”指的是Spark可以通过重新安排计算来自动重建丢失的分区。

        加载RDD或者执行转换不会立即触发任何数据处理操作,只是重建了一个计算的计划。只有当对RDD执行某个动作时,才会出发真正的计算。

    2.RDD五大特性

        1).A list of partitions

         RDD是一个由多个partition(某个节点里的某一片连续的数据)组成的的list;将数据加载为RDD时,一般会遵循数据的本地性(一般一个hdfs里的block会加载为一个partition)。

        2).A function for computing each split

        一个函数计算每一个分片,RDD的每个partition上面都会有function,也就是函数应用,其作用是实现RDD之间partition的转换。

        3).A list of dependencies on other RDDs

        RDD会记录它的依赖 ,依赖还具体分为宽依赖和窄依赖,但并不是所有的RDD都有依赖。为了容错(重算,cache,checkpoint),也就是说在内存中的RDD操作时出错或丢失会进行重算。

        4).Optionally,a Partitioner for Key-value RDDs

        可选项,如果RDD里面存的数据是key-value形式,则可以传递一个自定义的Partitioner进行重新分区,例如这里自定义的Partitioner是基于key进行分区,那则会将不同RDD里面的相同key的数据放到同一个partition里面

        5).Optionally, a list of preferred locations to compute each split on

        最优的位置去计算,也就是数据的本地性。

    3.RDD创建

  • 并行化一个集合(内存中的对象集合):该方法适用于对少量的输入数据进行并行的CPU密集型计算
  • 使用外部存储器(如:HDFS)中的数据集:创建一个对外部数据集的引用,如:为文本文件创建一个String对象的RDD

            Spark内部使用MapReduce API的TextInputFormat来读取文件,其文件分割行为与Hadoop一致,因此在使用HDFS的情况下,每个HDFS块对应于一个Spark分区。

  • 对现有的RDD进行转换。

    4.RDD转换和动作

        Spark为RDD提供了两大类操作:转换(transformation)和动作(action)。转换时从现有的RDD生成新的RDD,而动作则触发对RDD的计算并对计算结果执行某种操作,返回给用户或保存在外部存储器(计算在内存中进行)。动作时立即性的,而转换则是惰性的,因为在对RDD执行一个动作之前都不会为该RDD的任何转换操作采取实际行动。

        判断一个操作是转换还是动作:观察其返回类型,如果返回的类型是RDD,则是一个转换操作,否则是一个动作。

Spark API文档:https://spark.apache.org/docs/2.2.0/api/java/index.html

        聚合转换:按键为键值对RDD进行聚合操作的三个主要转换函数是:reduceByKey()、foldByKey()、aggregateByKey()

    5.RDD持久化

        使用场景:

             同一个RDD被多次使用,为了防止重新加载RDD,可以使用RDD持久化策略,每次你对一个RDD执行一个算子操作时,都会重新从源头处计算一遍,计算出那个RDD来,然后再对这个RDD执行你的算子操作。这种方式的性能是很差的。

       持久化级别

           

持久化级别

含义解释

MEMORY_ONLY

使用未序列化的Java对象格式,将数据保存在内存中,如果内存不够存放所有的数据,则数据可能就不会进行持久化。那么下次对这个RDD执行算子操作时,那些没有被持久化的数据,需要从源头处重新计算一遍。这是默认的持久化策略,使用cache()方法时。使用的就是这种持久化策略。

MEMORY_AND_DISK

使用未序列化的Java对象格式,优先尝试将数据保存在内存中。如果内存不够存放所有的数据,会将数据写入磁盘文件中,下次对这个RDD执行算子时,持久化在磁盘文件中的数据会被读取出来使用。

MEMORY_ONLY_SER

基本含义同MEMORY_ONLY,唯一的区别在于,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。这种方式更加节省内存,从而避免持久化的数据占用过多内存导致频繁GC

MEMORY_AND_DISK_SER

基本含义同MEMORY_AND_DISK,唯一的区别在于会将RDD先进行序列化,,RDD的每个partition会被序列化成一个字节数组。这样更节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC

DISK_ONLY

使用未序列化的Java对象格式,将数据全部写到磁盘文件中。

MEMORY_ONLY_2,MEMORY_AND_DISK_2

对于上述任意一种持久化策略,如果加上后缀_2,代表的是将每个持久化的数据,都复制一份副本,并将副本保存到其他节点上。这种基于副本的持久化机制主要用于进行容错。假如某个节点挂掉,节点的内存或磁盘中的持久化数据丢失了,那么后续对RDD计算时还可以使用该数据在其他节点上的副本。如果没有副本的话,就只能将这些数据从源头处重新计算一遍了。

    6.RDD序列化

        在使用Spark时,要从两方面来考虑序列化:数据序列化和函数序列化(闭包函数)

        默认情况下,Spark在通过网络将数据从一个executor发送到另一个executor时,或者以序列化的形式缓存(持久化)数据时,使用的是Java序列化机制:类实现java.io.Serializable或者java.io.Externalizable接口,该机制性能、效率不高。

       使用Kryo序列化机制是更好的选择,Kryo是一个高效的Java序列化库,在驱动程序的SparkConf中设置spark.serializer属性即可使用Kryo

conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")

       在Kryo中注册类:创建一个KryoRegistrator子类,然后重写registerClasses()方法,可提升序列化性能。

       函数序列化:通常函数序列化会使用默认的Java序列化机制。

    7.RDD共享变量

        共享变量分为广播变量和累加器变量两种。

       广播变量

广播变量(broadcast variable)在经过序列化后被发送到各个executor,然后缓存在那里,以便后期任务可以在需要时访问。相当于MapReduce中的分布式缓存,区别在于Spark将数据保存在内存中,只有在内存耗尽时才会溢出到磁盘上。创建一个广播变量:通过SparkContext的broadcast()方法传递。广播变量是单向传播的。

       累加器

累加器(accumulator)是在任务中只能对它做加法的共享变量,类似于MapReduce中的累加器,当作业完成后,driver程序可以检索累加器的最终值。通过SparkContext的accumulator()方法来创建一个累加器变量。

    8.RDD窄依赖与宽依赖

        窄依赖:一个父RDD只会把数据传输给一个子RDD

        宽依赖:一个父RDD会把数据传输给多个子RDD

四.Spark运行机制

    driver:负责托管应用(SparkContext)并为作业调度任务。通常作为一个不由集群管理器(cluster manager)管理的客户端来运行。

    executor:专属于应用,在应用运行期运行,并执行该应用的任务,一般运行在集群的计算机上。

    作业提交

    

    

  • 当对RDD执行一个动作(比如count())时,会自动提交一个Spark作业,导致内部的SprakContext调用runJob()。步骤1。

  • 然后将调用传递给作为driver的一部分运行的调度程序。步骤2。调度程序由两部分组成:DAG调度程序和任务调度程序。其中DAG调度程序把作业分解为若干阶段(stage),并由这些阶段构成一个DAG。任务调度程序则负责把每个阶段中的任务提交到集群中。

  • 当DAG调度程序已构建一个完整的多阶段DAG,它就将每个阶段的任务集合提交给任务调度程序。步骤3

  • 当任务集合被发送到任务调度程序后,任务调度程序开始为executor分配任务,分配的任务通过调度程序后端启动。步骤4

  • 调度程序后端向executor发送远程启动任务的消息。步骤5.

  • executor接收到消息通知后开始运行任务。步骤6

    运行在YARN上的Spark

        为了在YARN上运行,Spark提供了两种部署方式:YARN客户端模式和YARN集群模式。YRAN客户端模式的driver在客户端运行,而YARN集群模式的driver在YARN的application master集群上运行。

        1.YARN客户端模式:

        

  • 在YARN客户端模式下,当driver构建新的SparkContext实例时,便启动了与YARN之间的交互。步骤1

  • SparkContext向YARN资源管理器提交一个YARN应用。步骤2

  • YARN资源管理器则启动集群节点管理器上的YARN容器,并为在其中运行一个SparkExecutorLauncher的application master。步骤3

  • ExecutorLauncher向资源管理器请求资源。步骤4

  • 启动ExecutorBackend进程作为分配给它的容器。步骤5

        每个executor在启动时会连接回SparkContext,注册自身。即向SparkContext提供了关于可用于运行任务的executor的数量及其位置信息。这些信息被用在任务的位置偏好策略中。启动的executor的数量在saprk-shell、spark-submit或py-spark中设置。executor使用的内核数默认为1个,内存默认2014MB

        2.YARN集群模式:

        

        启动流程与客户端模式基本一致,仅是在步骤1,sprak-submit不会允许任何用户代码在集群上运行。

 

五.SparkSQL

 

     

     Spark SQL是spark套件中一个模板,它将数据的计算任务通过SQL的形式转换成了RDD的计算,类似于Hive通过SQL的形式将数据的计算任务转换成了MapReduce。

     Spark SQL的特点
         1、和Spark Core的无缝集成,可以在写整个RDD应用的时候,配置Spark SQL来完成逻辑实现。
         2、统一的数据访问方式,Spark SQL提供标准化的SQL查询。
        3、Hive的继承,Spark SQL通过内嵌的hive或者连接外部已经部署好的hive案例,实现了对hive语法的继承和操作。
        4、标准化的连接方式,Spark SQL可以通过启动thrift Server来支持JDBC、ODBC的访问,将自己作为一个BI Server使用

    Spark SQL数据抽象
        1、RDD(Spark1.0)->DataFrame(Spark1.3)->DataSet(Spark1.6)
        2、Spark SQL提供了DataFrame和DataSet的数据抽象
        3、DataFrame就是RDD+Schema,可以认为是一张二维表格,劣势在于编译器不进行表格中的字段的类型检查,在运行期进行检查
        4、DataSet是Spark最新的数据抽象,Spark的发展会逐步将DataSet作为主要的数据抽象,弱化RDD和DataFrame.DataSet包含了DataFrame所有的优化机制。除此之外提供了一样例类为Schema模型的强类型
        5、DataFrame=DataSet[Row]
        6、DataFrame和DataSet都有可控的内存管理机制,所有数据都保存在非堆上,都使用了catalyst进行SQL的优化。

    Spark SQL客户端查询:
        1、可以通过Spark-shell来操作Spark SQL,ss作为SparkSession的变量名,sc作为SparkContext的变量名
        2、可以通过Spark提供的方法读取json文件,将json文件转换成DataFrame
        3、可以通过DataFrame提供的API来操作DataFrame里面的数据。
        4、可以通过将DataFrame注册成为一个临时表的方式,来通过Spark.sql方法运行标准的SQL语句来查询。

 

六.SparkStreaming

 

    

    Spark Streaming是Spark coreAPI的扩展,支持实时数据流的处理,并且具有可扩展性,高吞吐量,容错的特点。数据可以从许多来源获取,如Kafka,Flume等并且可以使用复杂的算法进行处理,这些算法使用诸如map,reduce,join和window等高级函数表示。最后,处理后的数据可以推送到文件系统中、数据库等。

    总体来说,从三点进行考虑:输入-->计算-->输出

    处理数据的特点

        接收实时输入的数据流,然后将数据差分成多个batch,比如每一秒的数据封装成一个batch,然后交给每个batch的计算的计算引擎进行处理。最后产生一个结果数据流,其中的数据,也是由一个一个的batch组成。

    Spark Streaming DSreeam

         1.Spark Streaming提供了一种高级的抽象,叫做DStream,英文全称为Discretized Stream,中文反映称为“离散流”。它代表了一个持续不断的数据流。DStream可以通过不断输入的数据源来创建,比如Kafka、Flume、ZMQ和Kinesis;也可以通过对其他DStream应用高阶函数来创建,比如map、reduce、join、window。

        2.DStream的内部,其实就是一系列持续不断产生的RDD。RDD是Spark Core的核心抽象,即不可变的,分布式的数据集。DStream中的每个RDD都包含了一个时间段内的数据。

        

        3.对DStream应用的算子,比如map,其实在底层会被翻译为对DStream中每个RDD的操作。比如对一个DStream执行一个map操作,会产生一个新的DStream。但是,在底层,其实其原理为,对输入DStream中每个时间段的RDD,都应用一遍map操作,然后生成的新的RDD,即作为新的DStream中的那个时间段的一个RDD。底层的RDD的transformation操作。还是由Spark Core的计算引擎来实现的。Spark Streaming对Spark Core进行了一层封装,隐藏了细节,然后对开发人员提供了方便易用的高层次的API。

        

 

七.Spark遇到数据倾斜如何处理

    产生原因

        在进行shuffle的时候,必须将各个节点上相同的key拉取到某个节点上的一个task来进行处理,比如按照key进行聚合或join等操作。此时如果某个key对应的数据量特别大的话,就会发生数据倾斜。比如大部分key对应10条数据,但是个别key却对应了100万条数据,那么大部分task可能就只会分配到10条数据,然后1秒钟就运行完了;但是个别task可能分配到了100万数据,要运行一两个小时。因此,整个Spark作业的运行进度是由运行时间最长的那个task决定的。因此出现数据倾斜的时候,Spark作业看起来会运行得非常缓慢,甚至可能因为某个task处理的数据量过大导致内存溢出。

    调优方案

       1.提前处理数据

           1).把有可能产生的数据倾斜的数据,先通过SQL来实现

           2).把有可能产生数据的数据通过SQL给排除掉

       2.优化Spark程序

           对包含少数几个数据量过大的key的那个RDD,通过sample算子采样出一份样本来,然后统计一下每个key的数量,计算出来数据量最大的是哪几个key。 然后将这几个key对应的数据从原来的RDD中拆分出来,形成一个单独的RDD,并给每个key都打上n以内的随机数作为前缀,而不会导致倾斜的大部分key形成另外一个RDD。 接着将需要join的另一个RDD,也过滤出来那几个倾斜key对应的数据并形成一个单独的RDD,将每条数据膨胀成n条数据,这n条数据都按顺序附加一个0~n的前缀,不会导致倾斜的大部分key也形成另外一个RDD。 再将附加了随机前缀的独立RDD与另一个膨胀n倍的独立RDD进行join,此时就可以将原先相同的key打散成n份,分散到多个task中去进行join了。 而另外两个普通的RDD就照常join即可。 最后将两次join的结果使用union算子合并起来即可,就是最终的join结果。

 

八.Spark参数调优

spark.shuffle.file.buffer

  • 默认值:32k
  • 参数说明:该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘。
  • 调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如64k),从而减少shuffle write过程中溢写磁盘文件的次数,也就可以减少磁盘IO次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升。

spark.reducer.maxSizeInFlight

  • 默认值:48m
  • 参数说明:该参数用于设置shuffle read task的buffer缓冲大小,而这个buffer缓冲决定了每次能够拉取多少数据。
  • 调优建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如96m),从而减少拉取数据的次数,也就可以减少网络传输的次数,进而提升性能。在实践中发现,合理调节该参数,性能会有1%~5%的提升。

spark.shuffle.io.maxRetries

  • 默认值:3
  • 参数说明:shuffle read task从shuffle write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。如果在指定次数之内拉取还是没有成功,就可能会导致作业执行失败。
  • 调优建议:对于那些包含了特别耗时的shuffle操作的作业,建议增加重试最大次数(比如60次),以避免由于JVM的full gc或者网络不稳定等因素导致的数据拉取失败。在实践中发现,对于针对超大数据量(数十亿~上百亿)的shuffle过程,调节该参数可以大幅度提升稳定性。

spark.shuffle.io.retryWait

  • 默认值:5s
  • 参数说明:具体解释同上,该参数代表了每次重试拉取数据的等待间隔,默认是5s。
  • 调优建议:建议加大间隔时长(比如60s),以增加shuffle操作的稳定性。

spark.shuffle.memoryFraction

  • 默认值:0.2
  • 参数说明:该参数代表了Executor内存中,分配给shuffle read task进行聚合操作的内存比例,默认是20%。
  • 调优建议:在资源参数调优中讲解过这个参数。如果内存充足,而且很少使用持久化操作,建议调高这个比例,给shuffle read的聚合操作更多内存,以避免由于内存不足导致聚合过程中频繁读写磁盘。在实践中发现,合理调节该参数可以将性能提升10%左右。

spark.shuffle.manager

  • 默认值:sort
  • 参数说明:该参数用于设置ShuffleManager的类型。Spark 1.5以后,有三个可选项:hash、sort和tungsten-sort。HashShuffleManager是Spark 1.2以前的默认选项,但是Spark 1.2以及之后的版本默认都是SortShuffleManager了。tungsten-sort与sort类似,但是使用了tungsten计划中的堆外内存管理机制,内存使用效率更高。
  • 调优建议:由于SortShuffleManager默认会对数据进行排序,因此如果你的业务逻辑中需要该排序机制的话,则使用默认的SortShuffleManager就可以;而如果你的业务逻辑不需要对数据进行排序,那么建议参考后面的几个参数调优,通过bypass机制或优化的HashShuffleManager来避免排序操作,同时提供较好的磁盘读写性能。这里要注意的是,tungsten-sort要慎用,因为之前发现了一些相应的bug。

spark.shuffle.sort.bypassMergeThreshold

  • 默认值:200
  • 参数说明:当ShuffleManager为SortShuffleManager时,如果shuffle read task的数量小于这个阈值(默认是200),则shuffle write过程中不会进行排序操作,而是直接按照未经优化的HashShuffleManager的方式去写数据,但是最后会将每个task产生的所有临时磁盘文件都合并成一个文件,并会创建单独的索引文件。
  • 调优建议:当你使用SortShuffleManager时,如果的确不需要排序操作,那么建议将这个参数调大一些,大于shuffle read task的数量。那么此时就会自动启用bypass机制,map-side就不会进行排序了,减少了排序的性能开销。但是这种方式下,依然会产生大量的磁盘文件,因此shuffle write性能有待提高。

spark.shuffle.consolidateFiles

  • 默认值:false
  • 参数说明:如果使用HashShuffleManager,该参数有效。如果设置为true,那么就会开启consolidate机制,会大幅度合并shuffle write的输出文件,对于shuffle read task数量特别多的情况下,这种方法可以极大地减少磁盘IO开销,提升性能。
  • 调优建议:如果的确不需要SortShuffleManager的排序机制,那么除了使用bypass机制,还可以尝试将spark.shffle.manager参数手动指定为hash,使用HashShuffleManager,同时开启consolidate机制。在实践中尝试过,发现其性能比开启了bypass机制的SortShuffleManager要高出10%~30%。

本文分别讲解了开发过程中的优化原则、运行前的资源参数设置调优、运行中的数据倾斜的解决方案、为了精益求精的shuffle调优。希望大家能够在阅读本文之后,记住这些性能调优的原则以及方案,在Spark作业开发、测试以及运行的过程中多尝试,只有这样,我们才能开发出更优的Spark作业,不断提升其性能。

参考文献:https://www.jianshu.com/p/cf8f39fd7a5c(简书Spark总结篇)

                 https://tech.meituan.com/2016/05/12/spark-tuning-pro.html(美团Spark优化高级篇)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值