本期内容:
1,Exactly once
2,输出不重复
一、Exactly once
什么是事务处理?
数据一定会被处理而且只处理一次,这就是事务处理。
Spark在运行出错时不能保证输出也是事务级别的。例如说Task在处理一半的时候崩溃了,虽然在语义上保持了事务级别的处理,数据处理且仅被处理一次,但是如果把结果输出到数据库中,有可能输出多次。这种情况下就需要我们对事务进行控制。Spark Streaming基于Spark,在任务失败时会自动进行重试,这样会导致结果被多次写入下一步的程序中。所以在这里总结一下,数据能够处理且只被处理一次,能够输出且只被输出一次,这样才是比较完整的事务处理。
下面以图形的方式展现Spark Streaming的事务处理:
运行在Executor上的Receiver接收到数据通过BlockManager写入内存和磁盘,或者通过WAL机制写日志记录,然后把metedata信息汇报给Driver。在Driver端定期进行checkpoint操作。Job的执行完全基于Spark Core的调度模式。
Driver级别的数据恢复,直接从Driver进行Checkpoint的文件系统中把数据读取进来进行恢复,在内部重新构建StreamingContext(其实也就是构建SparkContext)并启动,恢复出元数据,再次产生RDD,恢复的是上次的Job,然后再次提交到Spark集群再次执行。
Receiver级别的数据恢复,会在以前数据的基础上继续接收数据,曾经接收的数据也会通过WAL的机制从磁盘上恢复回来。
我们以数据来自Kafka为例,运行在Executor上的Receiver在接收到来自Kafka的数据时会向Kafka发送ACK确认收到信息并读取下一条信息,Kafka会updateOffset来记录Receiver接收到的偏移,这种方式保证了在Executor数据零丢失。
Exactly Once 的事务处理:
1,数据零丢失:必须有可靠的数据来源和可靠的Receiver,且整个应用程序的metadata必须进行checkpoint,且通过WAL来保证数据安全。
2,Spark在1.3的时候为了避免WAL的性能损失和实现Exactly Once而提供了Kafka Direct API,把Kafka作为文件存储系统。此时兼具有流的优势和文件系统的优势,至此Spark Streaming+Kafka就构建了完美的流处理世界(1,数据不需要拷贝副本;2,不需要WAL对性能的损耗;3,Kafka使用ZeroCopy比HDFS更高效)。所有的Executors通过Kafka API直接消费数据,直接管理Offset,所以也不会重复消费数据。
数据可能在哪些地方丢失及其具体的解决方式:
在Receiver收到数据且通过Driver的调度Executor开始计算数据的时候,如果Driver突然崩溃,则此时Executor会被Kill掉,那么Executor中的数据就会丢失(如果没有进行WAL操作)。
解决方式:此时就必须通过例如WAL的方式,让所有的数据都通过例如HDFS的方式首先进行安全性容错处理。此时如果Executor中的数据丢失的话,就可以通过WAL机制恢复回来。
数据重复读取的情况:
在Receiver收到数据且保存到HDFS等持久化引擎但是没有来得及进行updateOffsets(以Kafka为例),此时Receiver崩溃后重新启动就会通过管理Kafka的Zookeeper中元数据再次重复读取数据,但是此时SparkStreaming认为是成功的,但是kafka认为是失败的(因为没有更新offset到ZooKeeper中),此时就会导致数据重新消费的情况。
解决方式:以Receiver基于ZooKeeper的方式,当读取数据时去访问Kafka的元数据信息,在处理代码中例如foreachRDD或transform时,将信息写入到内存数据库中(memorySet),在计算时读取内存数据库信息,判断是否已处理过,如果已处理过则跳过计算。这些元数据信息可以保存到内存数据结构或者memsql、sqllite中。
性能损失:
1,通过WAL的方式会极大的损伤SparkStreaming中Receivers接收数据的性能。
2,如果通过Kafka作为数据来源的话,Kafka中有数据,然后Receiver接收的时候又会有数据副本,这个时候其实是存储资源的浪费。
二、输出不重复
关于Spark Streaming数据输出多次重写及其解决方案:
1,为什么会有这个问题,因为Spark Streaming在计算的时候基于Spark Core天生会做以下事情导致Spark Streaming的结果(部分)重复输出:Task重试,慢任务推测,Stage重试,Job重试。
2,具体解决方案:
设置spark.task.maxFailures次数为1,这样就不会有Task重试了。
设置spark.speculation为关闭状态,就不会有慢任务推测了,因为慢任务推测其实非常消耗性能,所以关闭后可以显著提高Spark Streaming的处理性能。
Spark Streaming On Kafka的话,Job失败后可以设置Kafka的参数auto.offset.reset为largest方式。
最后再次强调可以通过transform和foreachRDD基于业务逻辑代码进行逻辑控制来实现数据不重复消费和输出不重复。这两个方法类似于Spark Streaming的后门,可以做任意想象的控制操作。
备注:
更多私密内容,请关注微信公众号:DT_Spark
更多干货请访问微博:http://weibo.com/ilovepains
每晚20:00大数据Spark技术永久免费公开课,YY频道:68917580