# mergerfs **Repository Path**: xiongdsnet/mergerfs ## Basic Information - **Project Name**: mergerfs - **Description**: 汉化处理 - **Primary Language**: C - **License**: ISC - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-09-09 - **Last Updated**: 2024-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README % mergerfs(1) mergerfs 用户手册 # NAME/名称 mergerfs - 一个功能强大的联合文件系统 # SYNOPSIS/简介 mergerfs -o<options> <branches> <mountpoint> # DESCRIPTION/描述 **mergerfs**是一个联合文件系统,旨在简化跨众多商品存储设备的文件存储和管理。 它类似于**mhddfs**, **unionfs** 和 **aufs** 。 # FEATURES/特性 * 可配置行为/文件放置 * 能够随意添加或删除文件系统 * 抵抗单个文件系统故障 * 支持扩展属性(xatrs) * 支持文件属性(chattr) * 运行时可配置(通过xatrs) * 适用于异构文件系统类型 * 写入时文件系统空间不足时移动文件 * 创建文件时忽略只读文件系统 * 将只读文件转换为底层文件的符号链接 * 写时硬链接副本/CoW * 支持POSIX ACL * 其他杂项 # HOW IT WORKS/如何工作 mergerfs在逻辑上将多条路径合并在一起。想象一个集合的联合。通过合并操作或呈现的文件或 目录基于为该特定操作选择的策略。阅读下面有关策略的更多信息。 ``` A + B = C /disk1 /disk2 /merged | | | +-- /dir1 +-- /dir1 +-- /dir1 | | | | | | | +-- file1 | +-- file2 | +-- file1 | | +-- file3 | +-- file2 +-- /dir2 | | +-- file3 | | +-- /dir3 | | +-- file4 | +-- /dir2 | +-- file5 | | +-- file6 | +-- file4 | +-- /dir3 | | | +-- file5 | +-- file6 ``` mergerfs **不** 支持 **aufs** 和 **overlayfs** 中的写时复制(CoW)或白化行为。 您不能挂载只读文件系统并对其进行写入。但是,在创建新文件时,合并将忽略只读文件系统, 因此您可以混合使用读写和只读文件系统。它也不会在文件系统之间分割数据。它不是RAID0/条带化。 它只是其他文件系统的结合。 # TERMINOLOGY/术语 * branch/分支:池中使用的基路径。 * pool/池:合并器挂载。分支机构的联合。 * relative path/相对路径:池中相对于分支和装载的路径。 * function/函数:文件系统调用(打开、取消链接、创建、getattr、rmdir等) * category/类目:基于基本行为(动作、创建、搜索)的函数集合。 * policy/策略:执行函数时用于选择文件的算法。 * path preservation/路径保留:某些策略的一个方面,包括检查创建文件的路径。 # BASIC SETUP/基本安装设置 如果你还不知道如何使用,那么就从以下选项集之一开始。 #### You need `mmap` (used by rtorrent and many sqlite3 base software)/ 您需要`mmap`(用于rtorent和许多sqlite3基础软件) `cache.files=auto-full,dropcacheonclose=true,category.create=mfs` 或者,如果你使用的是Linux内核>=6.6.x,那么当`cache.files=off`时,mergerfs 将启用允许共享mmap的模式。为了确保`cache.files=off`和`cache.fules=auto-full` 之间的最佳性能,您需要进行自己的基准测试,但通常`off`更快。 #### You don't need `mmap` / 您不需要`mmap` `cache.files=off,dropcacheonclose=true,category.create=mfs` ### Command Line /命令行 `mergerfs -o cache.files=auto-full,dropcacheonclose=true,category.create=mfs /mnt/hdd0:/mnt/hdd1 /media` ### /etc/fstab `/mnt/hdd0:/mnt/hdd1 /media mergerfs cache.files=auto-full,dropcacheonclose=true,category.create=mfs 0 0` ### systemd mount / 通过systemd挂载 https://github.com/trapexit/mergerfs/wiki/systemd ``` [Unit] Description=mergerfs service [Service] Type=simple KillMode=none ExecStart=/usr/bin/mergerfs \ -f \ -o cache.files=auto-full \ -o dropcacheonclose=true \ -o category.create=mfs \ /mnt/hdd0:/mnt/hdd1 \ /media ExecStop=/bin/fusermount -uz /media Restart=on-failure [Install] WantedBy=default.target ``` 请参阅mergerfs wiki [mergerfs wiki 中开发部分](https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments),了解真实世界的部署,以获取比较/想法。 # OPTIONS / 选项 无论您是在fstab中还是在配置文件中使用`mergerfs`命令行程序,这些选项都是相同的。 ### mount options / 挂载选项 * **config**: 配置文件的路径。与以下参数相同,采用 key=val /ini样式格式。 * **branches**: 用冒号分隔的分支列表。 * **minfreespace=SIZE**: 用于创建策略的最小空间值。可以被分支特定选项覆盖。 理解“K”、“M”和“G”分别表示千字节、兆字节和千兆字节。(默认值:4G) * **moveonenospc=BOOL|POLICY**: 启用后,如果 **写入** 失败, 出现 **ENOSPC** (设备上没有剩余空间)或 **EDQUOT** (超过磁盘配额), 所选策略将运行以查找文件的新位置。将尝试将文件移动到该分支(保留所有元数据), 如果成功,则取消原始文件的链接并重试写入。(默认值:false,true=mfs) * **inodecalc=passthrough|path-hash|devino-hash|hybrid-hash**: 选择inode计算算法。(默认:hybrid-hash/混合哈希) * **dropcacheonclose=BOOL**: 当一个文件被请求关闭时,首先调用 `posix_fadvise` 来 指示内核我们不再需要这些数据,它可以删除缓存。建议在 **cache.files=partial|full|auto-full|per-process** , 以限制双重缓存。(默认值:false) * **direct-io-allow-mmap=BOOL**: 在较新的内核(>=6.6)上,可以禁用文件页面缓存, 同时仍然允许共享mmap支持。mergerfs将启用此功能(如果可用),但提供了一个选项来关闭 它以进行测试和调试。(默认值:true) * **symlinkify=BOOL**: 启用后,如果文件不可写,并且其mtime或ctime早于**symlinkify_timeout** , 则文件将作为原始文件的符号链接报告。请在使用前阅读以下内容。(默认值:false) * **symlinkify_timeout=UINT**: 激活 **symlinkify** 行为的等待时间(秒)。(默认值:3600) * **nullrw=BOOL**: 将读取和写入转换为无操作。请求将成功,但什么也不做。 可用于对合并函数进行基准测试。(默认值:false) * **lazy-umount-mountpoint=BOOL**: mergerfs将在挂载自身之前尝试 "lazy umount"挂载点。在执行合并功能的实时升级时非常有用。(默认值:false) * **ignorepponrename=BOOL**: 重命名时忽略路径保留。通常,重命名和链接的操作方 式会因创建策略的不同而有所不同(详见下文)。启用此选项将导致重命名和链接始终使用非 路径保留行为。这意味着文件在重命名或链接时将保留在同一文件系统中。(默认值:false) * **export-support=BOOL**: 设置一个低层级FUSE功能,用于指示文件系统可以支持通过NFS导出。(默认值:true) * **security_capability=BOOL**: 如果为false,则在查询xattr security.capability时返回ENOATTR。(默认值:true) * **xattr=passthrough|noattr|nosys**: 对xatrs的运行时控制。 默认情况下是传递xattr请求。'noattr会像什么都不存在一样短路 'nosys' 将以ENOSYS响应, 就像不支持或禁用xatrs一样。(默认值:passthrough) * **link_cow=BOOL**: 启用此选项后,如果打开链接计数>1的常规文件, 它将把文件复制到临时文件并重命名为原始文件。断开链接,提供类似于cow-shell的基本写时复制功能。(默认值:false) * **statfs=base|full**: 控制statfs的工作方式。'base'意味着它将在statfs计算中始终使用所有分支, 'full' 实际上是路径保持,只包括路径存在的分支。(默认值:base) * **statfs_ignore=none|ro|nc**: 'ro' 将导致statfs计算忽略已挂载或标记为 'read-only'或'no create'的分支的可用空间。 'nc'将忽略标记为 'no create' 的分支的可用空间。(默认值:none) * **nfsopenhack=off|git|all**: 一种通过NFS导出合并文件的解决方法,当将模式设置为只读时, 在创建写入文件时会出现问题。(默认设置:off) * **branches-mount-timeout=UINT**: 启动时等待分支成为挂载点文件系统以外的挂载的秒数。(默认值:0) * **follow-symlinks=never|directory|regular|all**: 将符号链接转换为它们所指向的对象。(默认值:never) * **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**: 当链接失败时,EXDEV可以选择创建一个指向文件的符号链接。 * **rename-exdev=passthrough|rel-symlink|abs-symlink**: 当重命名失败时, EXDEV可以选择将文件移动到一个特殊目录并将符号链接到该目录。 * **readahead=UINT**: 如果大于0,则为合并函数和分支设置readahead(以千字节为单位)。(默认值:0) * **posix_acl=BOOL**: 启用POSIX ACL支持(如果内核和底层文件系统支持)。(默认值:false) * **async_read=BOOL**: 异步执行读取。如果禁用或不可用,内核将确保每个文件句柄最多 有一个待处理的读取请求,并尝试按偏移量对请求进行排序。(默认值:true) * **fuse_msg_size=UINT**: 设置每条FUSE消息的最大页数。仅在Linux>=4.20上可用, 否则忽略。(最小值:1;最大值:256;默认值:256) * **threads=INT**: 要使用的线程数。单独使用时(`process-thread-count=-1`), 它设置读取和处理FUSE消息的线程数。当一起使用时,它设置从FUSE读取的线程数。 当设置为零时,它将尝试发现和使用逻辑核的数量。如果线程数设置为负数,它将查找核数, 然后除以绝对值。即,8芯机器上的线程=-2将导致8/2=4个线程。总是至少有一个线程。 如果结合进程线程数设置为-1,则它将尝试根据CPU线程数选择合理的值。注意:线程数量越多, 并行性就越高,但通常会降低吞吐量。(默认值:0) * **read-thread-count=INT**: 线程`threads`的别名。. * **process-thread-count=INT**: 允许单独的线程池异步处理FUSE请求。在此模式下, `read-thread-count`是指读取FUSE消息的线程数,这些线程被分派给处理线程-1表示禁用,否则就像读取 线程数一样。(默认值:-1) * **process-thread-queue-depth=UINT**: 设置任何单个进程线程一次可以排队的请求数。 这意味着队列的总内存使用量是队列深度乘以进程线程数加上读取线程数。0将深度设置为与进程线程数相同。(默认值:0) * **pin-threads=STR**: 选择将线程固定到CPU的策略(默认值:unset) * **flush-on-close=never|always|opened-for-write**: 关闭文件时刷新数据缓存。 主要用于启用回写或合并网络文件系统时。(默认:opened-for-write) * **scheduling-priority=INT**: 设置合并器的调度优先级。有效值范围为-20到19。 有关更多详细信息,请参阅`setpriority`手册页。(默认值:-10) * **fsname=STR**: 设置文件系统的名称,如**mount**、**df**等。默认为连接在一起的源路径列表, 并删除最长的公共前缀。 * **func.FUNC=POLICY**: 设置特定FUSE函数的策略。请参阅下面的值类型列表。示例: **func.getattr=newest** * **func.readdir=seq|cosr|cor|cosr:INT|cor:INT**: 设置`readdir`策略。INT值设置用于并发的线程数。(默认值:seq) * **category.action=POLICY**: 设置action(操作)类别中所有FUSE函数的策略。(默认值:epall) * **category.create=POLICY**: 设置create(创建)类别中所有FUSE函数的策略。(默认值:epmfs) * **category.search=POLICY**: 设置search(搜索)类别中所有FUSE函数的策略。(默认值:ff) * **cache.open=UINT**: 'open' 策略缓存超时(秒)。(默认值:0) * **cache.statfs=UINT**: 'statfs' 缓存超时(秒)。(默认值:0 * **cache.attr=UINT**: 文件属性缓存超时(秒)。(默认值:1) * **cache.entry=UINT**: 文件名查找缓存超时(秒)。(默认值:1) * **cache.negative_entry=UINT**: 负文件名查找缓存超时(秒)。(默认值:0) Negative file name lookup cache timeout in seconds. (default: 0) * **cache.files=libfuse|off|partial|full|auto-full|per-process**: 文件页面缓存模式(默认:libfuse) * **cache.files.process-names=LIST**: 一个由管道|分隔的进程[命令](https://man7.org/linux/man-pages/man5/proc.5.html)名列表,用于在 `cache.files=per-process`时启用页面缓存。(默认值:“rtorent | qbittorrent nox”) * **cache.writeback=BOOL**: 启用writeback(内核写回)缓存(默认值:false) * **cache.symlinks=BOOL**: 缓存symlinks(符号链接)(如果内核支持)(默认值:false) * **cache.readdir=BOOL**: 缓存readdir(如果内核支持)(默认值:false) * **parallel-direct-writes=BOOL**: 允许内核为用`cache.files=per-process`(如果进程不在进程名称中) 或 `cache.files=off`打开的文件分派多个并行(非扩展)写入请求。(这需要内核支持,并在v6.2中添加) * **direct_io**: 已弃用-绕过页面缓存。请改用`cache.files=off`。(默认值:false) * **kernel_cache**: 已弃用-打开文件时不要使数据缓存无效。请改用`cache.files=full` 。(默认值:false) * **auto_cache**: 已弃用-如果文件时间或大小发生变化,则使数据缓存无效。请改用`cache.files=auto-full`。(默认值:false) * **async_read**: 已弃用-异步执行读取。请改用`async_read=true`。 * **sync_read**: 已弃用-同步执行读取。请改用`async_read=false`。 * **splice_read**: 已弃用-什么都不做。 * **splice_write**: 已弃用-什么都不做。 * **splice_move**: 已弃用-什么都不做。 * **allow_other**: 弃用-mergerfs v2.35.0及更高版本在以root身份运行时会自动设置此FUSE选项。 * **use_ino**: 已弃用-mergerfs应始终控制索引节点计算,因此始终启用此功能。 **注意:** 选项按照列出的顺序进行评估,因此如果选项为 **func.rmdir=rand,category.action=ff** , 则 **action** 类别设置将覆盖**rmdir** 设置。 **注意:** 请务必查看您正在使用的mergerfs版本的文档。旧版本中并非所有功能都可用。 使用`man mergerfs`或查找发布中链接的文档。 #### Value Types /值类型 * BOOL = 'true' | 'false' * INT = [MIN_INT,MAX_INT] * UINT = [0,MAX_INT] * SIZE = 'NNM'; NN = INT, M = 'K' | 'M' | 'G' | 'T' * STR = 字符串 (可能引用枚举值,请参阅参数的详细信息) * FUNC = 文件系统功能 * CATEGORY = 功能归类 * POLICY = mergerfs 功能策略 ### branches/分支 分支参数是一个冒号('`:`') 要合并在一起的分隔路径列表。路径是在相同还是不 同的文件系统上并不重要,文件系统类型也不重要(在合理的范围内)。同一文件系统上的 路径的已用和可用空间不会重复,底层文件系统不支持的任何功能(如文件属性或扩展属 性)都将返回相应的错误。 分支目前有两个可以设置的选项。一种影响分支是否包含在策略计算和单个最小自由空 间值中的类型。这些值是通过在分支名称的末尾添加一个`=` 并使用逗号作为分隔符来设置的。 示例:`/mnt/drive=RW,1234` #### branch mode/分支模式 * RW: (读/写)-默认行为。将符合所有政策类别的资格。 * RO: (只读)-将被排除在`create`(创建)和`action`(操作)策略之外。与只读挂载的文件系统相同(尽管处理速度更快)。 * NC: (不创建)-将被排除在`create`(创建)策略之外。您无法在该分支上创建,但可以更改或删除。 #### minfreespace/最小自由空间 与全局选项的目的和语法相同,但特定于分支。如果未设置,则使用全局值。 #### globbing/通配符绑定 为了更容易包含多个分支,mergerfs支持[通配符](http://linux.die.net/man/7/glob)。 **通过shell使用通配符标记时必须转义,否则shell本身将应用原始字符**。 ``` # mergerfs /mnt/hdd\*:/mnt/ssd /media ``` 上面的行将使用/mnt中以**hdd**和**ssd**为前缀的所有挂载点。 要在启动时安装池或通过相关工具访问池,请使用 **/etc/fstab**。 ``` # /mnt/hdd*:/mnt/ssd /media mergerfs minfreespace=16G 0 0 ``` **注意**:**globbing/通配符绑定**是在装载时完成的,或者是在使用运行时API进行更新时完成的。如果事后添加了与glob匹配的新目录,则不会自动包含该目录。 **注意**:要通过**fstab**进行安装,您必须安装**mount.fuse**。对于Ubuntu/Debian,它包含在**fuse**包中。 ### inodecalc/索引节点计算 索引节点(st_ino)是文件系统中的唯一标识符。每个挂载的文件系统都有设备ID(st_dev), 它们可以在整个系统上唯一标识一个文件。同一设备上具有相同索引节点的条目实际上是对同一 底层文件的引用。名称和索引节点之间存在多对一的关系。然而,由于目录的复杂性,它们在 大多数系统上没有多个链接。 FUSE允许服务器(mergerfs)设置索引节点值,但不允许设置设备ID。在合并的情况下, 创建索引节点值有点复杂,因为文件并不真正在其控制之下。如果策略更改了要选择的目录或 文件,或者发生了带外更改,则不清楚应该使用什么值。大多数软件并不关心值是什么,但如 果值意外更改,那些关心的值通常会中断。如果工具`find`看到目录inode发生变化,它将中止 目录遍历。如果索引节点在带外更改,NFS可能会返回过时的句柄错误。文件去重工具通常会 利用设备ID和索引节点作为搜索重复文件的快捷方式,如果发现不同的索引节点值,则会进行 完整的文件比较。 mergerfs提供了多种计算索引节点的方法,希望涵盖不同的用例。 * passthrough: 传递底层inode值。主要用于测试,因为使用此方法并不能解决上述任何问题, 并且可能会混淆文件重复数据删除软件,因为来自不同文件系统的索引节点可能是相同的。 * path-hash: 路径哈希,对相关条目的相对路径进行哈希运算。基础文件的值完全被忽略。这意味 着该文件路径的inode值将始终相同。当您使用NFS并在带外进行更改(例如在分支之间复制数据) 时,这很有用。这也意味着,指向同一文件的条目将无法通过索引节点识别。这并不意味着硬链接不 起作用。它们会起作用的。 * path-hash32: 32位版本的path-hash。 * devino-hash: 对底层条目的设备id和索引节点进行哈希处理。如果策略选择不同的文件或文件移出 带外,这不会阻止NFS的问题,但会为同样移出带外的底层文件提供相同的索引节点。 * devino-hash32: 32位版本的devino-hash。 * hybrid-hash: 对目录执行路径哈希,对其他文件类型执行devino哈希。由于目录不能有硬链接, 因此静态值不会产生影响,文件将获得用于查找重复项的值。如果不使用NFS,可能是最好的选择。因此, 这是默认设置。 * hybrid-hash32: 32位版本的hybrid-hash. 提供32位版本是因为有些软件不能很好地处理64位索引节点。 虽然在数百万个条目的测试中存在哈希冲突的风险,但没有发生任何冲突。与典型的文件系统不同,FUSE文件 系统可以重用索引节点,而不引用相同的条目。FUSE中用于引用文件的内部标识符与提供的inode值不同。前 者是`nodeid` ,实际上是一个由2个64位值组成的元组:`nodeid` 和`generation`。此元组不是面向客 户端的。呈现给客户端的inode未经解释地通过内核传递。 从FUSE文档中获取 `use_ino`: ``` Honor the st_ino field in the functions getattr() and fill_dir(). This value is used to fill in the st_ino field in the stat(2), lstat(2), fstat(2) functions and the d_ino field in the readdir(2) function. The filesystem does not have to guarantee uniqueness, however some applications rely on this value being unique for the whole filesystem. Note that this does *not* affect the inode that libfuse and the kernel use internally (also called the "nodeid"). ``` 从2.35.0版本开始, `use_ino` 选项已被删除。mergerfs应该始终管理inode值。 ### pin-threads 固定读取和/或进程线程的简单策略。如果未启用进程线程,则该策略仅适用于读取线程。无效值将被忽略。 * R1L: 所有读线程都固定在一个逻辑CPU上。 * R1P: 所有读线程都固定在一个物理CPU上。 * RP1L: 所有读取和处理线程都固定在一个逻辑CPU上。 * RP1P: 所有读取和处理线程都固定在一个物理CPU上。 * R1LP1L: 所有读取线程固定到单个逻辑CPU,所有进程线程固定到(如果可能的话)不同的逻辑CPU。 * R1PP1P: 所有读取线程固定到单个物理CPU,所有进程线程固定到(如果可能的话)不同的逻辑CPU。 * RPSL: 所有读取和处理线程都分布在所有逻辑CPU上。 * RPSP: 所有读取和处理线程都分布在所有物理CPU上。 * R1PPSP: 所有读取线程都固定在一个物理CPU上,而进程线程则分布在所有其他物理CPU上。 ### fuse_msg_size FUSE应用程序通过一个特殊的字符设备与内核通信:`/dev/fuse`。与FUSE相关的大部分开销是在该设备 上在用户空间和内核空间之间来回切换的成本。一般来说,所需的行程越少,性能就越好。减少行程次数可 以通过多种方式实现。内核级缓存和增加消息大小是两个重要的问题。当涉及到读写时,如果消息大小加倍, 则行程数量大约减半。 在Linux 4.20中,添加了一个新功能,允许协商最大消息大小。由于大小是 [pages/页面](https://en.wikipedia.org/wiki/Page_(computer_memory)) 的倍数,因此该功 能称为`max_pages`。最大`max_pages`值为256(1MiB),最小值为1(4KiB)。Linux>=4.20使用的默 认值,以及4.20之前使用的硬编码值,是32(128KiB)。在mergerfs中,它被称为`fuse_msg_size`, 以明确它的影响并提供一些抽象。 由于增加`fuse_msg_size` / `max_pages`应该没有缺点,除了由于消息缓冲区较大而导致的RAM使用量略 有增加外,mergerfs默认值为256。在4.20之前的内核上,该值没有影响。该值可配置的原因是为了进行实验和 基准测试。有关示例,请参阅基准部分。 ### follow-symlinks / 跟随符号连接 启用此功能后,将导致符号链接被合并函数解释为其目标(取决于模式)。 当有针对文件的getattr/stat请求时,mergerfs将检查该文件是否是符号链接,并根据以下符号链接设置将符 号链接的信息替换为它所指向的信息。 当取消链接或rmdir链接后,它将删除符号链接本身,而不是它指向的符号链接。 * never: 表现得像正常人一样。Symlinks就是这样对待的。 * directory: 只解析指向目录的符号链接。 * regular: 只解析指向常规文件的符号链接。 * all: 将所有符号链接解析为它们指向的符号链接。 不指向任何东西的符号链接保持原样。 警告:此功能有效,但可能还发现了边缘情况。如果你发现任何奇怪的行为,请在 [github](https://github.com/trapexit/mergerfs/issues)上提交一张工单。 ### link-exdev 如果使用路径保留,并且EXDEV的`link`(链接)失败,则调用`symlink`(符号链接), 其`target`(目标)是 `oldlink`(旧链接),`linkpath`(链接路径)是 `newpath`(新路径),其 `target`(目标)值由`link-exdev`的值决定。 * passthrough: 正常返回EXDEV。 * rel-symlink: `newpath`(新路径)的相对路径。. * abs-base-symlink: 使用基础分支的绝对值。 * abs-pool-symlink: 使用mergerfs挂载点的绝对值。 注意:某些应用程序可能会检查它们链接的文件。在这些情况下,它可能会出错或报警。 ### rename-exdev 如果在EXDEV中使用路径保留和`rename`(重命名)失败: 1. 将文件从 **/branch/a/b/c** 移动到 **/branch/.mergerfs_rename_exdev/a/b/c** 。 2. 将重命名的`newpath` (新路径)符号链接到移动的文件。 `target`(目标)值由`rename-exdev`的值决定。 * passthrough: 正常返回EXDEV。 * rel-symlink: `newpath`(新路径)的相对路径。 * abs-symlink: 使用mergerfs挂载点的绝对值。 注意:某些应用程序可能会检查它们重命名的文件。在这种情况下,它可能会出错或抱警。 注意:`abs-symlink`没有像`link-exdev`那样拆分为两个,是因为存在多个 `oldpaths` (旧路径)时管理绝对基符号链接的复杂性。 ### symlinkify 由于mergerfs和底层技术FUSE引入的间接性,性能可能会出现不同程度的下降。此功能将在mtime和 ctime超过超时时间后,将不可写的非目录转换为`readlink`策略找到的原始文件的符号链接。 **警告**:当前实现存在一个已知问题,即如果文件在转换为符号链接时打开并正在使用,则打开该文 件的应用程序在使用时将收到错误。这在实践中不太可能发生,但要记住。 **警告**:某些备份解决方案(如CrashPlan)不备份符号链接的目标。如果使用此功能,则有必要将 任何备份软件指向原始文件系统,或者将软件配置为follow-symlinks / 跟随符号连接(如果有此选项)。 或者,创建两个挂载。一个用于备份,一个用于一般消费。 ### nullrw 由于FUSE的工作原理,向FUSE文件系统发出的所有请求都会产生开销,而内核内文件系统则不存在。这意味 着即使是一个简单的转移也会有一些放缓。然而,与底层I/O的成本相比,通常开销最小。通过禁用底层I/O, 我们可以测试理论性能边界。 通过启用`nullrw`,mergerfs将一如既往地工作,**除了**所有的读取和写入都将是无操作。写入将成功 (写入的大小将被返回,就像写入成功一样),但mergerfs不会对给定的数据进行任何处理。同样,读取将返 回请求的大小,但不会触及缓冲区。 有关如何测试的建议,请参阅基准部分。 ### xattr 运行时扩展属性支持可以通过`xattr`选项进行管理。默认情况下,它将传递任何xattr调用。鉴于xattr支持 很少使用,并且可能对性能产生重大影响,mergerfs允许在运行时禁用它。性能问题主要出现在启用文件缓存时。 内核将在*每次写入之前*发送一个用于`security.capability` 的`getxattr`。它不会缓存对任何 `getxattr`的响应。这可能会在未来得到解决,但目前mergerfs只能提供以下解决方法。 `noattr`将导致合并器将所有xattr调用短路,并在适当的情况下返回ENOATTR。mergerfs仍然会收到 所有请求,但它们不会被转发到底层文件系统。运行时控件仍将在此模式下运行。 `nosys`将导致合并函数为任何xattr调用返回ENOSYS。与noattr的不同之处在于,内核将缓存这一事实, 并自行缩短未来的调用。这比`noattr`更有效,但会导致合并程序通过隐藏文件的运行时控制停止工作。 ### nfsopenhack NFS不完全符合POSIX标准,并且历史上某些行为(例如使用O_EXCL打开文件)不受支持或不受很好的支持。 当通过NFS导出mergerfs(或任何FUSE文件系统)时,由于NFS和FUSE的交互方式,会出现一些问题。 此黑客方式解决了创建具有只读模式但具有读/写或只写标志的文件的问题。通常这是完全有效的,但NFS会将 一个打开的调用拆分为多个调用。翻译的确切方式取决于NFS服务器和客户端的配置和版本,但这会导致权限 错误,因为不允许普通用户将只读文件作为可写文件打开。 尽管这是一个更小众的情况,但这种黑客破坏了正常的安全和行为,因此默认情况下是关闭的。如果设置为`git`, 则只有当所讨论的路径包含`/.git/`时,它才会执行黑客攻击。`all`(所有)这些都会导致它在打开空的只读文 件进行写入时应用。 ### export-support 理论上,此标志不应暴露给最终用户。它是一个低级FUSE标志,指示内核是否可以向其发送某些类型的消息, 以便与NFS一起使用。mergerfs确实支持这些消息,但由于内核和mergerfs中存在的错误和怪癖,提供此 选项只是为了调试需要。 鉴于此标志是在首次启动FUSE连接时设置的,因此在运行时无法更改。 # FUNCTIONS, CATEGORIES and POLICIES / 功能、类别和策略 POSIX文件系统API由许多函数组成。**creat**, **stat**, **chown**等。为了便于在合并中进行配置, 大多数核心功能分为3类:**action**(操作), **create**(创建),和 **search**(搜索) 这些功能和类别可以分配一个策略,该策略规定在执行该功能时选择哪个分支。 以下类别 `N/A`中列出的某些功能无法分配正常策略。这些函数使用文件句柄,而不是由`open`或`create` 创建的文件路径。也就是说,很多时候,当客户端调用 `fgetattr`, `fchown`, `fchmod`,`futimens`, `ftruncate`等时,当前的FUSE内核驱动程序并不总是提供文件句柄。这意味着它将调用常规的、基于路径的 版本。`statfs`的行为可以通过其他选项进行修改。 当使用基于分支可用空间的策略时,将使用提供的基本路径。不是所讨论文件的完整路径。这意味着在空间计算中 不会考虑分支中的挂载。原因是它不适用于非路径保持策略,并可能导致不明显的行为。 注:虽然任何政策都可以分配给一个职能或类别,但有些政策在实践中可能不是很有用。例如: **rand**(random) 可能对文件创建(create)有用,但如果用于`chmod`,如果文件有多个副本,则可能会导致非常奇怪的行为。 ### Functions and their Category classifications /功能及其类别分类 | 分类 | FUSE 功能 | |----------|-------------------------------------------------------------------------------------| | action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens | | create | create, mkdir, mknod, symlink | | search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink | | N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write, copy_file_range | 在可能搜索某些内容(如克隆路径)的情况下,通常会使用**getattr**。 ### Policies / 策略 策略是一种算法,用于为功能选择一个或多个分支,或者通常选择功能的行为方式。 如果需要,`create`类别中的任何函数都会克隆相对路径。其他一些函数(`rename`,`link`,`ioctl`) 有特殊要求或行为,您可以在下面阅读更多信息。 #### Filtering /过滤 大多数策略基本上都是搜索分支,并为功能创建一个文件/路径列表。该策略负责过滤和排序分支。过滤器包括 **minfreespace**、分支是否以只读方式挂载以及分支标记(RO、NC、RW)。这些过滤器适用于大多数策略。 * 没有**search**(搜索)功能策略筛选器。 * 所有**action**(动作)功能策略都会筛选出**read-only**(只读)挂载或标记为**RO(只读)**的分支。 * 所有**create**(创建)功能策略都会筛选出**read-only**(只读)挂载、标记为**RO(只读)**或 **NC (不创建)**,或可用空间小于`minfreespace`的分支。 策略可能有自己的附加过滤,例如需要存在现有路径的过滤。 如果所有分支都被过滤,将返回错误。通常,**EROFS**(只读文件系统)或**ENOSPC**(设备上没有剩余空间) 取决于过滤分支的最新原因。如果没有找到符合条件的分支机构,将返回**ENOENT**。 如果**create**, **mkdir**, **mknod**或 **symlink**失败,出现`EROFS`或其他基本错误,则mergerfs 将标记任何发现为只读的分支(IE将设置`RO`模式),并重新运行策略并重试。这主要适用于`ext4`文件系统,当遇到错 误时,它可能会突然变为只读。 #### Path Preservation /路径保持 如下所述,政策有两种基本分类。 `path preserving`(路径保持)和`non-path preserving`(非路径保持)。 所有以`ep`开头的策略 (**epff**, **eplfs**, **eplus**,**epmfs**, **eprand**)都是 `path preserving`(路径保持)的。`ep`代表现有路径。 路径保持策略将仅考虑已存在所访问的相对路径的分支。 使用非路径保持策略时,路径将根据需要克隆到目标分支。 使用 `msp`或 `most shared path`(大多数共享路径策略),它们被定义为路径保持,用于控制`link`(链接) 和`rename`(重命名)的行为,因为 `ignorepponrename` 可用于禁用该行为。 #### Policy descriptions / 策略描述 如上所述,策略的行为因其使用的功能而异。有时,提供某些政策可能真的没有意义,因为它们与 其他政策实际上是相同的,但它使事情变得更加统一。 | 策略 | 描述 | |------------------|------------------------------------------------------------| | all | Search(搜索):对于**mkdir**, **mknod**和**symlink**,它将应用于所有分支。 **create**工作类似 **ff**。| | epall (existing path——现有路径, all) | 对于**mkdir**, **mknod**, 和**symlink** 它将应用于所有找到的路径。**create**工作类似**epff** (但更昂贵,因为它在找到有效分支后不会停止)。| | epff (existing path——现有路径, first found——首次找到) | 根据在挂载时定义或在运行时配置的分支顺序,对相对路径所在的第一个分支进行操作 | | eplfs (existing path——现有路径, least free space-最小可用空间) | 在存在相对路径的所有分支中,选择自由空间最小的分支。 | | eplus (existing path——现有路径, least used space-最小使用空间) | 在存在相对路径的所有分支中,选择使用空间最少的分支。 | | epmfs (existing path——现有路径, most free space-最大可用空间) | 在存在相对路径的所有分支中,选择具有最大自由空间的分支。| | eppfrd (existing path——现有路径, percentage free random distribution-无百分比随机分布) | 与**pfrd**类似,但仅限于现有路径| | eprand (existing path——现有路径, random-随机) | 调用**epall**,然后随机化。返回1。| | ff (first found——首次找到) | 根据在挂载时定义或在运行时配置的分支顺序,对找到的第一个分支进行操作。 | | lfs (least free space-最小可用空间) | 选择可用空间最少的分支。 | | lus (least used space-最小使用空间) | 选择使用空间最少的分支。 | | mfs (most free space-最大可用空间) | 选择可用空间最大的分支。| | msplfs (most shared path-共享路径最多, least free space-可用空间最少) | 与**eplfs** 类似,但如果找不到分支,它将使用父目录重试。继续这种模式,直到找到一个。 | | msplus (most shared path-共享路径最多, least used space-最小使用空间) | 与 **eplus**类似, 但如果它找不到分支,它将使用父目录重试。继续这种模式,直到找到一个。 | | mspmfs (most shared path-共享路径最多, most free space-最大可用空间) | 与**epmfs**类似, 但如果它找不到分支,它将使用父目录重试。继续这种模式,直到找到一个。 | | msppfrd (most shared path-共享路径最多, percentage free random distribution) | 与 **eppfrd**类似,但如果它找不到分支,它将使用父目录重试。继续这种模式,直到找到一个。 | | newest | 选择具有最大mtime的文件/目录。 | | pfrd (percentage free random distribution-无百分比随机分布) | 随机选择一个分支,选择的可能性基于分支相对于总空间的可用空间。 | | rand (random-随机) | 调用**all** 然后随机化。返回1个分支。 | **注意:**如果您使用的是保留块的底层文件系统,如ext2、ext3或ext4,请注意,mergerfs在策略计算中使用`f_bavail`(非特权用户的可用块数)而不是`f_bfree`(可用块数来)来尊重保留。**df** 不使用`f_bavail`,它使用`f_bfree`,因此直接比较**df**输出和 mergerfs策略是不合适的。 #### 默认值 | 类别 | 策略 | |----------|--------| | action | epall | | create | epmfs | | search | ff | #### func.readdir 例如: `func.readdir=seq`, `func.readdir=cor:4` `readdir` 有策略来控制它如何管理读取目录内容。 | 策略 | 描述 | |--------|-------------| | seq | "sequential-顺序队列" :按定义的顺序迭代分支。这是引入readdir策略之前的默认和传统行为。| | cosr | "concurrent open, sequential read-并发打开,顺序读取" : 使用线程池同时打开分支目录,并按定义顺序处理它们。这使内存和CPU使用率保持较低水平,同时也减少了等待分支响应的时间。线程数默认为逻辑核数。可以通过语法`func.readdir=cosr:N`覆盖,其中`N` 是线程数。| | cor | "concurrent open and read-并发打开和读取" : 同时打开分支目录,并立即开始使用线程池读取其内容。这将导致内存和CPU使用率略高,但延迟减少。特别是在使用延迟较高/速度较慢的网络文件系统分支时。与 `seq`和 `cosr`不同,由于线程池的异步特性,文件的顺序可能会发生变化。线程数默认为逻辑核数。可以通过语法 `func.readdir=cor:N` 覆盖,其中`N`是线程数。 | 请记住,`readdir`主要只是提供目录中的文件名列表,可能还提供有关所述文件的一些基本元数据。要了解有关文 件的详细信息,正如人们从`find` 或`ls`等命令中看到的那样,需要对由 `fuse.getattr`控制的文件调用`stat`。 #### ioctl 当`ioctl`与打开的文件一起使用时,它将使用在原始`open`(打开)调用时创建的文件句柄。但是,当将`ioctl`与目录一起使用时 ,mergerfs将使用`open`(打开)策略来查找要操作的目录。 #### rename & link —— 重命名 与 链接 #### **注意:**如果在移动/重命名/链接文件时收到软件错误,则应考虑将创建策略更改为**不**保留路径的策略, 启用`ignorepponrename`,或联系违规软件的作者并要求正确处理`EXDEV` (跨设备/不正确链接)。 `rename`(重命名) 和 `link`(链接)是联合文件系统中的棘手功能。重命名仅在单个文件系统或设备中有效。如果由于源路径 和目标路径存在于不同的装载点上而无法原子地进行重命名,则它将返回**-1**,**errno = EXDEV**(跨设备/不正确的链接)。 因此,如果重命名的源和目标位于池中的不同文件系统上,则会产生问题。 最初,只要以任何方式跨目录请求重命名,mergerfs都会返回EXDEV。这使得代码变得简单,并且在技术上符合POSIX要求。 然而,许多应用程序根本无法处理EXDEV,将其视为正常错误或处理不当。这些应用程序包括:gvfsd-fuse v1.20.3及更早版本、 Apple OSX 10.9+中的Finder/CIFS/SMB客户端、NZBGet、Samba的回收箱功能。 因此,为了让大多数软件在遵守 mergerfs规则的同时正常工作,双方达成了妥协。下面是基本逻辑。 * 如果使用试图保留目录路径(epff、eplfs、eplus、epmfs)的**create**(创建)策略 * 使用**rename**(重命名)策略获取要重命名的文件列表 * 对于每个文件尝试重命名: * 如果ENOENT失败(没有这样的文件或目录),请运行**create**策略 * 如果创建策略返回的分支与当前评估的分支相同,则克隆路径 * 重新尝试重命名 * 如果**任何**重命名成功,则更高级别的重命名被视为成功 * 如果重命名**失败**,将返回遇到的第一个错误 * 关于成功: * 从所有没有源文件的分支中删除目标 * 从所有重命名失败的分支中删除源 * 如果使用**不**尝试保留目录路径的**创建**策略 * 使用**重命名**策略获取要重命名的文件列表 * 使用**getattr**策略获取目标路径 * 对于每个文件尝试重命名: * 如果源分支!=目标分支: * 将目标路径从目标分支克隆到源分支 * 重命名 * 如果**任何**重命名成功,则更高级别的重命名被视为成功 * 如果重命名**失败**,将返回遇到的第一个错误 * 关于成功: * 从所有没有源文件的分支中删除目标 * 从所有重命名失败的分支中删除源 清除须经过正常的权利检查。 上述行为将有助于将返回EXDEV的可能性降至最低,但仍然有可能。 **link**使用相同的策略,但没有删除。 #### statfs / statvfs #### [statvfs](http://linux.die.net/man/2/statvfs)根据片段大小对源文件系统进行规范化, 并将调整后的块和索引节点的数量相加。这意味着您将看到所有源的组合空间。总计、已使用和免费。 然而,源是基于文件系统进行数据消除的,因此同一驱动器上的多个源不会导致重复计算其空间。在 检查挂载的统计信息时,将不包括在分支树下游挂载的其他文件系统。 选项`statfs` 和 `statfs_ignore`可用于修改`statfs`行为。 #### flush-on-close https://lkml.kernel.org/linux-fsdevel/20211024132607.1636952-1-amir73il@gmail.com/T/ 默认情况下,FUSE会在发布文件描述符之前发出刷新。这被认为有点激进,并添加了一个功能, 使FUSE服务器能够选择何时发生这种情况。 选项: * always——总是 * never——从不 * opened-for-write——打开进行写入 目前,它默认为“opened-for-write”,这比添加此功能之前的行为不那么激进。这应该不是问题, 因为刷新实际上只在写入文件时才相关。鉴于刷新对未来的许多文件系统来说是不相关的,因此可以 添加一个特定于分支的标志,这样只有在特定分支上打开的文件才会在关闭时被刷新。 # ERROR HANDLING / 错误处理 POSIX文件系统功能提供了一个单一的返回代码,这意味着在处理多个分支时,像mergerfs一样存在 一些复杂性。它试图以一种通常会为特定功能返回有意义值的方式处理错误。 ### chmod, chown, removexattr, setxattr, truncate, utimens 等操作的错误处理 1) 如果无错:返回 0(表示操作成功) 2) 如果不成功: 返回第一个错误 3) 如果操作的文件之一与相关搜索功能相同:返回其值 4) 返回 0 (成功) 虽然这样做会增加错误处理的复杂性和成本,特别是步骤3,但这可能提供了最合理的返回值。 ### unlink, rmdir 等操作的错误处理 1) 如果无错:返回 0(表示操作成功) 2) 如果不成功: 返回第一个错误 如果发生任何成功,旧版本的mergerfs将返回成功,但对于unlink和rmdir,下游假设虽然并非 不可能发生,但可能会使一些软件混淆。 ### 其他错误处理 对于搜索功能,总是有一个单一的对象被操作,因此返回来自单个功能调用的任何返回值。 对于创建函数`mkdir`, `mknod`, 和`symlink`,它们不返回文件描述符,因此可以具有`all`有 或`epall`策略,如果任何调用成功,它将返回成功,否则将返回错误。 # INSTALL / 安装 https://github.com/trapexit/mergerfs/releases 如果您的发行版的包管理器包含mergerfs,请检查版本是否为最新版本。如果已过期,建议使用发布页 面上的最新版本。常见发行版的详细信息如下。 #### Debian 大多数Debian安装都是稳定的分支,因此没有最新的软件。虽然mergerfs可以通过apt获得,但建议用户 从[发布页面](https://github.com/trapexit/mergerfs/releases)安装最新版本。 #### 预构建deb ``` wget https://github.com/trapexit/mergerfs/releases/download//mergerfs_.debian-_.deb dpkg -i mergerfs_.debian-_.deb ``` #### apt ``` sudo apt install -y mergerfs ``` #### Ubuntu 大多数Ubuntu安装都是稳定的分支,因此没有最新的软件。虽然mergerfs可以通过apt获得, 但建议用户从[发布页面](https://github.com/trapexit/mergerfs/releases)安装最新版本。。 #### 预构建deb ``` wget https://github.com/trapexit/mergerfs/releases/download//mergerfs_.ubuntu-_.deb dpkg -i mergerfs_.ubuntu-_.deb ``` #### apt ``` sudo apt install -y mergerfs ``` #### Raspberry Pi OS 实际上与Debian或Ubuntu相同。 #### Fedora ``` wget https://github.com/trapexit/mergerfs/releases/download//mergerfs-.fc..rpm sudo rpm -i mergerfs-.fc..rpm ``` #### CentOS / Rocky ``` wget https://github.com/trapexit/mergerfs/releases/download//mergerfs-.el..rpm sudo rpm -i mergerfs-.el..rpm ``` #### ArchLinux 1. 支持 AUR 2. 安装 `mergerfs` #### 其他 静态二进制文件是为本地包不可用的情况提供的。 ``` wget https://github.com/trapexit/mergerfs/releases/download//mergerfs-static-linux_.tar.gz sudo tar xvf mergerfs-static-linux_.tar.gz -C / ``` # BUILD / 构建 **注意:** 预构建的软件包可以在上找到,并推荐给大多数用户: https://github.com/trapexit/mergerfs/releases **注意:** 仅支持标记的版本。`master`和其他分支机构应被视为正在进行的工作。 首先, 从 [github](https://github.com/trapexit/mergerfs)获取代码。 ``` $ git clone https://github.com/trapexit/mergerfs.git $ # or $ wget https://github.com/trapexit/mergerfs/releases/download//mergerfs-.tar.gz ``` #### Debian / Ubuntu ``` $ cd mergerfs $ sudo tools/install-build-pkgs $ make deb $ sudo dpkg -i ../mergerfs__.deb ``` #### RHEL / CentOS / Rocky / Fedora ``` $ su - # cd mergerfs # tools/install-build-pkgs # make rpm # rpm -i rpmbuild/RPMS//mergerfs-..rpm ``` #### Generic 已经安装 git, g++, make, python 。 ``` $ cd mergerfs $ make $ sudo make install ``` #### Build options / 构建选项 ``` $ make help usage: make make USE_XATTR=0 - build program without xattrs functionality /构建没有xatrs功能的程序 make STATIC=1 - build static binary /构建静态链接程序 make LTO=1 - build with link time optimization /使用链路时间优化进行构建 ``` # UPGRADE / 升级 mergerfs可以通过挂载到前一个实例之上进行实时升级。只需安装新版本的mergerfs并按照以下说明操作即可。 再次运行mergerfs,或者如果使用/etc/fstab调用,则再次挂载它。现有的打开文件等将继续正常工作,尽管 它们不会看到运行时的更改,因为任何此类更改都是新的挂载。如果您计划使用新装载更改设置,则应该/可以在 装载新版本之前应用这些设置。 ``` $ sudo mount /mnt/mergerfs $ mount | grep mergerfs media on /mnt/mergerfs type mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other) media on /mnt/mergerfs type mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other) ``` 这种方法的一个问题是,即使使用它的软件停止或重新启动,底层实例也会继续运行。为了解决这个问题,你可以使用 "lazy umount(懒卸载)"。在使用mergerfs issue的新实例进行顶部挂载之前: `umount -l `。 或者,您可以通过设置选项`lazy-umount-mountpoint=true`来让mergerfs执行此操作。 # RUNTIME INTERFACES / 运行时接口 ## RUNTIME CONFIG / 运行时配置 #### .mergerfs 伪文件 #### ``` /.mergerfs ``` 装载点有一个伪文件,允许在运行时修改某些合并选项。该文件不会显示在**readdir**中,但可以通过[{list,get,set}xattrs](http://linux.die.net/man/2/listxattr)调用方式进行**stat/查询**和操作。 运行时所做的任何更改都不会持久化。如果您希望值持久化,则必须在配置**mergerfs**挂载的任何地方将其作为选项包含在内(/etc/fstab)。 ##### Keys / 键名 ##### 使用`getfattr -d /mountpoint/.mergerfs`或`xattr -l /mountpoint/.mergerfs`查看所有支持的键。有些是信息性的,因此是只读的。 `setxattr`将在只读键上返回EINVAL(无效参数)。 ##### Values / 键值 ##### 与命令行相同。 ###### user.mergerfs.branches ###### 用于查询或修改分支列表。修改时,有几个快捷方式可以轻松操作列表。 | 值 | 描述 | |--------------|-------------| | [list] | 设置 | | +<[list] | 预置 | | +>[list] | 添加 | | -[list] | 删除提供的所有值 | | -< | 删除列表中第一个值 | | -> | 删除列表中最后一个值 | `xattr -w user.mergerfs.branches +用户空间往返,FUSE的成本相对较高,因此有用于文件条目和属性的内核端缓存。条目缓存将`lookup` 调用限制为询问文件是否存在的mergerfs请求。属性缓存限制了对提供文件属性(模式、大小、类型等)的mergerfs 函数进行`getattr`调用的需要。与页面缓存一样,如果底层文件系统同时被操纵,则不应使用这些缓存,因为这可能 会导致奇怪的行为或数据损坏。设置这些选项的选项是条目缓存的 `cache.entry`和`cache.negative_entry`以 及属性缓存的`cache.attr` 。 `cache.negative_entry`是指对查找(不存在的文件)的否定响应的超时时间。 #### writeback caching / 回写缓存 启用`cache.files`后,默认情况下它会执行直写缓存。这种行为无助于提高性能,因为每次写入仍然是通过文件系统 进行的。通过启用FUSE回写缓存,内核可以聚合少量写,然后将其作为一个更大的请求发送给 mergerfs请求。这可以大 大提高低效写入文件的应用程序的吞吐量。内核可以聚合的量受到FUSE消息大小的限制。有关更多详细信息,请阅读 `fuse_msg_size`部分。 启用回写缓存会产生一个小的副作用。基础文件永远不会用O_APPEND或O_WRONLY打开。前者是因为内核随后管理追加模式, 后者是因为内核可能会从mergerfs请求文件数据以填充写缓存。O_APPEND更改意味着,如果文件在mergerfs程序之外更改, 则可能会导致损坏,因为内核不会知道文件的结尾已更改。也就是说,任何时候使用缓存时,都应该避免同时使用mergerfs之 外的同一文件。 请注意,如果应用程序正确调整写入大小,则回写缓存将几乎没有影响。它只会帮助小于FUSE消息大小的写入(旧内核为128K, 新内核为1M)。 #### statfs 缓存 在mergerfs使用的系统调用策略中,`statfs` / `statvfs`调用可能是最昂贵的。它用于找出文件系统的可用空间以及它是否以 只读方式挂载。根据设置和使用模式,这些查询的成本可能相对较高。启用 `cache.statfs` 后,策略对`statfs`的所有调用都将 缓存其设置的秒数。 示例:如果创建策略为`mfs`,超时为60,则在这60秒内,将返回与创建目标相同的文件系统,因为在这段时间内不会更新可用空间。 #### symlink 缓存 从4.20版本开始,Linux支持符号链接缓存。在使用大量符号链接的工作负载中可以显著提高性能。设置`cache.symlinks=true` 将导致仅在支持的情况下从内核请求符号链接缓存。因此,在4.20之前的系统上启用它是安全的。也就是说,目前默认情况下它是禁用的。 您可以通过查询xattr `user.mergerfs.cache.symlinks`来查看是否启用了缓存,但由于必须在启动时请求缓存,因此无法在运行 时更改它。 #### readdir 缓存 从4.20版本开始,Linux支持readdir缓存。这可能会对目录遍历产生重大影响。尤其是与条目(`cache.entry`)和属性(`cache.attr`) 缓存结合使用时。设置`cache.readdir=true`将导致在每个`opendir`上从内核请求readdir缓存。如果内核不支持readdir缓存,则将选项 设置为`true`无效。此选项可在运行时通过xattr `user.mergerfs.cache.readdir`进行配置。 #### tiered / 分层 缓存 一些存储技术支持所谓的“分层”缓存。将通常较小、较快的存储作为透明缓存放置到较大、较慢的存储中。 例如,NVMe、SSD、Optane在传统HDD之前。 mergerfs本身不支持任何类型的分层缓存。大多数用户不需要这样的功能,它的包含会使代码复杂化。 然而,在某些情况下,缓存文件系统可以帮助进行典型的 mergerfs 设置。 1. 快速网络,慢速文件系统,许多读取器:您有一个10+Gbps的网络,有许多读取器,而您的常规文件系统无法跟上。 2. 快速的网络、缓慢的文件系统、小的突发写入:您有一个10+Gbps的网络,希望传输的数据量少于缓存文件系统,但希望快速传输。 对于#1,你是否应该使用mergerfs是有争议的。RAID可能是更好的解决方案。如果要使用mergerfs,还有其他策略可能会有所帮助: 在文件系统之间传播数据(请参阅mergerfs.dup工具),设置`func.open=rand`,使用`symlinkify`,或使用dm-cache或类似技 术向底层设备添加分层缓存。 使用#2也可以使用dm-cache,但还有另一种解决方案,只需要mergerfs和cronjob。 1. 创建2个mergerfs池。一个仅包含慢速设备,另一个同时包含快速设备(SSD、NVME等)和慢速设备。 2. “缓存”池应首先列出缓存文件系统。T 3. 用于“缓存”池的最佳`create`策略可能是 `ff`, `epff`, `lfs`和 `eplfs`。后两种假设是缓存文件系统远小于备份文件系统。 如果使用路径保留策略,请记住,您需要手动创建希望缓存的路径的核心目录。确保权限同步。使用`mergerfs.fsck`检查/纠正它们。 您还可以将慢速文件系统模式设置为`NC`,但这意味着如果缓存文件系统填满,您将收到“空间不足”错误。 4. 启用`moveonenospc`并适当设置`minfreespace`。为了确保“慢速”池上有足够的空间,您可能希望将 `minfreespace`设置为至 少与最大缓存文件系统的大小一样大,如果不是更大的话。这样,在最坏的情况下,整个缓存文件系统都可以移动到其他驱动器。 5. 将程序设置为使用缓存池。 6. 保存以下脚本之一或创建自己的脚本。 7. 使用`cron` (作为root)以适合您工作流程的任何频率安排命令 ##### 基于时间的到期 仅根据上次访问文件的时间将文件从缓存移动到备份池。如果你想要几分钟而不是几天,请用 `-amin`替换 `-atime`。 可能希望使用rsync的 `fadvise`/`--drop-cache`版本,或使用工具"nocache"运行rsync。 *注意:* 这些脚本的参数包括缓存**文件系统**本身。不是带有缓存文件系统的池。如果源是缓存池,则可能会丢失数据。 [mergerfs.time-based-mover](tools/mergerfs.time-based-mover?raw=1) ##### 到期百分比 将最旧的文件从缓存移动到备份池。继续,直到低于百分比阈值。 *注意:* 这些脚本的参数包括缓存**文件系统**本身。不是带有缓存文件系统的池。如果源是缓存池,则可能会丢失数据。 [mergerfs.percent-full-mover](tools/mergerfs.percent-full-mover?raw=1) # 性能表现 mergerfs的核心只是一个代理,因此其理论上的最大性能是底层设备的性能。然而,鉴于它是一个从用户空间工作的FUSE 文件系统,与基于内核的解决方案相比,开销会增加。也就是说,性能可以与理论最大值相匹配,但这在很大程度上取决于 系统的配置。特别是在将网络文件系统添加到混合中时,有许多变量会影响性能。设备速度和延迟、网络速度和延迟,一般 并发性、读/写大小等。不幸的是,考虑到变量的数量,很难找到一组提供最佳性能的设置。如果您有性能问题,请查看以 下建议(包括基准测试部分) 注意:在更改这些功能之前,一定要阅读这些功能,以了解它可能会影响哪些行为 * 禁用 `security_capability` 和/或 `xattr` * increase cache timeouts `cache.attr`, `cache.entry`, `cache.negative_entry` * 启用 (或禁用) 页面缓存 (`cache.files`) * 启用 `parallel-direct-writes` * 启用 `cache.writeback` * 启用 `cache.statfs` * 启用 `cache.symlinks` * 启用 `cache.readdir` * 更改工作线程的数量 * 禁用 `posix_acl` * 禁用 `async_read` * 使用`nullrw`或安装ram磁盘测试理论性能 * 如果您的数据基本上是静态和只读的,请使用`symlinkify` * 使用分层缓存设备 * 在HDD前面有SSD时使用LVM和LVM缓存 * 增加预读: `readahead=1024` 如果您遇到对性能有重大影响的设置,请联系trapexit,以便他进一步调查。请根据您的正常设置(单个分支)和`nullrw=true`进行测试 # 基准管理 文件系统很复杂。他们做了很多事情,其中许多是相互关联的。此外,操作系统、驱动程序、硬件等都会影响性能。因此,在进行基准测试时, **有必要**尽可能缩小测试的范围。 对于大多数吞吐量来说,关键的基准是吞吐量。测试吞吐量dd是有用的,但**必须**使用正确的设置,以确保文件系统或设备真正被测试。操作系统 可以并且将会缓存数据。如果不强制同步读写和/或禁用缓存,返回的值将无法代表设备的真实性能。 当通过mergerfs函数进行基准测试时,请确保只使用一个分支,以消除策略使情况复杂化的任何可能性。首先对底层文件系统进行基准测试,然后在 其上挂载mergerfs并再次测试。如果你遇到的速度低于预期,你需要精确地缩小导致减速的因素。最好按所列顺序测试以下内容(但不要组合)。 1. 使用`nullrw=true`启用nullrw模式。这将有效地使读写操作无效。从等式中删除底层设备/文件系统。这将为我们提供最高的理论速度。 2. 在`tmpfs`上挂载mergerfs。`tmpfs`是一个RAM磁盘。极高的速度和极低的延迟。这是一个更现实的最佳情况。 示例: `mount -t tmpfs -o size=2G tmpfs /tmp/tmpfs` 3. 在本地设备上安装mergerfs。NVMe、SSD、HDD等。如果你有不止一个,我建议测试它们中的每一个,因为驱动器和/或控制器(它们的驱动程序) 可能会影响性能。 4. 最后,如果您打算将mergerfs与网络文件系统一起使用,无论是作为数据源还是通过mergerfs将其与另一个文件系统结合使用,请如上所述单独测 试每个文件系统。 一旦你发现有性能问题的组件,你可以用不同的选项进行进一步的测试,看看它们是否会影响性能。对于读写操作,最相关的是:`cache.files`, `async_read`。使用NFS或某些文件系统时,不太可能但相关的是`security_capability`、`xattr`和`posix_acl`。如果你发现某个特定 的系统、设备、文件系统、控制器等性能不佳,请联系trapexit,以便他进一步调查。 有时问题实际上是应用程序通过mergerfs访问或写入数据。一些软件使用较小的缓冲区大小,这可能会导致更多的请求,从而产生更大的开销。您可以 自己测试一下,将下面示例中的`bs=1M`替换为`ibs`或`obs` ,并使用512而不是1M的大小。在一个使用`nullrw`的示例测试中,当从`1M`移动到 `512`时,写入速度从4.9GB/s下降到69.7MB/s。测试读数时也得到了类似的结果。利用写缓存可以改善小的写开销,但在偶然的测试中几乎没有发现 任何收益。在该功能可用之前,还需要进行更多的测试。如果你有一个应用程序在mergerfs时看起来很慢,这可能是由于这个原因。联系 trapexit , 以便他进一步调查。 ### write benchmark / 写基准 ``` $ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=fdatasync status=progress ``` ### read benchmark / 读基准 ``` $ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress ``` ### other benchmarks 其他基准 如果你试图对其他行为进行基准测试,你必须确保在运行前清除内核缓存。事实上,在读写基准测试之前运行也是一件好事,以防万一。 ``` sync echo 3 | sudo tee /proc/sys/vm/drop_caches ``` # 提示/注意事项 * 这份文件是逐字逐句的。如果未提及可疑特征,则该特征不存在。如果未列出某些libfuse参数,则可能不应使用它们。 * 确保您使用的是最新版本。 * 以`root`身份运行mergerfs。mergerfs旨在以`root`身份运行,否则可能会出现不正确的行为。 * 如果你没有看到一些你期望的目录和文件,策略似乎会跳过分支,你会遇到奇怪的权限错误等。请确保底层文件系统的权限 都是相同的。使用`mergerfs.fsck`审核文件系统的不同步权限。 * 如果您仍然存在权限问题,请确保您使用的是符合POSIX ACL的文件系统。mergerfs通常不会对FAT、NTFS或其他非POSIX文件系统进行例外处理。 * 如果您希望应用程序(如rtorrent)使用 [mmap](http://linux.die.net/man/2/mmap)文件,请**不要**使用`cache.files=off`。禁 用页面缓存的FUSE当前不支持共享mmap。当`cache.files=partial|full|auto-full`时,建议启用`dropcacheonclose`。 * [Kodi](http://kodi.tv), [Plex](http://plex.tv), [Subsonic](http://subsonic.org)等可以使用目录[mtime](http://linux.die.net/man/2/stat)更有效地确定是否扫描新内容, 而不是简单地执行完整扫描。如果使用默认的**getattr**策略**ff** ,这些程序可能会错过更新,因为它返回了找到的第一个目录的**stat**信息, 而这是另一个最近更新了**mtime**的挂载上的一个较新目录。要解决此问题,您需要设置**func.getattr=newest**。请记住,这只是静态的。如 果稍后**open**或**unlink**文件,并且这些文件的策略不同,则可以对完全不同的文件或目录进行操作。 * 某些策略与某些功能混合可能会导致奇怪的行为。并不是说这些行为和竞争条件中的一些不能在**mergerfs**之外发生,而是因为试图 **mergerfs** 多个数据源而更有可能发生,这些数据源可能因不同的策略而不同步。 * 为了保持一致性,通常最好设置类别范围的策略,而不是单个**func**。这将有助于减少[rsync](http://linux.die.net/man/1/rsync)等工具的混淆。 然而,如果需要,灵活性是存在的。 # 已知问题/错误 #### 内核问题和错误 [https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs](https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs) #### 目录mtime未更新 记住`getattr`的默认策略是`ff`。将返回找到的第一个目录的信息。如果不是已更新的目录,那么它将显得过时。 这是默认设置的原因是,任何其他策略都会更昂贵,对于许多应用程序来说,这是不必要的。要始终返回具有最新mtime 或基于所有找到值的伪造值的目录,需要扫描所有文件系统。 如果您总是希望从具有最新mtime的目录中获取目录信息,那么请使用`getattr`的`newest`策略。 #### 'mv /mnt/pool/foo /mnt/disk1/foo' 删除 'foo' 这不是bug。 在详细模式下运行以更好地了解发生了什么: ``` $ mv -v /mnt/pool/foo /mnt/disk1/foo copied '/mnt/pool/foo' -> '/mnt/disk1/foo' removed '/mnt/pool/foo' $ ls /mnt/pool/foo ls: cannot access '/mnt/pool/foo': No such file or directory ``` `mv`在跨设备工作时,将源复制到目标,然后删除源。由于在这种情况下源**就是**目标,根据取消链接策略,它将删除刚刚 复制的文件和跨分支的其他文件。 如果你想将文件移动到一个文件系统,只需将它们复制到那里,然后使用mergerfs.dedup清理旧路径或直接手动将其从分支中删除。 #### 缓存内存似乎比应该的要大 使用`cache.files=off`和/或`dropcacheonclose=true`。请参阅页面缓存部分。 #### NFS客户端返回ESTALE/Stale文件句柄 NFS通常不喜欢带外更改。有关更多详细信息,请查看[#remote-filesystems](#remote-filesystems)中关于NFS的部分。 #### rtourrent因ENODEV(无此类设备)而失败 请确保为设置`cache.files=partial|full|auto-full|per-processe`。rtorent和其他一些应用程序使用 [mmap](http://linux.die.net/man/2/mmap) 来读写文件,并且不提供传统方法的回退。FUSE在使用`direct_io` 时目前不支持mmap。在关闭`direct_io`的情况下写入可能会产生性能损失以及双重缓存的问题,但这是使此类应用程序正常 工作的唯一方法。如果性能损失对其他应用程序来说太高,您可以两次挂载mergerfs。一次启用`direct_io`,一次不启用。 如果不使用`direct_io`请确保设置`dropcacheonclose=true`。 #### Plex不适用于mergerfs功能 确实如此。如果您试图将Plex的配置/元数据/数据库放在mergerfs服务器上,则无法设置`cache.files=off`,因为Plex正 在使用启用mmap的sqlite3。当页面缓存被禁用时,Linux的FUSE实现不支持共享mmap。要修复此问题,请将数据放在其他位置 (首选)或启用 `cache.files`(`dropcacheonclose=true`)。Sqlite3不需要mmap,但如果mmap失败,开发人员需要回 退到标准IO。 这适用于其他软件:Radarr、Sonarr、Lidarr、Jellyfin等。 我建议联系您遇到问题的软件的开发人员,并要求他们在mmap不可用时向常规文件IO添加回退。 如果问题是扫描似乎无法拾取介质,那么一定要设置`func.getattr=newest`,不过一般来说,完整扫描仍会拾取所有介质。 #### 当程序试图移动或重命名文件时,它会失败 请阅读上面关于[rename & link(重命名和链接)](#rename--link)的部分。 问题是,许多应用程序没有正确处理`EXDEV`错误,即使`rename`(重命名)和`link`(链接)是完全有效的情况,也可能返回, 这些情况并不表示实际的设备、文件系统或操作系统错误。只有使用上述策略部分中描述的路径保留策略时,mergerfs才会返回错 误。如果您不关心路径保留,只需将mergerfs策略更改为非路径保留版本即可。例如:`-o category.create=mfs`理想情况下, 有问题的软件会得到修复,建议您在遇到此问题时联系软件的作者,请求正确处理`EXDEV`错误。 #### 我的32位软件有问题 某些软件在64位索引节点值方面存在问题。这些症状可能包括尝试列出文件时出现EOVERFLOW错误。您可以通过将`inodecalc` 设置 为相关部分中描述的基于32位的算法之一来解决这个问题。 #### Samba: 移动文件/目录失败 解决方法:复制文件/目录,然后删除原始文件而不是移动。 这不是Samba的问题,而是一些SMB客户端的问题。GVFS fuse v1.20.3及更早版本(在Ubuntu 14.04等版本中发现)未能正确处理 某些错误代码。特别是当调用跨越挂载点时,通过 **rename** (重命名)返回的 **EXDEV** 中的 **STATUS_NOT_SAME_DEVICE** 。 当一个程序得到 **EXDEV** 时,它需要明确地采取一个替代动作来实现其目标。在 **mv** 或类似情况下,它会尝试 **rename**(重命名), 在 **EXDEV** 上,它会退回到在两个位置之间手动复制数据并解除源链接。在这些旧版本的GVFS引信中,如果收到 **EXDEV** ,它会将其转 换为 **EIO** 。这将导致 **mv** 或大多数试图在该SMB共享上移动文件的应用程序因IO错误而失败。 [GVFS-fuse v1.22.0](https://bugzilla.gnome.org/show_bug.cgi?id=734568)及以上版本修复了此问题,但大量系统使用旧版本。 在Ubuntu上,可以通过发出`apt-cache showpkg gvfs-fuse`来检查版本。2015年发布的大多数发行版似乎都有更新版本,可以正常工作, 但旧系统可能无法正常工作。升级gvfs-fuse或一般的发行版将解决这个问题。 在苹果的MacOSX 10.9中,他们用自己的产品取代了Samba(客户端和服务器)。他们的新客户端似乎也不处理 **EXDEV** ,并且对Linux上 gvfs的旧版本有类似的响应。 #### 回收文件偶尔会失败 这与Samba的问题相同。重命名返回`EXDEV`(在我们的例子中,这只会在`epmfs`等路径保持策略中发生),软件不能很好地处理这种情况。不幸 的是,这是移动文件的软件的常见故障。该标准表明,实现`MAY`选择支持非用户主目录对文件的垃圾清理(这是`MUST`)。该实现 `MAY`还支持 "top directory trashes"(顶级目录垃圾),许多人可能会这样做。 创建 `$topdir/.Trash` 。标准中定义的垃圾箱目录使用[mergerfs-tools](https://github.com/trapexit/mergerfs-tools) 工具 `mergerfs.mktrash`。 #### 补充用户组 由于[getgroups/setgroups](http://linux.die.net/man/2/setgroups) 的开销,mergerfs使用缓存。此缓存是机会主义的,每个线程 都有。当特定线程需要更改凭据时,每个线程都会查询用户的补充组,并在线程的生命周期内保留这些数据。这意味着,如果将用户添加到组中,则在 不重新启动mergerfs程序的情况下,可能无法获取该用户。然而,由于高层FUSE API(至少是标准版本)的线程池动态地增长和收缩,因此随着时 间的推移,线程可能会被杀死,稍后一个没有缓存的新线程将启动并查询新数据。 gid缓存使用固定存储来简化设计,并与可能没有C++11编译器的旧系统兼容。有足够的存储空间容纳256个用户的补充组。每个用户最多允许32个补充组。 Linux>=2.6.3允许每个用户最多65535个组,但大多数其他*nix允许的组要少得多。NFS只允许16个。系统确实能很好地处理溢出。如果用户有超过32 个补充组,则只使用前32个。如果发现一个未缓存的用户时,有超过256个用户正在使用该系统,它将随机驱逐现有用户的缓存。只要活跃用户不超过256 人,这应该没问题。如果任何一个值都太低,不符合您的需求,您将不得不修改`gidcache.hpp`以增加值。请注意,这样做会增加每个线程所需的内存。 虽然不是一些用户在使用容器时发现的错误,但容器内定义的补充组在权限方面无法正常工作。这是意料之中的,因为mergerfs位于容器之外,因此正在 查询主机的组数据库。可能有一种破解方法可以解决这个问题(使mergerfs读取容器中的/etc/group文件),但它尚未实现,并且仅限于 Linux和/etc/group数据库。用户最好将主机组文件中的文件挂载到容器中,或者使用标准的共享用户和组技术,如NIS或LDAP。 # 远程文件系统 许多用户询问与远程文件系统的兼容性。本节将描述在将mergerfs用于常见的远程文件系统时出现的任何已知问题或怪癖。 请记住,与缓存一样,在[out-of-band/带外更改](https://en.wikipedia.org/wiki/Out-of-band)远程文件系统的内容不是一个好主意。 这意味着您真的不应该更改承载远程文件系统的服务器上的底层文件系统或合并文件的内容。如果多个程序试图同时写入或读取相同的数据,这样做可 能会导致奇怪的行为、不一致、错误,甚至数据损坏。这并不是说你不能做到这一点,也不是说数据可能会损坏,但这是有可能发生的。最好始终使用 远程文件系统。甚至在为其服务的机器上。 ## NFS [NFS](https://en.wikipedia.org/wiki/Network_File_System) 是Unix/POSIX系统上常见的远程文件系统。由于NFS的工作原理, 需要设置一些设置才能使合并器使用它。 应该指出的是,由于FUSE(和mergerfs)中的某些设计选择,NFS和FUSE(mergerfs使用的技术)不能完美地相互配合。由于这些问题,通常 建议在可能的情况下使用SMB,直到情况发生变化。也就是说,mergerfs通常应作为NFS的导出,发现的问题仍应报告。 为确mergerfs和NFS之间的兼容性,请使用以下设置。 mergerfs设置: * noforget * inodecalc=path-hash NFS导出设置: * fsid=UUID * no\_root\_squash `noforget`是必要的,因为NFS使用`name_to_handle_at`和`open_by_handle_at`函数,这些函数允许程序保留对文件的引用,而无需在技 术上打开它。问题是FUSE无法知道NFS有一个句柄,稍后将使用该句柄再次打开文件。因此,内核可能会告诉mergerfs忘记节点,如果NFS将来要求提 供该节点的详细信息,它将没有任何响应。让节点永远存在并不理想,但目前是管理这种情况的唯一方法。 因为NFS对带外更改很敏感,所以必需设置`inodecalc=path-hash`。FUSE不关心文件的inode值是否发生变化,但NFS是有状态的,会发生变化。 因此,如果你使用默认的索引节点计算算法,那么如果你更改了一个文件或更新了一个目录,合并器将使用的文件可能会在不同的分支上,因此索引节 点也会发生变化。这不是一个理想的解决方案,正在考虑其他解决方案,但它适用于大多数情况。 因为FUSE文件系统没有不同的`st_dev`值,所以需要设置`fsid=UUID`,这可能会在导出时导致问题。最简单的方法是将每个mergerfs导出`fsid` 设置为某个随机值。生成随机值的一种简单方法是使用命令行工具`uuid`或`uuidgen`,或通过 [uuidgenerator.net](https://www.uuidgenerator.net/)等网站。 `no_root_squash`不是严格必要的,但如果启用了根压缩,可能会导致混淆的权限和所有权问题。 ## SMB / CIFS [SMB](https://en.wikipedia.org/wiki/Server_Message_Block) 是Microsoft Windows系统最常用的协议,用于共享文件共享、打印机等。 然而,由于Windows的普及,它也在包括Linux在内的许多其他平台上得到支持。在Linux上支持SMB最流行的方式是通过Samba软件。 [Samba](https://en.wikipedia.org/wiki/Samba_(software)),和其他通过SMB为Linux文件系统提供服务的方式应该可以很好地与mergerfs 配合使用。这些服务不倾向于使用NFS使用的相同技术,因此没有相同的问题。在Samba中使用mergerfs不需要特殊设置。然而,CIFSD和其他程序尚未经 过广泛测试。如果您将mergerfs与CIFSD或其他SMB服务器一起使用,请提交您的经验,以便更新这些文档。然而, [CIFSD](https://en.wikipedia.org/wiki/CIFSD) 和其他程序尚未经过广泛测试。如果您将mergerfs与CIFSD或其他SMB服务器一起使用,请提 交您的经验,以便更新这些文档。 ## SSHFS [SSHFS](https://en.wikipedia.org/wiki/SSHFS) 是一个利用SSH作为连接和传输层的FUSE文件系统。虽然与NFS或Samba相比,设置通常更简单, 但性能可能不足,项目处于维护模式。 将sshfs与mergerfs一起使用没有已知问题。您可能希望使用以下参数来提高性能,但您的程度可能会有所不同。 * `-o Ciphers=arcfour` * `-o Compression=no` 更多信息可以在[这里](https://ideatrash.net/2016/08/odds-and-ends-optimizing-sshfs-moving.html)找到。 ## 其它 还有其他远程文件系统,但没有一个被广泛用于为mergerfs。如果你使用上面没有列出的东西,请随时联系我,我会把它添加到列表中。 # FAQ #### How well does mergerfs scale? Is it "production ready?"/ mergerfs支持的规模有多大?“支持生产环境吗?” 用户报告说,从Raspberry Pi到具有>20个内核的双插槽Xeon系统,都在运行mergerfs。我知道至少有几家公司在生产中使用合并。 [Open Media Vault](https://www.openmediavault.org) 包括mergerfs作为其池化文件系统的唯一解决方案。mergerfs的 作者让它运行了300多天,管理着16台以上的设备,具有相当高的24/7读写使用率。仅在机器电源耗尽后停止。 大多数严重的问题(崩溃或数据损坏)都是由于[内核错误(kernel bugs)](https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs) 造成的。所有这些都在稳定版本中得到了修复。 #### Can mergerfs be used with filesystems which already have data / are in use? /mergerfs可以与已经有数据/正在使用的文件系统一起使用吗? 对。mergerfs实际上只是一个代理, **不** 会干扰它管理的文件系统/挂载/路径的正常形式或功能。它只是另一个用户领域的应用程序, 正在扮演一个中间人的角色。它不能做任何其他随机软件不能做的事情。 mergerfs **不** 是控制底层块设备的传统文件系统。mergerfs **不** 是RAID。它 **不** 会操纵通过它的数据。 它 **不** 会在文件系统之间分片数据。它只是将一些行为分片并聚合其他行为。 #### Can drives/filesystems be removed from the pool at will? /可以随意从池中删除驱动器/文件系统吗? 对。请参阅上一个问题的答案。 #### Can mergerfs be removed without affecting the data? /是否可以在不影响数据的情况下删除合并符? 对。请参阅上一个问题的答案。 #### Can drives/filesystems be moved to another pool? /驱动器/文件系统可以移动到另一个池吗? 对。请参阅上一个问题的答案。 #### How do I migrate data into or out of the pool when adding/removing drives/filesystems? /在添加/删除驱动器/文件系统时,如何将数据移入或移出池? 你不需要。看上一个问题的答案。 #### How do I remove a drive/filesystem but keep the data in the pool? /如何删除驱动器/文件系统,但将数据保留在池中? 不需要做任何特别的事情。从mergerfs的配置中删除分支,并将数据从删除的文件系统复制(rsync)到池中。实际上, 就像你将数据从一个文件系统传输到另一个文件系一样。 如果您希望在执行传输时继续使用该池,只需创建另一个没有相关文件系统的临时池,然后复制数据。在执行此操作之前,最好将分支设置为 `RO` , 以确保在执行复制时没有新内容写入文件系统。 #### What policies should I use? /我应该使用什么策略? 除非你正在做一些更小众的事情,否则普通用户最好在`category.create`使用`mfs` 。 它将根据可用空间将文件分散到各个分支中。如果你想尝试更多地托管数据,请使用`mspmfs`。 如果您喜欢稍微不同的数据分布(如果您有较小和较大的文件系统的混合),您可能需要使用`lus`。 一般来说,`mfs`、`lus`甚至`rand`都适用于一般用例。如果你从一个不平衡的池开始, 你可以使用工具 **mergerfs.balance** 在池中重新分发文件。 如果你真的想尝试根据目录托管文件,你可以将`func.create`设置为`epmfs` 或类似的, 将`func.mkdir`设置为 `rand`或`eprand` ,具体取决于你是想一般托管还是在特定分支上托管。 无论哪种方式,托管的 **需求** 都很少见。例如:如果您希望定期删除设备,并希望数据可预测地位于该设 备上,或者如果您根本不使用备份,也不希望零碎地替换该数据。在这种情况下,使用路径保护可能会有所帮助, 但需要一些人工注意。事后共置化可以使用 **mergerfs.consolidate** 工具完成。如果您不需要`ep` 策略提供的严格托管,那么您可以使用基于`msp`的策略,该策略将返回路径,直到找到一个有效的分支。 最终没有正确的答案。这是一种偏好或基于某种特定需求。mergerfs非常易于测试和实验。我建议创建一个测 试设置并进行实验,以了解你想要什么。 `epmfs`是默认类别。创建策略是因为`ep`策略不会改变分支的总体布局。它不会将 文件/目录 放置在没有相对分支的分支上。 因此,它使系统保持在已知状态。停止使用`epmfs` 或在文件系统周围重新分发文件比将其整合回来容易得多。 #### What settings should I use? / 我应该使用什么设置? 这取决于你想要什么功能。一般来说,没有“错误”的设置。所有设置都与性能或功能相关。最好的办法是阅读可用的选项, 并选择适合你情况的选项。如果文档中有不清楚的地方,请联系我们,文档将得到改进。 也就是说,对于普通人来说,以下几点应该没问题: `cache.files=off,dropcacheonclose=true,category.create=mfs` #### Why are all my files ending up on 1 filesystem?! / 为什么我的所有文件最终都在一个文件系统上?! 你是从空文件系统开始的吗?您是否明确配置了`category.create`策略?您是否使用`existing path` / `path preserving`(现有的路径/路径保留)策略? 默认的创建策略是 `epmfs`。这是一种路径保持算法。使用这样的`mkdir`和`create`策略,当创建第一个目录时,它将只 选择一个文件系统。在第一个目录中创建的任何文件或目录都将放置在同一个分支上,因为它保留了路径。 这让许多新用户措手不及,但更改默认值会破坏许多现有用户的设置,而此策略是最安全的策略,因为它不会改变现有文件系统 的总体布局。如果您不关心路径保留,并希望您的文件分布在所有文件系统中,请更改为`mfs`或上述类似策略。如果您确实希 望保留路径,则需要在传输数据之前,在希望数据登录的文件系统上手动创建路径。设置`func.mkdir=epall`可以简化 `create`(创建)路径保留的管理。或者,如果您只想按文件系统对目录内容进行分组,请使用 `func.mkdir=rand` 。 #### Do hardlinks work? / 硬链接有效吗? 对。另请参阅选项`inodecalc` ,了解如何计算inode值。 mergerfs不会在分支机构之间伪造硬链接。阅读"rename & link(重命名和链接)" 一节了解其工作原理。 请记住,硬链接无法跨设备工作。这包括在原始文件系统和mergerfs池之间,在同一底层文件系统的两个单独池之间,或者在 合并池内绑定路径装载。后者在使用Docker或Podman时很常见。同一底层文件系统的多个卷(绑定挂载)被视为不同的设备。 他们之间没有联系。如果你想让链接正常工作,你应该挂载到mergerfs池中包含所有路径的最高目录中。 #### Does FICLONE or FICLONERANGE work? /FICLONE或FICLONERANG有效吗? 不幸的是没有。mergerfs所基于的FUSE技术不支持其工作所需的`clone_file_range`功能。mergerfs甚至不会知道有人 发出这样的请求。内核只会向发出请求的应用程序返回一个错误。 如果FUSE获得了这种能力,mergerfs将被更新以支持它。 #### Can I use mergerfs without SnapRAID? SnapRAID without mergerfs? / 我可以在没有SnapRAID的情况下使用mergerfs吗?SnapRAID没有合并功能? 对。它们是完全无关的软件。 #### Can mergerfs run via Docker, Podman, Kubernetes, etc. / mergerfs可以通过Docker、Podman、Kubernetes等运行吗。 对。使用Docker时,您需要将`--cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined` 或与其他容器运行时类似。您还应该以root身份运行它,或者给它足够的上限来更改用户和组标识,以及具有类似root的文件系统权限。 请记住,在使用容器时 **必须** 考虑身份。例如:除非您通过共享相关/etc文件或使用其他方式在容器之间共享身份来正确管理用户和组, 否则将从容器中提取补充组。同样,如果您使用"rootless"容器和用户命名空间进行uid/gid转换,则在管理共享文件时 **必须** 考虑这一点。 此外,正如[hotio](https://hotio.dev/containers/mergerfs)所提到的,使用Docker时,您可能应该将`bind-propagation`设置`slave`。 #### Does mergerfs support CoW / copy-on-write / writes to read-only filesystems? / mergerfs是否支持CoW/写时复制/写入只读文件系统? 不是像BTRFS或ZFS这样的文件系统,也不是覆盖或aufs意义上的。它确实提供了一种类似 [cow-shell](http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html)的 硬链接破坏(复制到临时文件,然后重命名为原始文件),当想要通过硬链接重复文件来节省空间,但希望将 每个名称视为一个唯一且独立的文件时,这可能很有用。 如果你想写入只读文件系统,你应该看看重叠。您始终可以将overlayfs挂载包含到mergerfs池中。 #### Why can't I see my files / directories? / 为什么我看不到我的文件/目录? 这几乎总是一个权限问题。与以root身份运行并试图访问内容的mhddfs和unionfs-fuse不同, mergerfs总是将其凭据更改为调用者的凭据。这意味着,如果用户没有访问文件或目录的权限, 则两者都不会合并。然而,由于mergerfs正在创建一个路径联合,它可能能够读取一个文件系统 上的一些文件和目录,但无法读取另一个,从而导致集合不完整。 每当您遇到拆分权限问题(看到一些但不是所有文件)时,请尝试使用[mergerfs.fsck](https://github.com/trapexit/mergerfs-tools) 工具检查并修复不匹配。如果你根本看不到任何东西,请确保基本权限是正确的。用户和组值是正确的, 并且目录的可执行位已设置。Linux新手的一个常见错误是使用`chmod -R 644`,而他们应该使用`chmod -R u=rwX,go=rX`。 如果使用NFS或SMB(Samba)等网络文件系统,请务必密切关注有关权限和用户的任何信息。 例如,根压缩和用户转换在某种程度上给一些mergerfs用户带来了麻烦。其中一些还影响了 来自Docker等容器平台的mergerfs请求的使用。 #### Why use FUSE? Why not a kernel based solution? / 为什么要使用FUSE?为什么不采用基于内核的解决方案? 与任何解决问题的方法一样,每种方法都有优缺点。 基于FUSE的解决方案具有FUSE的所有缺点: * 由于内核空间的出入,IO延迟增加 * 由于内核空间的出入而导致更高的总体开销 * 使用页面缓存时进行双重缓存 * FUSE设计造成的其他限制 但FUSE也有很多优点: * 更容易提供跨平台解决方案 * 更容易向前和向后兼容 * 为用户提供更便捷的更新 * 更简单、更快的释放节奏 * 允许在设计和功能上具有更大的灵活性 * 总体上更易于编写、安全和维护 * 进入门槛要低得多(最初将代码放入内核需要花费大量时间和精力) 选择FUSE是因为上面列出的所有优点。FUSE的负面影响并不大于正面影响。 #### Is my OS's libfuse needed for mergerfs to work? /我的操作系统的libfuse需要合并功能才能工作吗? 不需要。通常需要`mount.fuse` 来获取mergerfs(或使用 `mount` 命令挂载的任何FUSE文件系统),但在提供libfuse库时, `mount.fuse`应用程序已重命名为`mount.mergerfs`,这意味着 `fstab` 中的文件系统类型可以简单地为`mergerfs`。 也就是说,安装它并继续使用`fuse.mergerfs`作为`/etc/fstab`的类型应该没有坏处。 如果`mergerfs`不能作为一种类型工作,可能是由于`mount.mergerfs`工具的安装方式造成的。必须位于`/sbin/`中, 并具有适当的权限。 #### Why was splice support removed? / 为什么要拆除拼接支架? 经过多年的大量测试,拼接似乎总是最好的,提供同等的性能,在某些情况下,性能更差。其他平台不支持拼接,迫使提供传统的读/写回退。 为了简化代码库,删除了拼接代码。 #### What should mergerfs NOT be used for? / mergerfs不应该用于什么? * 数据库:即使数据库将数据存储在单独的文件中(否则mergerfs不会提供太多),间接的较高延迟也会降低性能。 如果它是一个使用较少的SQLITE数据库,那么它可能没问题,但您需要进行测试。 * VM映像:原因与数据库相同。VM映像被非常积极地访问,合并将引入太多的延迟(如果它真的有效的话)。 * 作为RAID的替代品:mergerfs仅用于池化分支。如果你需要这种设备性能聚合或高可用性,你应该坚持使用RAID。 #### Can filesystems be written to directly? Outside of mergerfs while pooled? / 文件系统可以直接写入吗?在mergerfs中,在mergerfs之外? 是的,但是,不建议同时使用池内和池外的同一文件(特别是写入)。特别是如果使用任何类型的缓存(cache.files、 cache.entry、cache.attr、cache.ngative_entry、cache.symlinks、cache.readdir等),因为缓存数据 和非缓存数据之间可能存在冲突。 #### Why do I get an "out of space" / "no space left on device" / ENOSPC error even though there appears to be lots of space available? / 为什么即使似乎有很多可用空间,我也会收到“空间不足”/“设备上没有剩余空间”/ENOSPC错误? 首先,确保您已经阅读了上面关于策略、路径保留、分支过滤以及 **minfreespace**, **moveonenospc**, **statfs** 和 **statfs_ignore** 选项的部分。 mergerfs只是在多个分支中呈现内容的联合。报告的可用空间是池中可用空间的集合(行为由 **statfs** 和 **statfs_ignore** 修改)。它并不代表一个连续的空间。与只读文件系统、具有配额或保留空间的文件系 统报告全部理论可用空间的方式相同。 由于路径保留、分支标记、只读状态和 **minfreespace** 设置,返回`ENOSPC`/“空间不足”/ “设备上没有剩余空间”是完全有效的。它正在做要求它做的事情:根据这些设置过滤可能的分支。只能返回一个错误, 如果过滤分支的原因之一是**minfreespace** ,那么它将按原样返回。 **moveonenospc** 只与写入一个对 于它当前所在的文件系统来说太大的文件有关。 也可能是所选文件系统的索引节点已用完。使用`df -i`列出每个文件系统的总索引节点和可用索引节点。 如果你不关心路径保留,那么只需将`create` 策略更改为不关心的策略。`mfs` 可能是大多数人想要的。它不是默认 值的原因是,它最初设置为`epmfs`,现在更改它会改变人们的设置。这种设置更改可能会在mergerfs 3中发生。 #### Why does the total available space in mergerfs not equal outside? / 为什么合并器中的总可用空间与外部不相等? 你在用ext2/3/4吗?留根?mergerfs使用可用空间进行statfs计算。如果你为root预留了空间,那么它就不会出现。 您可以通过运行以下命令删除保留:`tune2fs -m 0 ` #### I notice massive slowdowns of writes when enabling cache.files. / 启用cache.files时,我注意到写入速度大幅减慢。 当以任何形式启用文件缓存时(`cache.files!=off`),它将在 *每次写入* 之前发出`security.capability` 的`getxattr`请求。这通常会导致性能下降,特别是在使用网络文件系统(如NFS或SMB)时。不幸的是,此时内核没有缓存响应。 为了解决这种情况,mergerfs提供了一些解决方案。 1. 设置`security_capability=false`。它将使任何呼叫短路并返回`ENOATTR`。这仍然意味着, 虽然合并函数将在每次写入之前收到请求,但至少它不会传递给底层文件系统。 2. 设置`xattr=noattr`。与上述相同,但适用于 *所有* 对getxattr的调用。不仅仅是`security.capability`。 这也不会被内核缓存,但mergerfs的运行时配置系统仍将正常工作。 3. 设置`xattr=nosys`。结果mergerfs返回`ENOSYS`,内核 *将* 缓存该ENOSYS。未来的xattr调用将不会转发给mergerfs。 缺点是,这也意味着基于xattr的配置和查询功能也不起作用。 4. 禁用文件缓存。如果你没有使用使用`mmap`的应用程序,那么完全禁用它可能会更简单。禁用缓存时,内核不会发送请求。 #### It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them? / 有人提到,mhddfs存在一些安全问题。它们是什么?mergerfs如何解决这些问题? [mhddfs](https://github.com/trapexit/mhddfs) 以 **root** 身份通过调用[getuid()](https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319)来管理[chown](http://linux.die.net/man/1/chown),如果它返回 **0** , 则它将选择该文件。这不仅是一种竞态条件,而且不能处理其他情况。与其试图模拟POSIX ACL行为, 不如使用 [seteuid](http://linux.die.net/man/2/seteuid) 和[setegid](http://linux.die.net/man/2/setegid) 来管理它,实际上,成为进行原始调用的用户,并像他们一样执行操作。这就是mergerfs的作用,也是为什么mergerfs应该始终以root身份运行。 在Linux中,setreuid系统调用仅适用于线程。GLIBC通过使用实时信号通知所有线程更改凭据来隐藏这一点。在 **Samba** 之后,mergerfs使用 **syscall(SYS_setreuid,...)** 仅为该线程设置调用者凭据。如果需要升级权限(例如:克隆文件系统之间的路径),则必要时跳回 **root** 目录。 对于非Linux系统,mergerfs使用读写锁,仅在必要时更改凭据。如果多个线程都是用户X,那么只有第一个线程需要更改进程凭据。 只要其他线程需要是用户X,它们就会采用读锁,允许多个线程共享凭据。一旦请求以用户Y的身份运行,该线程将尝试写锁,并在可 能的情况下更改为Y的凭据。如果支持赋予写入者优先级的能力,那么将使用该标志,这样尝试更改凭据的线程就不会饿死。这不是最 好的解决方案,但假设用户很少,应该能很好地工作。 # mergerfs 与X #### mhddfs mhddfs已经有一段时间没有维护了,并且存在一些已知的稳定性和安全性问题。mergerfs提供了mhddfs功能的超集,应该能提供相同或更好的性能。 下面是一个mhddfs和mergerfs设置为类似工作的示例。 `mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool` `mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool` #### aufs aufs大多已被放弃,在大多数Linux发行版中不再可用。 虽然aufs可以提供更好的峰值性能,但mergerfs提供了更多的可配置性,通常更容易使用。然而,mergerfs不提供aufs所具有的写时覆盖/复制(CoW)功能。 #### unionfs-fuse unionfs-fuse更像是aufs而不是mergerfs,因为它提供了覆盖/写时复制(CoW)功能。如果你只是想 创建一个文件系统的联合,并希望在文件/目录放置方面具有灵活性,那么mergerfs可以提供这种灵活性, 而unionfs更适合将读/写文件系统覆盖在只读文件系统之上。 #### overlayfs overlayfs类似于aufs和unionfs-fuse ,因为它也主要用于在一个或多个只读文件系统上分层读/写文件系统。 它无法在众多文件系统中传播文件/目录。 #### RAID0, JBOD,驱动器级联,条带化 使用简单的JBOD/驱动器级联/stripping/RAID0,单个驱动器故障将导致整个池故障。mergerfs执行类似的功能, 没有灾难性故障的可能性和恢复的困难。驱动器可能会发生故障,但所有其他文件系统及其数据将继续可访问。 与mergerfs的主要实际区别在于,你实际上没有像使用其他技术那样大的连续空间。这意味着您无法在2个1TB文件 系统的池中创建2TB文件。 当与 [SnapRaid](http://www.snapraid.it)和/或异地备份解决方案结合使用时,您可以在没有单点故障的情 况下获得JBOD的灵活性。 #### UnRAID UnRAID是一个完整的操作系统,据我所知,它的存储层是专有的、闭源的。有过这两种经验的用户经常说,他们更喜 欢mergerfs提供的灵活性,对一些人来说,它是开源的这一事实很重要。 尽管我对这个用例并不完全熟悉,但也有许多UnRAID用户使用mergerfs。 对于半静态数据,mergerfs+ [SnapRaid](http://www.snapraid.it)提供了类似的解决方案。 #### ZFS mergerfs与ZFS有很大不同。mergerfs旨在为任意大小的任意文件系统(本地或远程)和任意文件系统提供灵活的池化。 对于 `write once, read many` (一次写入,多次读取)用例,如大容量介质存储。以其他方式管理数据完整性和备份。 在这些用例中,ZFS可能会引入许多成本和限制,如 [这里](http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html)、 [这里](https://markmcb.com/2020/01/07/five-years-of-btrfs/)和 [这里](https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping)这里所述。 #### StableBit's DrivePool DrivePool仅适用于Windows,因此不像其他Linux解决方案那样常见。如果你想使用Windows,那么DrivePool 是一个不错的选择。从功能上讲,这两个项目的工作方式有点不同。DrivePool总是写入具有最多可用空间的文件系统, 然后重新平衡。mergerfs不提供重新平衡,而是在文件/目录创建时选择一个分支。DrivePool的重新平衡可以在任何 目录中以不同的方式进行,并具有文件模式匹配功能,以进一步自定义行为。没有重新平衡的mergerfs没有这些功能, 但计划为mergerfs v3提供类似的功能。DrivePool内置了文件复制功能,而mergerfs本身并不支持(但可以通过 外部脚本完成) 这两个项目之间有很多不同之处,但DrivePool中的大多数功能都可以通过外部工具结合mergerfs来复制。 此外,DrivePool是一个闭源商业产品,而mergerfs是一个ISC许可的OSS项目。 # 支持 文件系统很复杂,很难调试。mergerfs虽然只是一种代理,但由于它本身可以有大量可能的设置以及它可以在多个环境 中运行,因此很难调试。在报告可疑问题时, **请** 尽可能多地包含以下信息,否则将难以或不可能进行诊断。另外, 请阅读上述文档,因为它提供了许多以前遇到的问题/问题的详细信息。 **请确保您使用的是 [最新版本 release](https://github.com/trapexit/mergerfs/releases) ,或者已经尝试过比较。Debian和Ubuntu等 发行版中通常包含的旧版本永远不会更新,您遇到的问题可能已经得到解决。 ** **如需商业支持或功能请求,请直接[联系我](mailto:support@spawn.link)** #### 错误报告中应包含的信息 * [关于更广泛问题的信息以及任何尝试的解决方案。](https://xyproblem.info) * 解决方案已经排除,原因何在。 * mergerfs的版本:`mergerfs --version` * mergerfs 设置/参数: 来自 fstab, systemd unit, 命令行,OMV插件等。 * 操作系统版本: `uname -a` and `lsb_release -a` * 分支列表、它们的文件系统类型、大小(问题发生前后): `df -h` * 有关相关路径和文件的 **所有** 信息: 权限、所有权等 * 有关发出请求的客户端应用程序的 **所有** 信息: 版本, uid/gid * 运行时环境: * mergerfs是否在容器内运行? * 使用mergerfs的客户端应用程序是否在容器中运行? * 应用程序出现问题的`strace`: * `strace -fvTtt -s 256 -o /tmp/app.strace.txt ` * 当程序试图做它失败的事情时,会出现一系列mergerfs操作的`strace`: * `strace -fvTtt -s 256 -p -o /tmp/mergerfs.strace.txt` * **精确** 介绍问题。不要遗漏 **任何东西** 。 * 尝试使用标准程序以最简单的方式重现问题: `ln`, `mv`, `cp`, `ls`, `dd`, 等. #### 联系方式/问题提交 * github.com: https://github.com/trapexit/mergerfs/issues * discord: https://discord.gg/MpAr69V * reddit: https://www.reddit.com/r/mergerfs #### 捐赠 https://github.com/trapexit/support 像mergerfs这样的项目的开发和支持需要大量的时间和精力。该软件是在非常自由的ISC许可证下发布的, 因此可以自由用于个人或商业用途。 如果您是个人用户,发现mergerfs及其支持很有价值,并希望在财务上支持该项目,我们将不胜感激。 如果您正在商业上使用mergerfs,请考虑赞助该项目,以确保其继续得到维护并收到更新。如果需要自定 义功能,请随时直接 [与我联系](mailto:support@spawn.link)。 # 一些链接 * https://spawn.link * https://github.com/trapexit/mergerfs * https://github.com/trapexit/mergerfs/wiki * https://github.com/trapexit/mergerfs-tools * https://github.com/trapexit/scorch * https://github.com/trapexit/bbf