「大数据」(七十七) Spark之IO机制
【导读:数据是二十一世纪的石油,蕴含巨大价值,这是·情报通·大数据技术系列第[77]篇文章,欢迎阅读和收藏】
1 基本概念
与传统的 IO 相比, Spark IO 有很大区别。传统的数据存在单个计算机中,数据量少,而 Spark 的数据存储在集群中,数据量巨大。另外, Spark 需要考虑本地主机的 IO 开销,还需要顾虑到不同主机之间的传输开销。针对这些情况, Spark 就要制定一些机制,来解决数据量巨大开销的问题。
2 术语解释
3 Spark IO 机制介绍
3.1 IO 序列化
序列化是将对象转换为字节流,本质上可以理解为将链表存储的非连续空间的数据存储转化为连续空间存储的数组中。这样就可以将数据进行流式传输或者块管理。
序列化主要有以下两个目的:
l 进程间通信:不同节点之间进行数据传输;
l 数据持久化存储到磁盘:本地节点将对象写入磁盘;
无论是内存或者磁盘中的 RDD 含有的对象存储,还是节点间的传输数据,都需要执行序列化的过程。序列化与反序列化的速度、序列化后的数据大小等都影响数据传输的速度,以致影响集群的计算效率。
Spark 通过集中方式实现进程通信,包括 Actor 的消息模式、 Java NIO 和 Netty 的 OIO 。
3.2 IO 压缩
当大片连续区域进行数据存储并且存储区域中数据重复性高的状况下,数据适合进行压缩。数组或者对象序列化后的数据块可以考虑压缩。所以序列化后的数据可以压缩,使数据紧缩,减少空间开销。
压缩采用了两种算法:Snappy 和 LZF ,底层分别采用了两个第三方库实现,同时可以自定义其它压缩对 Spark 进行扩展。
Snappy 提供了更高的压缩速度, LZF 提供了更高的压缩比。
val conf=sc.getConf
conf.getBoolean( “ spark.broadcast.compress ” ,true)
conf.set( “ spark.broadcast.compress ” ,true)
Sc 是 SparkContext 对象, conf 是 SparkConf 对象。
在分布式中,序列化和压缩是两个重要的手段。
Spark 通过序列化将链式分布的数据转化为连续分布的数据,这样就能够进行分布式的进程间数据的通信,或者在内存进行数据压缩等操作,提升 Spark 的应用性能。通过压缩,能够减少数据的内存占用,以及 IO 和网络数据传输开销。
3.3 IO 块管理
RDD 在逻辑上是按照 Partition 分块的,可以将 RDD 看成是一个分区作为数据项的分布式数组。物理上存储的 RDD 是以 Block 为单位的,一个 Partition 对应一个 Block ,用 Partition 的 ID 通过元数据的映射到物理上的 Block ,而这个物理上的 Block 可以存储在内存,也可以存储在某个节点的 Spark 的硬盘临时目录。
Spark 的 I/O 管理整体分为两个层次:
1) 、通信层:I/O 模块也是采用 Master-Slave 结构来实现通信层的架构, Master 和 Slave 之间传输控制信息、状态信息。
2) 、存储层:Spark 的块数据需要存储在内存或者硬盘,有可能还需要传输到远端机器,这些有存储层完成。
可以通过以下几个维度来理解整个存储系统:
1 )、管理和接口:
当其他模块要与 storage 模块进行交互时, storage 模块提供了统一的操作类 BlockManager ,外部类与 storage 模块打交道都需要调用 BlockManager 相应接口来实现。
2 )、通信层:
BlockManagerMasterActor :在主节点创建,从节点通过这个 Actor 的引用向主节点传递消息和状态。
BlockManagerSlaveActor :在从节点创建,主机点通过这个 Actor 的引用向从节点传递命令,控制从节点的块读取。
BlockManagerMaster :对 Actor 通信进行管理。
3 )、数据读写层:
DiskStore :提供 Block 在磁盘上以文件形式读写的功能;
MemoryStore :提供 Block 在内存中的 Block 读写功能;
ConnectionManager :提供本地机器和远端节点进行网络传输 Block 的功能;
BlockManagerWorker :对远端数据的异步传输进行管理。
主节点和从节点之间通过 Actor 传递消息来传递命令和状态。
Master 节点负责总体控制, Slave 节点接受命令、汇报状态。( Actor 和 ref 是 AKKA 中两个不同的 Actor 引用)。
BlockManager 在内部封装 BlockManagerMaster ,并通过 BlockManagerMaster 进行通信。Spark 在各节点创建各自的 BlockManager ,通过 BlockManager 对 storage 模块进行操作。Block 对象在 SparkEnv 创建, SparkEnv 相当于线程的上下文变量,在 SparkEnv 也会创建很多的管理组件。
3.4 数据读写
数据写入主要分以下步骤:
1 )、 RDD 调用 compute() 方法进行制定分区的写入。
2 )、 CacheManager 中调用 BlockManager 判断数据是否已经写入,如果未写入则写入。
3 )、 BlockManager 中数据与其他节点同步。
4 )、 BlockManager 根据存储级别写入制定的存储层。
5 )、 BlockManager 向主节点汇报存储状态。
数据读取:
在 RDD 类中,通过 compute 方法调用 iterator 读写某个分区( Partition ),作为数据读取的入口。分区是逻辑概念,在物理上是一个 Block 。
在本地同步读取数据块,首先看能否在内存读取数据块,如果不能读取,则看能否从 Tacjyon 读取数据块,如果仍不能读取,则看能否从本地磁盘读取数据。如果仍不存在,再看看网络中其它节点是否有数据。
内存 Block 块管理是通过链表来实现的,在 DiskStore 中,一个 Block 对应一个文件。