作者:刘旭晖 Raymond 转载请注明出处
Email:colorant at 163.com
BLOG:http://blog.csdn.net/colorant/
Tachyon是AmpLab的Li Haoyuan所开发的一个基于内存的分布式文件系统,出发点是作为AMPLAB的BDAS的一个组成部分
总体设计思想
从Tachyon的设计目标来看,是要提供一个基于内存的分布式的文件共享框架,需要具备容错的能力,还要体现内存的性能优势
Tachyon以常见的Master/worker的方式组织集群,由Master节点负责管理维护文件系统MetaData,文件数据维护在Worker节点的内存中。
在容错性方面,主要的技术要点包括:
- 底层支持Plugable的文件系统如HDFS用于用户指定文件的持久化
- 使用Journal机制持久化文件系统的Metadata
- 使用Zookeeper构建Master的HA
- 没有使用replica复制内存数据,而是采用和Spark RDD类似的Lineage的思想用于灾难恢复(这一点后面再讨论)
此外为了兼容Hadoop应用,提供了HDFS兼容的API接口
具体实现分析
初始化流程
Tachyon文件系统的初始化,其实就是创建和清空Master/worker所需的工作目录
对Master节点来说这些目录包括底层持久化文件系统上的Data/worker/Journal目录,实际上这里的Worker目录是由Worker节点使用的(用于存放一些零时的持久化文件,丢失Meta信息的数据块等),但是放在Master节点来创建,本质上是为了简化创建逻辑(因为放在HDFS上,只创建一次)
对worker节点来说所需的目录就是本地Ramdisk目录
此外,在master的Journal文件夹中,会创建一个特定前缀的空文件用于标志文件系统格式化完毕
Tachyon Master的启动过程
Tachyon Master的启动过程,首先当然是要读取Master相关配置参数,目前都是通过-D参数传给Java的,理想的是通过配置文件来做。目前这些参数,一部分是在Env文件里设置变量,再通过-D参数设置,也有的直接写死在-D参数中的,也有启动脚本中默认未配置,在MasterConf代码里使用了默认值的
通过读取特定的format文件判断文件系统是否格式化
接下来就是在内存中重建文件系统信息
Tachyon的文件系统信息依靠Journal日志保存,Journal包括两部分,一是meta信息在某个时刻的快照Image,二是增量Log。Tachyon Master启动时首先从快照Image文件中读取文件系统meta信息,包括各种数据节点(文件/目录/Raw表/Checkpoint/依赖关系等)信息,而后再从继续EditLog(可能多个)中读取增量操作记录,EditLog的内容基本对应于Tachyon文件系统Client的一些相关操作,包括文件的添加,删除,重命名,数据块的添加等等
需要注意的是,这里的Log记录不包括实际的文件内容数据,只是meta信息,所以如果Cache中的文件内容丢失,如果没有持久化,也没有绑定相关lineage信息,那么对应的文件的具体内容也就丢失了
文件系统信息恢复完毕以后,在Tachyon Master正式启动服务之前,Tachyon Master会先把当前的Meta Data写出为新的快照Image
在启用zookeepeer的情况下,standby的Master会定期将Editlog合并并创建Standby的Image,如果没有Standby的Master则只有在启动过程中,才通过上述步骤合并到新的Image中。这里多个Master并发操作Image的editlog,没有Lock或者互斥的机制,不知道会不会存在竞争冲突,数据stale或丢失的问题
文件的存储
Tachyon存放在RamDisk上的文件以Block(默认为1G)为单位划分,Master为每个Block分配一个BlockID,Worker直接以BlockID作为实际的文件名在Ramdisk上存储对应Block的数据
数据的读写
Tachyon的文件读写,尽可能的通过Java NIO API将文件直接映射到内存中,做为数据流进行读写操作,目的在于避免在Java Heap中使用大量的内存,由此减小GC的开销,提升响应速度
读写过程中,所有涉及到Meta相关信息的,都需要通过调用Tachyon Master经由Thrift暴露的ServerAPI来执行
Tachyon的文件读操作支持本地和远程两种模式,从Client API的角度来说对用户是透明的。读文件的实现,其流程基本就是先从Master处获取对应文件Offset位置对应的Block的ID
而后连接本地Worker取得相应ID对应的文件名,如果文件存在,Client端代码会通知Worker锁定对应的Block,而后Client端代码直接映射相关文件为RandomAccessFile直接进行读操作,并不经由Worker代理读取实际的数据
如果本地没有Worker,或者文件在本地worker上不存在,Client代码再进一步通过Master的API获取相关Block所对应的Worker,而后通过Worker暴露的DataServer接口读取对应Block的内容,在DataServer内部,同样延续锁定对应Block,映射文件的流程读取并将数据返回给Client
另外,基于读数据的时候使用的TachyonFile的API接口,如果使用的是FileStream的接口,当远程Worker也没有对应文件Block时,RemoteBlockInStream还会尝试从底层持久化文件系统层(如果存在对应的文件的话)去读取数据,而ReadByteBuffer接口则没有对应的流程(个人感觉,应该做到两种方式的行为匹配才对)。
Tachyon目前只支持本地写操作,写操作按写入位置可以分为
Cache:写到Tachyon内存文件系统中
Through:写到底层持久化文件系统中
具体的类型是以上几种情况的合法的组合,如单cache,cache +through等
还有一个Async模式:异步写到底层持久化文件系统中,这个大概是为了优化那些数据需要持久化,但是又对性能Latency等有要求的场合
读写操作现存问题和并发操作相关
前面提到读取数据时Client端会通知WorkerLock对应的Block。需要注意的是这里的Lock实际上并没有互斥的意思,只是一个标志表示当前还有用户在使用相关文件和数据,这样,在Worker需要分配内存淘汰旧的数据的时候,当前正在使用的文件将不会被删除。
而在写操作过程中,目前的实现看来对并发处理相关的内容基本没有考虑
例如Read操作已经Lock的文件block,依然可以被主动Delete,不考虑lock的状态,当然这一点可能和多数Linux类的Filesystem的设计一致,(但是Windows上显然可能提示无法删除)这个还要再研究一下在大数据分布是环境下其它的设计实现是怎样的
而写操作本身的再入也没有很处理好,不能支持并发是一个问题,单线程重写文件也会造成前面的数据块的丢失或者数据块的混合,当然,这也是因为目前还没有考虑到支持这些情况。
Write目前不支持Append操作,这个和当前的设计也有很大的关系,block尺寸按文件计算,尺寸固定,所以要Append就需要考虑必须在同一节点上写数据,要不然就要支持远程写数据到当前Block所在的节点上,要不然就要支持动态Block大小。然后如果支持异地写,还要考虑并发Append的问题,需要Lock文件,阻止并发写等,这些都是目前Tachyon所无法支持的
Raw Table表单
Tachyon所谓的Raw Table的支持,目前的实现,本质上只是一个分级(column)的文件目录,每个Colum下的一个Partition对应一个Tachyon文件,从用户的角度上来看,相对于直接构建这样的目录结构,仅仅是省去了为每个Partition命名,以及方面统一操作几个文件,实际上并没有提供其它额外的辅助功能,如检索等等
HDFS文件接口
Tachyon提供了兼容HDFS API的文件操作接口,基本上就是提供了一个TFS拓展Hadoop的FileSystem接口,主要就是用Tachyon Client提供的接口实现HDFS对文件相关信息和Metadata的操作
在具体的数据读写上,则是在建立好数据流的基础上,通过Tachyon的FileInStream和FileOutStream来执行
比较奇怪的是FileOutStream是直接传递给了Hadoop FSDataOutputStream,而FileInStream则进一步封装成了HdfsFileInputStream再传递给Hadoop FSDataInputStream使用,理论上难道不是应该只要实现Java InputStream的类就可以了么,其它API接口应该是Hadoop FSDataInputStream实现的
White list/ pin list功能?
White list和 pin list以路径前缀的方式存储一些URLpath用作Filter,用作设置默认需要加载到内存中的文件
White List的设计意图是在读取数据时自动尝试在内存中Cache对应文件,但是具体的实现貌似仅仅设置了标志位,但是没有完成相关功能?实际使用Tachyon API时需要指定Read Cache Type来指定需要Cache对应文件
PinList的目的是保证对应的文件常驻在内存中,目前的实现:在写数据时,强制要求要有足够的内存空间否则出错。在Worker端,当内存空间不足,需要淘汰数据,释放空间时,也会忽略PinList列表中的文件。但是在读数据的路径上,如果由于某种原因,对应文件不在内存上,需要从底层持久化文件系统中获取的话,PinList并不能保证自动Cache这些文件在内存中,依然依赖于Read文件时使用的read Cache type
总结
总体而言,目前Tachyon的功能基本可以看作就是:对外提供了一个以顺序文件流的方式,写本地内存,读本地和远程内存的接口,持久化特定文件,同时兼容HDFS API。其处理内存丢失和替换数据的方式使其更像一个Cache系统而非文件系统。其它的各种额外辅助功能都还不完善。就其实现的部分,各级component包括IPCProtocol,配置,Image,Data API设计,各种异常处理乃至并发处理架构等方面,个人感觉实现方式略显简单粗暴,可以理解为以实现快速原型为思想设计的,存在一定的改进的空间,或者需要考虑优化设计方案。
而前面提到的做为容错设计上,最重要的Lineage的设定,(这也是作者的论文的核心内容所在,毕竟其它部分如果从学术的角度上来说并没有太大创新,而只是具体的工程实现)目前看来,似乎并没有很理想的实现,或者说在实际应用场合中有比较多的局限性?大概需要一个说服力比较强的Case来证明其实用性和适用性(当然,或者是我没有看到更多的这方面的代码,据说有更多的相关实现还没有public?)