作者:张可1983来源:https://www.jianshu.com/p/fc0...一、概述分布式文件系统是分布式领域的基础应用,其中最著名的当属HDFS/政府飞行服务队。现在这个领域已经趋于成熟,但是了解它的设计点和思路对我们以后遇到类似的场景/问题有参考意义。而且,分布式文件系统并不是HDFS/GFS的唯一形式。除此之外,还有其他形式的产品,它们具有不同的形式和优势。了解它们也有助于扩大我们的视野。本文试图分析和思考我们要解决什么问题,我们有什么样的解决方案,以及我们在分布式文件系统领域各自选择的依据。2、过去是什么样子几十年前,分布式文件系统出现了,以1984年Sun公司开发的“网络文件系统(NFS)”为代表,当时主要要解决的问题是磁盘的形式的一个网络。,将磁盘与主机分开。这样不仅可以获得更大的容量,还可以随时切换主机,实现数据共享、备份、容灾等,因为数据是计算机中最重要的资产。NFS的数据通信示意图如下:部署在主机上的客户端,通过TCP/IP协议将文件命令转发给远程文件服务器执行,整个过程对主机用户是透明的。互联网时代,流量和数据快速增长,分布式文件系统要解决的主要场景发生了变化。一开始需要非常大的磁盘空间,这在磁盘系统的垂直扩展中是无法实现的。它必须同时分发和分发。该架构下,主机均为普通服务器,可靠性较低,因此容错、高可用、持久化、可扩展性等指标成为必须考虑的特性。3、对分布式文件系统的要求对于分布式文件系统,有一些特性是必须满足的,否则就没有竞争力。主要是:要符合POSIX文件接口标准,使系统易于使用,同时不需要修改用户的遗留系统;对用户透明,可以像使用本地文件系统一样直接使用;持久化确保数据不会丢失;具有可扩展性,在数据压力逐渐增大时也能平滑扩展;具有可靠的安全机制,确保数据安全;数据一致性,只要文件内容没有变化,你读的时候内容应该是一样的。另外,一些功能是分发的奖励项目,如下:支持的空间越大越好;支持的并发访问请求越多越好;性能越快越好;硬件资源利用率越高,越合理越好。4.架构模型从业务模型和逻辑架构来看,分布式文件系统需要这几类组件:存储组件:负责存储文件数据,必须保证文件持久化、副本间数据一致性、数据块的分配/合并等等等等;管理组件:负责元信息,即文件数据的元信息,包括文件存储在哪个服务器上,文件大小,权限等。此外,它还负责存储组件的管理,包括存储组件的位置服务器是否正常存活,是否需要进行数据迁移等;接口组件:提供接口服务供应用程序使用,包括SDK(Java/C/C++等)、CLI命令行终端,支持FUSE挂载机制。在部署架构上,有“集中化”和“分散化”两条路线,即是否采用“管理组件”作为分布式文件系统的中心管理节点。两条路线都有优秀的产品,下面分别介绍它们的区别。1、有一个以GFS为代表的中心节点。中心节点负责文件定位、文件元信息维护、故障检测、数据迁移等管控功能。下图是GFS的架构图:图中GFSmaster是GFSNode的中心,GFchunkserver是GFS的存储节点。其运行路径如下:客户端向中心节点请求“查询文件的某部分数据”;中央节点返回文件的位置(哪个文件在哪个chunkserver上)和字节范围信息;Client根据中心节点返回的信息,直接向对应的chunkserver发送数据读取请求;块服务器返回数据。该方案中,一般中心节点不参与真正的数据读写,而是将文件元信息返回给客户端,即客户端直接与数据节点通信。其主要目的是减轻中心节点的负载,防止其成为瓶颈。这种带有中心节点的方案由于中心节点易于控制且功能强大,已广泛应用于各种存储系统中。2、非中心节点用ceph表示。每个节点都是自治和自我管理的。整个ceph集群只包含一类节点,如下图所示(最下面的红色RADOS被ceph定义为“同时包含元数据和文件数据”的节点)。去中心化最大的好处就是解决了中心节点本身的瓶颈,这也是ceph号称可以向上无限扩展的原因。但是由于Client直接与Server通信,所以Client在对某个文件进行操作时,必须知道自己应该访问集群中的哪个节点。Ceph提供了一个非常强大的原始算法来解决这个问题——CRUSH算法。5.持久性对于文件系统来说,持久性是根本。只要Client收到Server的响应保存数据成功,数据应该不会丢失。这个主要是通过多副本的方式来解决的,但是在分布式环境下,多副本就有这些问题要面对。如何保证每个副本的数据是一致的?如何分散副本,让灾难发生时,所有副本都不会损坏?如何检测损坏或过期的副本,以及如何处理它们?应将哪份副本退还给客户?1、如何保证每个副本的数据一致?同步写入是保证副本数据一致性最直接的方式。当客户端写入文件时,服务器会等待所有副本写入成功后才返回给客户端。这种方法简单且有保证,唯一的缺点是会影响性能。假设有3个副本,如果每个副本耗时N秒,可能会阻塞Client3N秒。有几种优化方式:并行写入:一个副本作为主副本,数据并行发送给其他副本;chainwrite:几份组成一条链(chain),不是在接收到内容之后再传播,而是像流一样,在接收上游传来的数据的同时,传递给下游。另一种方式是使用CAP中提到的W+R>N的方式,例如在3副本(N=3)的情况下,W=2,R=2,即如果成功写入2副本,则算是成功了,read有时也会从2个replicas中读取。这种方法通过牺牲一定的读取成本来降低写入成本,同时增加了写入的可用性。这种方法在分布式文件系统中占用较少的空间。2、如何分散副本,当灾难发生时,所有副本都不会损坏?这主要是为了避免某个机房或某个城市的自然环境故障,所以要在很远的地方分配一个副本。它的副作用是这个副本的写性能可能会有一定程度的下降,因为它离Client最远。因此,如果物理条件不能保证足够的网络带宽,则在读写副本的策略上需要做一定的考虑。可以参考同步写入只写2个副本和异步写入远方副本的方式。同时,为了保证一致性,在读取的时候一定要注意,避免从异步写入的副本中读取到过时的数据。3.如何检测损坏或过期的拷贝,如何处理?如果存在中心节点,则数据节点定期与中心节点通信,上报自己数据块的相关信息,中心节点将其与自己维护的信息进行比对。如果一个数据块的校验和不正确,说明这个数据块已经损坏;如果数据块的版本不正确,则表明该数据块已经过期。如果没有中心节点,以ceph为例,它在自己的节点集群中维护了一个比较小的monitor集群,数据节点向这个monitor集群报告自己的情况,判断是否损坏或者过期。当发现损坏或过期的副本时,只需将其从元信息中删除并创建一个新副本。被移除的副本会在后续的回收机制中被回收。4.应将哪份副本退还给客户?这里有很多策略,比如round-robin,最快的节点,成功率最高的节点,空闲CPU资源最多的节点,甚至第一个固定的节点作为master节点,或者离你最近的节点第一,这将在一定程度上节省整体操作完成时间。六、可扩展性1.存储节点的可扩展性当一个新的存储节点加入到集群中时,它会主动向中心节点注册并提供自己的信息。当后续创建文件或将数据块添加到现有文件中时,可以将中心节点分配给这个新节点,比较简单。但是有一些问题需要考虑。如何让每个存储节点的负载相对均衡?如何保证新增节点不会因为短期负载压力过大而崩溃?如果需要进行数据迁移,如何做到对业务层透明?1)如何让每个存储节点Node负载相对均衡?首先,要有评估存储节点负载的指标。方式有很多种,可以从磁盘空间使用率来考虑,也可以从磁盘使用率+CPU使用率+网络流量情况综合判断。一般来说,磁盘使用率是核心指标。其次,在分配新空间时,优先选择资源占用率低的存储节点;对于现有存储节点,如果负载过载或资源使用不均衡,则需要进行数据迁移。2)如何保证新增节点不会因为短期负载压力过大而崩溃?当系统发现增加了一个新的存储节点时,它的资源占用显然是最低的,所以所有的写流量都路由到这个存储节点上,这可能会导致这个新节点的短期负载过大。所以在分配资源的时候,需要有一个预热时间,在一段时间内慢慢路由写压力,直到达到新的平衡。3)如果需要进行数据迁移,如何做到对业务层透明?在有中心节点的情况下,这个工作比较好做,中心节点会搞定——确定哪个存储节点压力更大,确定哪些文件迁移到哪里,更新自己的元信息,什么在迁移过程中如何处理写入,以及如果发生重命名该怎么办。不需要上层应用程序来处理它。如果没有中心节点,成本比较高。在系统的整体设计中,也必须考虑到这种情况。例如ceph采用了逻辑位置和物理位置的两层结构。暴露给客户端的是逻辑层(pool和placegroup),这个在迁移过程中是不变的,下层物理层数据块的移动只是逻辑层引用的物理块的地址。从Client的角度来看,逻辑块的位置并没有发生。改变。2.中心节点的可扩展性如果有中心节点,还要考虑它的可扩展性。由于中心节点作为控制中心,是主从模型,扩展性比较有限,有上限,不能超过单台物理机的规模。我们可以考虑各种手段尽可能的提高这个上限。有几种方式可以考虑:以大数据块的形式存储文件——比如HDFS中数据块大小为64M,ceph中数据块大小为4M,远远超过stand中的4k-单独的文件系统。其意义在于大大减少了元数据量,使得中心节点的单机内存能够支撑足够的磁盘空间元信息。中心节点采用多级方式——最顶层的中心节点只存储目录的元数据,并指定哪个二级主控节点去查找某个目录下的文件,然后通过二级主控节点Nodes;中心节点共享存储设备——部署多个中心节点,但它们共享同一个存储外设/数据库,存储元信息,中心节点本身是无状态的。这种模式大大增强了中心节点的请求处理能力,但性能会受到一定影响。iRODS就是这样做的。七、高可用1、中心节点的高可用中心节点的高可用不仅保证了自身应用的高可用,也保证了元数据数据的高可用。元数据的高可用主要是数据持久化,需要有备份机制保证不丢失。一般的方法是增加一个slave节点,master节点的数据实时同步到slave节点。共享磁盘还用于通过raid1硬件资源确保高可用性。显然,增加从节点的主备方式部署更容易。元数据的数据持久化策略有以下几种方式:直接保存到存储引擎,通常是数据库。以文件的形式直接存盘也不是不可以,但是因为元信息是结构化数据,这相当于自己开发一个小型数据库,比较复杂。将日志数据保存到磁盘文件中(类似于MySQL的binlog或Redis的aof),在系统开始提供服务时在内存中重建结果数据。修改时,先修改磁盘日志文件,再更新内存数据。这种方式简单易用。目前内存服务+日志文件持久化是主流方式。一种是纯内存操作,效率很高,日志文件的写入也是顺序写入;另一种是不依赖外部组件的独立部署。为了解决日志文件会随着时间的推移越来越大的问题,使系统能够尽快启动和恢复,需要采用内存快照的方式来辅助——定期保存内存转储,并且只转储时间后保留日志文件。这样,恢复时,从最新的内存转储文件开始,找到相应检查点后的日志文件,开始重放。2.存储节点的高可用在上一章“持久化”中,在保证数据副本不丢失的同时保证了高可用。8.性能优化和缓存一致性近年来随着基础设施的发展,局域网中的千兆甚至10G带宽已经越来越普遍。按10Gbit计算,每秒大约传输1250M字节的数据,而SATA磁盘的读取速度和写入速度近年基本达到瓶颈,300-500M/s左右,也就是对于纯读写来说,网络已经超出了磁盘的容量,不再是瓶颈。NAS等网盘也在近年流行起来。但这并不意味着不需要优化读写。毕竟网络读写的速度还是比内存慢很多。常见的优化方式主要有:在内存中缓存文件内容;预加载数据块以避免客户端等待;合并读写请求,即将单个请求累加起来,分批发送给服务器。使用缓存在提高读写性能的同时,也带来了数据不一致的问题:会出现丢失更新的现象。当多个客户端在一段时间内连续写入同一个文件时,先写入的客户端可能会丢失其写入的内容,因为它可能被后写入的客户端的内容覆盖;数据可见性问题。客户端读取自己的缓存。过期前,如果其他客户端更新了文件内容,自己是看不到的;即不同客户端同时读取同一个文件,内容可能不一致。此类问题有以下几种处理方法:文件只能读取不能修改:文件一旦创建,就只能读取不能修改。这样就不会出现客户端缓存不一致的问题;通过锁:使用锁时必须考虑不同的粒度。是否允许其他客户端在写入时读取?是否允许其他客户在读取时写入?这是性能和一致性之间的权衡。权衡比较困难,所以最好提供不同粒度的锁,让业务方选择。但是这样做的副作用就是增加了业务端的使用成本。9.安全性由于分布式文件存储系统必须是多客户端、多租户的产品,它存储着潜在的重要信息,安全性是其中重要的一环。主流文件系统的权限模型如下:DAC:全称DiscretionaryAccessControl,也就是我们熟悉的类Unix权限框架。它采用user-group-privilege作为三级体系,其中user是所有者,group包括所有者所在的组。非所有者的组和权限包括读、写和执行。这个系统主要是基于所有者,谁允许谁对哪些文件有什么权限。MAC:全称MandatoryAccessControl,根据资源的机密性来划分。比如分为“普通”、“机密”、“绝密”三层,每个用户可能对应不同的机密阅读权限。这种权限系统起源于安全机构或军队的系统,并且会更加普遍。它的权限由管理员控制和设置。Linux中的SELinux是MAC的一种实现,是为了弥补DAC的缺陷和安全隐患而提供的。SELinux解决的问题请参考什么是SELinux?RBAC:全称是RoleBasedAccessControl,是一种基于角色的权限系统。角色拥有什么样的资源权限,用户属于哪个角色,非常适合企业/公司的组织架构。RBAC也可以具体化演化为DAC或MAC的权限模型。市场上的分布式文件系统有不同的选择。例如,ceph提供了一个类似于DAC但又略有不同的权限系统。Hadoop本身依赖于操作系统的权限框架。同时,ApacheSentry在其生态系统中提供了基于RBAC权限体系的权限体系作为补充。十、其他1、空间分配有两种:连续空间和链表空间。contiguousspace的好处是读写速度快,顺序恰到好处。缺点是会造成磁盘碎片。比较麻烦的是,连续分配大磁盘空间,必须找漏洞时,需要提前知道连续分配,才能写入。文件的大小,为了找到合适大小的空间,而要写入的文件的大小往往是事先不知道的(比如可编辑的word文档,其内容可以随时增加);而链表空间的优点是磁盘碎片少。缺点是读写都很慢,尤其是随机读。你必须从链表的第一个文件块开始一个一个往下找。为了解决这个问题,出现了索引表——将文件与数据块的对应关系保存一份,存放在索引节点(一般称为i节点)中。操作系统会将i-node加载到内存中,这样程序就可以在内存中随机寻找数据块了。这样就解决了磁盘链表的劣势。如果索引节点的内容太大,内存无法加载,可能形成多级索引结构。2、文件删除是实时删除还是延迟删除?实时删除的好处是可以快速释放磁盘空间;延迟删除只是在执行删除动作的时候设置一个标志,之后在某个时间点批量删除。优点是仍然可以分阶段保留文件,最大程度避免误删,缺点是仍然占用磁盘空间。在分布式文件系统中,磁盘空间是比较充裕的资源,所以几乎都是使用逻辑删除来恢复数据,同时在一段时间后(可能是2天或者3天,这个参数一般是可以配置的),然后回收删除的资源。如何回收已删除或无用的数据?可以从文件的元信息入手——如果某个数据块包含在元信息的“文件-数据块”映射表中,则有用;数据块已经无效。因此,删除文件实际上就是删除meta中的“文件-数据块”映射信息(如果想保留一段时间,需要把这个映射信息移到别的地方)。3.小文件的分布式文件系统这样的场景有很多,比如电子商务——那么多的产品图片,个人头像,比如社交网站——那么多的照片,它们的特点可以简单概括如下:每个文件不大;数量极大;多读少写;不会被修改。对于这种业务场景,主流的实现方式还是以大数据块的形式存储,小文件以逻辑存储的形式存在,即在大数据块上的文件元信息记录,以及在哪个数据块上的偏移量和长度是多少,形成一个逻辑上独立的文件。这样既复用了大数据块系统的优势和技术积累,又减少了元信息。4、文件指纹和重复文件指纹是根据文件的内容,通过算法计算出文件的唯一标识。如果两个文件具有相同的指纹,则文件内容相同。在使用网络云盘的时候,发现有时候上传文件的速度很快,这就是文件指纹的作用。云盘服务商通过判断文件的指纹,发现之前已经有人上传过,所以不需要实际上传文件,添加引用即可。在文件系统中,文件指纹可以用来去重,也可以用来判断文件内容是否损坏,或者用来比对文件副本的内容,是一个基本的组成部分。文件指纹的算法也有很多,包括大家熟悉的md5、sha256,还有谷歌针对文本字段的simhash和minhash。11.小结分布式文件系统的内容很复杂,要考虑的问题远不止上面说的那么多,具体实现也比较复杂。本文只是试图从分布式文件系统需要考虑的问题出发,进行简要的分析和设计。如果以后遇到类似的场景需要解决,可以想到“有这样的解决方案”,然后再深入研究。同时,市场上分布式文件系统的形式多种多样。下面是一个研究团队对几种常见的分布式文件系统的设计比较。从这里也可以看出其实有很多选择,GFS论文中的方法并不是最好的。在不同的业务场景下,也可以有更多的选择策略。近期热点文章推荐:1.1,000+Java面试题及答案(2021最新版)2.终于通过开源项目拿到了IntelliJIDEA激活码,太贴心了!3、阿里Mock工具正式开源,秒杀市面上所有Mock工具!4、SpringCloud2020.0.0正式发布,全新颠覆版本!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!
