结构化流的无状态操作+有状态操作及常见的性能优化

结构化流的无状态操作

结构化流中进行select,map等投影以及filter等选择操作时,都是独立的处理一条条的记录,后续处理的记录不依赖于之前处理过的记录的任何信息,所以spark不需要为这种操作保留任何的状态信息,所以这些操作就称为无状态操作,这种操作支持追加输出模式(所以可以选择文件输出池进行输出),也支持更新输出模式,不过不支持完整输出模式,原因仅仅是因为要保留处理过的所有记录到检查点目录代价巨大(内存和磁盘容量有极高的要求)

结构化流的有状态操作

相比于无状态操作,有状态操作一般最常见的就是指聚合操作,在处理当前的微批型数据时,会把结果加到前面微批型生成的结果上面,这些保存前面微批型计算结果的数据就是状态,这些状态值会被保存到检查点目录中.

流式无状态连接

流式的数据可以和静态表的数据进行连接,如下代码所示:

#流式数据DataFrame[ip String, message String]
events = spark.readStream.
#静态数据DataFrame[ip String, hostName String]
hosts = spark.read.
#流式数据和静态数据的连接
joinStream = events.join(hosts, "ip")

数据流与静态表的连接需要注意的几个点:

  1. 数据流与静态表的连接属于无状态操作,不需要维护任何状态,因为也就没有什么要设置水印的说法。PS: 这里需要明确一点,这里说的是数据流与静态表连接操作本身是无状态操作,但是不代表连接之后的结果,也就是joinStream不能进行groupby的有状态操作,连接后的joinStream后续可以进行全局的聚合或者按照时间窗口维度的聚合这些有状态的操作,在进行后续的有状态操作时自然也可以设置水印
  2. 由于静态表在每次微批型连接操作时都会被读取,所以进行连接操作时最好缓存下静态表,这样就不用每次都进行读取操作了

性能优化

最主要的是要调整混洗操作的分区数,对于静态批操作的spark混洗操作来说,由于批操作每次都是操作GB甚至TB级别的数据,所以默认的200分区数不算多,但是对于结构化流操作来说,由于操作的数据是微批型,对于每个微批型来说,他的数据大小不大,比如基于时间窗口的聚合操作时比如五分钟一个窗口,这样每个微批型的数据也就是五分钟内的输入数据,所以每个微批型的数据量和批操作相比少了很多,所以默认的200混洗分区数量对于结构化流操作来说是太多了,可以设置少一点,比如cpu核心数的两到三倍即可.此外,一般来说批操作进行数据混洗操作时,每个执行器会把混洗操作后的结构保存到执行器所在的本地磁盘进行持久化,以便后续的失败重启后不需要从头开始计算,而对于结构化流来说,进行shuffle操作后要把结果保存到检查点目录中,而这个检查点目录一般是一个统一的hdfs目录,所以各个执行器需要跨网络传输把数据保存到统一的检查点目录中,相比之下,结构化流多了很多网络传输的开销

你可能感兴趣的:(spark,spark,大数据,分布式)