• 分布式文件系统FastDFS


    简介

    FastDFS 是一个开源的分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了海量数据存储和负载均衡的问题。特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务,如相册网站、视频网站等等。

    FastDFS 作为一款轻量级分布式文件系统,版本 V6.01 代码量 6.3 万行。FastDFS 用 C 语言实现,支持 Linux、FreeBSD、MacOS 等类 UNIX 系统。FastDFS 类似 google FS,属于应用级文件系统,不是通用的文件系统,只能通过专有 API 访问,目前提供了 C 和 Java SDK,以及 PHP 扩展 SDK。

    FastDFS 特点

    • 分组存储,简单灵活;
    • 对等结构,不存在单点;
    • 文件 ID 由 FastDFS 生成,作为文件访问凭证。FastDFS 不需要传统的 name server 或 meta server;
    • 大、中、小文件均可以很好支持,可以存储海量小文件;
    • 一台 storage 支持多块磁盘,支持单盘数据恢复;
    • 提供了 nginx 扩展模块,可以和 nginx 无缝衔接;
    • 支持多线程方式上传和下载文件,支持断点续传;
    • 存储服务器上可以保存文件附加属性。

    FastDFS 系统结构

    在这里插入图片描述

    FastDFS 服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。

    客户端和 Storage server 主动连接 Tracker server。Storage server 主动向 Tracker server 报告其状态信息,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。Storage server 会连接集群中所有的 Tracker server,向他们报告自己的状态。Storage server 启动一个单独的线程来完成对一台 Tracker server 的连接和定时报告。需要说明的是,一个组包含的 Storage server 不是通过配置文件设定的,而是通过 Tracker server 获取到的。

    不同组的 Storage server 之间不会相互通信,同组内的 Storage server 之间会相互连接进行文件同步。

    Storage server 采用 binlog 文件记录文件上传、删除等更新操作。binlog 中只记录文件名,不记录文件内容。

    文件同步只在同组内的 Storage server 之间进行,采用 push 方式,即源头服务器同步给目标服务器。只有源头数据才需要同步,备份数据并不需要再次同步,否则就构成环路了。有个例外,就是新增加一台 Storage server 时,由已有的一台 Storage server 将已有的所有数据(包括源头数据和备份数据)同步给该新增服务器。

    Storage server 中由专门的线程根据 binlog 进行文件同步。为了最大程度地避免相互影响以及出于系统简洁性考虑,Storage server 对组内除自己以外的每台服务器都会启动一个线程来进行文件同步。

    文件同步采用增量同步方式,系统记录已同步的位置(binlog 文件偏移量)到标识文件中。标识文件名格式:{dest storage IP}_{port}.mark,例如:192.168.10.30_23000.mark。

    存储节点(Storage)简介

    存储节点存储文件,完成文件管理的所有功能:存储、同步和提供存取接口,FastDFS 同时对文件的 meta data 进行管理。所谓文件的 meta data 就是文件的相关属性,以键值对(key value pair)方式表示,如:width=1024,其中的 key 为 width,value 为 1024。文件 meta data 是文件属性列表,可以包含多个键值对。

    跟踪器和存储节点都可以由一台多台服务器构成。跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。

    为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷 的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起 到了冗余备份和负载均衡的作用。

    FastDFS 不会对文件进行分块存储,客户端上传的文件和 Storage server 上的文件一一对应。

    存储空间以group内容量最小的storage为准,所以建议group内的多个storage尽量配置相同,以免造成存储空间的浪费。

    为了避免单个目录下的文件数太多,在storage第一次启动时,会在每个数据存储目录里创建2级子目录,每级256个,总共65536个文件,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据直接作为一个本地文件存储到该目录中。

    在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。

    当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。

    FastDFS 中的文件标识分为两个部分:卷名和文件名,二者缺一不可。

    在这里插入图片描述

    上传文件交互过程:

    1. client 询问 tracker 上传到的 storage,不需要附加参数;当集群中不止一个tracker server时,由于tracker之间是完全对等的关系,客户端在upload文件时可以任意选择一个trakcer。
    2. 当tracker接收到upload file的请求时,会为该文件分配一个可以存储该文件的group。支持如下选择group的规则:
      (a)Round robin:所有的group间轮询
      (b)Specified group:指定某一个确定的group
      (c)Load balance:剩余存储空间多多group优先
    3. 当选定group后,tracker会在group内选择一个storage server给客户端,支持如下选择storage的规则:
      (a) Round robin:在group内的所有storage间轮询
      (b)First server ordered by ip:按ip排序
      (c)First server ordered by priority:按优先级排序(优先级在storage上配置)
    4. 当分配好storage server后,客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录,支持如下规则:
      (a) Round robin:多个存储目录间轮询
      (b)剩余存储空间最多的优先
    5. 选定存储目录之后,storage会为文件生一个Fileid,由storage server ip、文件创建时间、文件大小、文件crc32和一个随机数拼接而成,然后将这个二进制串进行base64编码,转换为可打印的字符串。
    6. 当选定存储目录之后,storage会为文件分配一个fileid,每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下。
    7. 当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。
    文件同步过程

    写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。

    每个storage写文件后,同时会写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有server的时钟保持同步。

    storage的同步进度会作为元数据的一部分汇报到tracker上,tracke在选择读storage的时候会以同步进度作为参考。

    比如一个group内有A、B、C三个storage server,A向C同步到进度为T1 (T1以前写的文件都已经同步到B上了),B向C同步到时间戳为T2(T2 > T1),tracker接收到这些同步进度信息时,就会进行整理,将最小的那个做为C的同步时间戳,本例中T1即为C的同步时间戳为T1(即所有T1以前写的数据都已经同步到C上了);同理,根据上述规则,tracker会为A、B生成一个同步时间戳。

    在这里插入图片描述
    下载文件交互过程:

    1. Client发送download请求给某个tracker,必须带上文件名信息(卷名和文件名),
    2. tracke从文件名中解析出文件的group、大小、创建时间等信息,并为该请求选择一个storage用来服务读请求。由于group内的文件同步时在后台异步进行的,所以有可能出现在读到时候,文件还没有同步到某些storage server上,为了尽量避免访问到这样的storage,tracker按照如下规则选择group内可读的storage。
      (a)该文件上传到的源头storage。源头storage只要存活着,肯定包含这个文件,源头的地址被编码在文件名中。
      (b)文件创建时间戳等于storage被同步到的时间戳 且(当前时间-文件创建时间戳) > 文件同步最大时间(如5分钟) 。文件创建后,认为经过最大同步时间后,肯定已经同步到其他storage了。
      (c)文件创建时间戳 < storage被同步到的时间戳。 同步时间戳之前的文件确定已经同步了
      (d)(当前时间-文件创建时间戳) > 同步延迟阀值(如一天)。 经过同步延迟阈值时间,认为文件肯定已经同步了。

    需要说明的是,client 为使用 FastDFS 服务的调用方,client 也应该是一台服务器,它对 tracker 和 storage 的调用均为服务器间的调用。

    小文件合并存储

    将小文件合并存储主要解决如下几个问题:

    1. 本地文件系统inode数量有限,从而存储的小文件数量也就受到限制。
    2. 多级目录+目录里很多文件,导致访问文件的开销很大(可能导致很多次IO)
    3. 按小文件存储,备份与恢复的效率低

    FastDFS在V3.0版本里的机制,可将多个小文件存储到一个大的文件(trunk file),为了支持这个机制,FastDFS生成的文件fileid需要额外增加16个字节。

    1. trunk file id
    2. 文件在trunk file内部的offset
    3. 文件占用的存储空间大小 (字节对齐及删除空间复用,文件占用存储空间>=文件大小)

    每个trunk file由一个id唯一标识,trunk file由group内的trunk server负责创建(trunk server是tracker选出来的),并同步到group内其他的storage,文件存储合并存储到trunk file后,根据其offset就能从trunk file读取到文件。

    文件在trunk file内的offset编码到文件名,决定了其在trunk file内的位置是不能更改的,也就不能通过compact的方式回收trunk file内删除文件的空间。但当trunk file内有文件删除时,其删除的空间是可以被复用的,比如一个100KB的文件被删除,接下来存储一个99KB的文件就可以直接复用这片删除的存储空间。

    HTTP访问支持

    FastDFS的tracker和storage都内置了http协议的支持,客户端可以通过http协议来下载文件,tracker在接收到请求时,通过http的redirect机制将请求重定向至文件所在的storage上;除了内置的http协议外,FastDFS还提供了下载文件的支持。

    其他特性

    FastDFS提供了设置/获取文件扩展属性的接口(setmeta/getmeta),扩展属性以key-value对的方式存储在storage上的同名文件(拥有特殊的前缀或后缀),比如/group/M00/00/01/some_file为原始文件,则该文件的扩展属性存储在/group/M00/00/01/.some_file.meta文件(真实情况不一定是这样,但机制类似),这样根据文件名就能定位到存储扩展属性的文件。

    以上两个接口作者不建议使用,额外的meta文件会进一步“放大”海量小文件存储问题,同时由于meta非常小,其存储空间利用率也不高,比如100bytes的meta文件也需要占用4K(block_size)的存储空间。

    FastDFS还提供appender file的支持,通过upload_appender_file接口存储,appender file允许在创建后,对该文件进行append操作。实际上,appender file与普通文件的存储方式是相同的,不同的是,appender file不能被合并存储到trunk file。

    在 FastDFS 中,客户端上传文件时,文件 ID 不是由客户端指定,而是由 Storage server 生成后返回给客户端的。文件 ID 中包含了组名、文件相对路径和文件名,Storage server 可以根据文件 ID 直接定位到文件。因此 FastDFS 集群中根本不需要存储文件索引信息,这是 FastDFS 比较轻量级的一个例证。而其他文件系统则需要存储文件索引信息,这样的角色通常称作 NameServer。其中 mogileFS 采用 MySQL 数据库来存储文件索引以及系统相关的信息,其局限性显而易见,MySQL 将成为整个系统的瓶颈。

    跟踪器(tracker)简介

    Tracker是FastDFS的协调者,负责管理所有的storage server和group,每个storage在启动后会连接Tracker,告知自己所属的group等信息,并保持周期性的心跳,tracker根据storage的心跳信息,建立group==>[storage server list]的映射表。

    Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。

    问题讨论

    从FastDFS的整个设计看,基本上都已简单为原则。比如以机器为单位备份数据,简化了tracker的管理工作;storage直接借助本地文件系统原样存储文件,简化了storage的管理工作;文件写单份到storage即为成功、然后后台同步,简化了写文件流程。但简单的方案能解决的问题通常也有限,FastDFS目前尚存在如下问题(欢迎探讨)。

    数据安全性

    • 写一份即成功:从源storage写完文件至同步到组内其他storage的时间窗口内,一旦源storage出现故障,就可能导致用户数据丢失,而数据的丢失对存储系统来说通常是不可接受的。
    • 缺乏自动化恢复机制:当storage的某块磁盘故障时,只能换存磁盘,然后手动恢复数据;由于按机器备份,似乎也不可能有自动化恢复机制,除非有预先准备好的热备磁盘,缺乏自动化恢复机制会增加系统运维工作。
    • 数据恢复效率低:恢复数据时,只能从group内其他的storage读取,同时由于小文件的访问效率本身较低,按文件恢复的效率也会很低,低的恢复效率也就意味着数据处于不安全状态的时间更长。
    • 缺乏多机房容灾支持:目前要做多机房容灾,只能额外做工具来将数据同步到备份的集群,无自动化机制。

    存储空间利用率

    • 单机存储的文件数受限于inode数量
    • 每个文件对应一个storage本地文件系统的文件,平均每个文件会存在block_size/2的存储空间浪费。
    • 文件合并存储能有效解决上述两个问题,但由于合并存储没有空间回收机制,删除文件的空间不保证一定能复用,也存在空间浪费的问题

    负载均衡

    • group机制本身可用来做负载均衡,但这只是一种静态的负载均衡机制,需要预先知道应用的访问特性;同时group机制也导致不可能在group之间迁移数据来做动态负载均衡。

    疑难解答

    一、文件同步延迟问题

    客户端将一个文件上传到一台 Storage server 后,文件上传工作就结束了。由该 Storage server 根据 binlog 中的上传记录将这个文件同步到同组的其他 Storage server。这样的文件同步方式是异步方式,异步方式带来了文件同步延迟的问题。新上传文件后,在尚未被同步过去的 Storage server 上访问该文件,会出现找不到文件的现象。FastDFS 是如何解决文件同步延迟这个问题的呢?

    文件的访问分为两种情况:文件更新和文件下载。文件更新包括设置文件附加属性和删除文件。文件的附加属性包括文件大小、图片宽度、图片高度等。FastDFS 中,文件更新操作都会优先选择源 Storage server,也就是该文件被上传到的那台 Storage server。这样的做法不仅避免了文件同步延迟的问题,而且有效地避免了在多台 Storage server 上更新同一文件可能引起的时序错乱的问题。

    二、那么文件下载是如何解决文件同步延迟这个问题的呢?

    要回答这个问题,需要先了解文件名中包含了什么样的信息。Storage server 生成的文件名中,包含了源 Storage server 的 IP 地址和文件创建时间等字段。文件创建时间为 UNIX 时间戳,后面称为文件时间戳。从文件名或文件 ID 中,可以反解出这两个字段。

    然后我们再来看一下,Tracker server 是如何准确地知道一个文件已被同步到一台 Storage server 上的。前面已经讲过,文件同步采用主动推送的方式。另外,每台 storage server 都会定时向 tracker server 报告它向同组的其他 storage server 同步到的文件时间戳。当 tracker server 收到一台 storage server 的文件同步报告后,它会依次找出该组内各个 storage server(后称作为 S)被同步到的文件时间戳最小值,作为 S 的一个属性记录到内存中。

    三、FastDFS 对文件同步延迟问题的解决方案

    答案见:下载文件过程

    四、FastDFS 为什么要结合 Nginx

    我们在使用 FastDFS 部署一个分布式文件系统的时候,通过 FastDFS 的客户端 API 来进行文件的上传、下载、删除等操作。同时通过 FastDFS 的 HTTP 服务器来提供 HTTP 服务。但是 FastDFS 的 HTTP 服务较为简单,无法提供负载均衡等高性能的服务,我们使用 FastDFS 的 Nginx 模块来弥补这一缺陷。
    FastDFS 通过 tracker 服务器,将文件放在 Storage 服务器存储,但是同组之间的服务器需要复制文件,有延迟的问题。假设 tracker 服务器将文件上传到了 192.168.10.32,文件 ID 已经返回客户端,这时后台会将这个文件复制到 192.168.10.33,如果复制没有完成,客户端就用这个 ID 在 192.168.10.33 取文件,肯定会出现错误。而 fastdfs-nginx-module 可以重定向连接到源服务器取文件,避免客户端由于复制延迟的问题出现错误。

  • 相关阅读:
    闪存 64TQFP CY8C6244AZI-S4D92 32 位双核微控制器
    Yolov5代码解析(输入端、BackBone、Neck、输出端))
    macOS上制作arm64的jdk17镜像
    Vue之Jwt的运用(一起探索JWT在Vue中的用途吧)
    数字化转型导师坚鹏:成为数字化转型顾问 引领数字化美好未来
    ffmpeg -sources分析
    【话题】开源大模型与闭源带模型你更看好哪一方
    Visual Studio 2017 安装
    使用uwsgi和gunicorn部署Django项目(智能客服系统)
    NSTextField如何实现字体居中
  • 原文地址:https://blog.csdn.net/qq_37335220/article/details/126617213