Spark-手记-3


通过转化操作,你从已有的RDD中派生出新的RDD,
Spark会使用RDD Lineage(谱系图)来记录这些不同RDD之间的依赖关系。
Spark需要用这些信息来按需计算每个RDD,
也可以依赖谱系图在持久化的RDD丢失部分数据时恢复所丢失的数据。

行动操作,它们会把最终得到的结果返回到驱动器程序,或者写入外部存储系统中。
需要注意的是,每当调用一个新的行动操作时,整个RDD都会从头开始计算,
要避免这种低效的行为,用户可以将中间结果持久化(缓存)。


惰性求值
RDD的转化操作都是惰性求值的。
这意味着在被调用行动操作之前Spark不会开始计算。
当我们对RDD调用转化操作(例如:map)时,操作不会立即执行,
Spark会在内部记录下来所要求执行的操作的相关信息。

Spark使用惰性求值,这样就可以把操作合并到一起来减少计算数据的步骤。


如果要缓存的数据太多,内存放不下,
Spark会自动利用LRU(最近最少使用)的缓存策略把最老的分区从内存中移除。
对于仅把数据放在内存中的缓存级别,下一次要用到已经移除的分区时,这些分区就需要重新计算。
但是对于使用内存和磁盘(MEMORY_AND_DISK)的缓存级别的分区来说,被移除的分区都会写入磁盘。
不论哪一种情况,都不必担心你的作业因为缓存太多数据而被打断。
不过,缓存不必要的数据会导致有用的数据被移出内存,带来更多重新计算的时间开销。
最后,RDD还有一个方法叫作 unpersits(),调用该方法可以手动把持久化的RDD从缓存中移除。


分区
有时,使用可控的分区方式把常被一起访问的数据放到同一个节点,
可以大大减少应用的通信开销。


Spark对数据集在节点间的分区进行控制。
在分布式程序中,通信的代价是很大的,因此控制数据分区以获得最少的网络传输可以极大地提升整体性能。


文件压缩
在大数据工作中,我们经常需要对数据进行压缩以节省存储空间和网络传输的开销。
Spark原生的输入方式可以自动处理一些类型的压缩。
在读取压缩后的数据时,一些压缩编解码器可以推测压缩类型。

对于像Spark这样的分布式系统,我们通常会尝试从多个不同机器上一起读入数据。
要实现这种情况,每个工作节点都必须能够找到一条新记录的开端。
有些压缩格式会使得这变得不可能,而必须要单个节点来读入所有数据,这就很容易产生性能瓶颈。
可以很容易地从多个节点上并行读取的格式被称为"可分割"的格式(bzip2、lzo)。


广播变量
广播变量是Spark的一种共享变量类型。
它可以让程序高效地向所有工作节点发送一个较大的只读值,以供一个或多个Spark操作使用。

 

$SPARK_HOME/bin/spark-submit --properties-file my-config.conf

vim my-config.conf
#key和value用空格隔开
spark.master local[4]
spark.app.name "My Spark App"

几乎所有的Spark配置都发生在SparkConf的创建过程中,
但,有一个重要的选项是个例外。
你需要在$SPARK_HOME/conf/spark-env.sh中将环境变量:Spark_Local_Dirs(全部大写)设置为逗号隔开的存储位置列表,
来指定Spark用来混洗数据的本地存储路径。
这需要在独立模式和Mesos模式下设置。

 

硬件供给
除了内存和CPU核心,Spark还要用到本地磁盘来存储数据shuffle操作的中间数据,以及溢写到磁盘中的RDD分区数据。
因此,使用大量的本地磁盘可以帮助提升Spark应用的性能。

在YARN模式下,由于YARN提供了自己的指定临时数据存储目录的机制,
Spark的本地磁盘配置项会直接从YARN的配置中读取。
而在独立模式下,我们可以在部署集群时,在spark-env.sh文件中设置环境变量:Spark_Local_Dirs(大写),
这样Spark应用启动时就会自动读取这个配置项的值。

在所有情况下,本地目录的设置都应当使用由逗号隔开的目录列表。


在Spark的独立模式中,我们需要启动多个工作节点实例(使用 Spark_Worker_Instances指定)
来让单个应用在一台主机上运行于多个执行器节点中。


序列化格式
当Spark需要通过网络传输数据,或是将数据溢写到磁盘上时,Spark需要把数据序列化为二进制格式。
序列化会在数据进行shuffle操作时发生,此时有可能需要通过网络传输大量数据。
默认情况下,Spark会使用Java内建的序列化库。
Spark也支持使用第三方序列化库Kryo,
可以提供比Java的序列化工具更短的序列化时间和更高压缩比的二进制表示,但不能直接序列化全部类型的对象。


在默认情况下,Spark会使用60%的空间来存储RDD,20%存储shuffle操作产生的数据,剩下的20%留给用户程序。
用户可以自行调节这些选项来追求更好的性能表现。
如果用户代码中分配了大量的对象,那么降低RDD存储和数据shuffle存储所占用的空间可以有效避免程序内存不足的情况。


Spark默认的cache()操作会以 MEMORY_ONLY 的存储级别持久化数据。
这意味着如果缓存新的RDD分区的时候空间不够,旧分区就会直接被删除。
当用到这些分区数据时,再重新计算。
所以,有时以 MEMORY_AND_DISK 的存储级别的缓存策略会获得更好的效果,
因为在这种存储级别下,内存中放不下的旧分区就会被写入磁盘,当再次需要用到的时候再从磁盘上读取回来。
这样的代价有可能比重算各分区要低很多,也可以带来更稳定的性能表现。当RDD分区的重算代价很大时,这种设置尤其有用。

对于默认缓存策略的另一个改进是缓存序列化后的对象而不是直接缓存。
我们可以通过 MEMORY_ONLY_SER 或者 MEMORY_AND_DISK_SER 的存储级别来实现这一点。
缓存系列化后的对象会使缓存的过程变慢,因为序列化对象也会消耗一些资源,
不过,这可以显著减少JVM的垃圾回收时间,因为很多独立的记录现在可以作为单个序列化的缓存而存储。
这种缓存方式会把大量对象序列化为一个巨大的缓存区对象。

 


Spark SQL 性能调优选项
set spark.sql.codegen=true;
spark.sql.codegen 这个选项可以让 Spark SQL 把每条查询语句在运行前编译为Java二进制代码。
由于生成了专门运行指定查询的代码,codegen可以让大型查询或频繁重复的查询明显变快。
然而,在运行特别快的即时查询语句时,codegen有可能会增加额外开销,因为codegen需要让每条查询走一遍编译的过程。

set spark.sql.inMemoryColumnarStorage.batch=1000;
spark.sql.inMemoryColumnarStorage.batch 在缓存时,
Spark SQL 会按照这个选项设置的大小(默认是:1000),把记录分组,然后分批压缩。
太小的批处理大小会导致压缩比过低
批处理大小过大的话,比如当每个次批处理的数据超过内存所能容纳的大小时,也有可能会引发问题。
如果你表中的记录比较大,你就有可能需要调低批处理大小来避免内存不够的错误(OOM)。
如果不是在这样的场景下,默认的批处理大小比较合适的,因为压缩超过1000条记录也基本无法获得更高的压缩比了。

 

posted @ 2021-01-27 22:21  茗::流  阅读(92)  评论(0)    收藏  举报
如有雷同,纯属参考。如有侵犯你的版权,请联系我。