图1
图2
图3
图4
数据不断的流进Receiver(也就是Executor,但这个只是数据级别的,InputDStream真正的产生是在Driver上,因为它是框架调度级别的),不断的接受到数据的时候为了保证自己的安全性,默认情况下,它会不断的通过容错的方式进行处理,容错的处理方式:把这个数据通过BlockManager写入内存 + 磁盘,同时用副本的方式或者WAL(Write Ahead Log)来保证安全性。
WAL的机制:它写数据的时候,就会先通过WAL机制写到文件系统中,然后在进行所谓的存储到Executor,Executor又存储到内存或者磁盘中,这个是由Storage Level去规定的。
假设没能存储到WAL,后面一定不会存储到Executor中,如果没有存储到Executor的话,一定不会汇报给Driver,这个数据也一定不会被处理。
从数据流的角度来讲Executor是一条一条的接受数据的,Receiver会把这个数据在内存中积累到一定程度以后它才写到WAL或者磁盘,还没有积累到写WAL或者磁盘的程度,Executor或者Receiver崩溃了,这个时候数据会丢失一点。
处理的数据在处理前CheckPoint,处理之后也会Checkpoint。
Exactly Once的事务处理:
1、数据零丢失:必须有可靠的数据来源和可靠的Receiver,且整个应用程序的metaData必须进行checkpoint,且通过WAL来保证数据安全;从全面的数据的角度去考虑,包括接受到的数据和元数据本身,实际生产环境下,Receiver接受到来自Kafka的数据或者Spark Streaming读取Kafka的数据,默认的情况下,它存储的话会Replication到两个Executor中,具体执行计算的时候,必须完成这两份机器的容错之后,它才开始真正的计算;如果Receiver接受数据的时候崩溃了,这个时候不会有数据丢失,没有完成副本的复制,所以Receiver恢复的时候要重新接受数据;
2、Spark Streaming 1.3的时候为了避免WAL的性能损失和实现Exactly Once而提供了Kafka Direct API,把Kafka作为文件存储系统!!!此时兼具有流的优势和文件系统的优势,至此,Spark Streaming + Kafka就构建了完美的流处理的世界!!!所有的Executor通过Kafka API直接消费数据,直接管理Offset,所以也不会重复消费数据;实现了事务!!!
数据丢失及其具体的解决方式:
在Receiver收到数据且通过Executor开始计算数据的时候如果Driver突然崩溃,则此时Executor会被Kill掉,那么Executor中的数据就会丢失,此时就必须通过例如WAL的方式让所有的数据都通过例如HDFS的方式首先进行安全性容错处理,此时如果Executor中的数据丢失的话就可以通过WAL恢复回来;
数据重复读取的情况:
在Receiver收到数据且保存到了HDFS等持久化引擎上,但是没有来得及进行updateOffsets,此时Receiver崩溃后重新启动就会通过管理Kafka的ZooKeeper中的元数据再次重复读取数据,但是此时Spark Streaming认为是成功的,但是Kafka认为是失败的(因为没有更新offset到ZooKeeper中),此时就会导致数据重复消费的情况;处理数据的时候可以访问Kafka在ZooKeeper中保存的元数据信息,处理的过程中把元数据信息写到数据库等,然后再次进行计算的时候,查询一下这个保存的元数据信息是否被处理过,如果处理过就跳过,每次处理的时候都查一遍。
性能损失:
1、通过WAL的方式会极大的损伤Spark Streaming中Receivers接受数据的性能;
2、如果通过Kafka作为数据来源的话,Kafka中有数据,然后Receiver接受数据的时候又会有数据副本,这个时候其实是存储资源的浪费。