文件导入和转存
JuiceFS 默认会将文件进行分块存储,并 将元数据与数据剥离 。正是这样的存储格式和分离架构,让 JuiceFS 得以成为高性能、强一致性文件系统。
但是在某些非常特殊的场景下,用户更需要直接在对象存储上保存原始文件,让对象存储里的文件能够脱离 JuiceFS 元数据使用,或者直接将对象存储中已有的大量文件直接导入 JuiceFS,使其可以通过 POSIX 访问,并利用上 JuiceFS 的强大缓存能力。在对象存储里保存完整文件并在 JuiceFS 中使用,我们称为「兼容格式」。而默认的文件分块存储的高性能模式,则称为「优化格式」。
从 5.0 开始,JuiceFS 极大改善了对兼容格式的支持,提供以下功能,来满足上述要求:
juicefs import
命令。确切地说,这个命令早已支持,但从 5.0 开始,导入的文件也支持读缓存。
导入对象存储已有文件
juicefs import
会扫描给定的对象存储地址,然后将目标文件的元数据信息写入 JuiceFS 元数据引擎,让这些文件在 JuiceFS 中也能访问,该操作并不会实际复制任何文件,文件仍原样保存在对象存储里,因此相较于 JuiceFS 原生的分块格式,这种存储格式称为「兼容格式」,意为和对象存储保持兼容。
导入的文件,使用上要注意:
juicefs info
,如果在
info
的输出中关注
object
字段(而没有
chunks
表格),来识别兼容格式。
从 JuiceFS 5.0 开始,导入的文件同样支持 本地缓存 和 分布式缓存 。虽然导入的文件并没有真正写入 JuiceFS 文件系统、经过 JuiceFS 的 分块格式化 ,但实际缓存到本地盘时,仍然会被拆分为数据块(大小为文件系统的 block size),因此对于导入文件的缓存的使用和管理方式,和正常写入 JuiceFS 文件系统中的文件没有任何不同。
从外部桶导入的文件,不支持缓存。因此如果希望导入的文件能享受到缓存功能,文件系统和导入源必须使用同一个对象存储桶,并且导入命令必须使用以下格式:
# URI 中不包含 bucket 参数,则支持缓存
juicefs import / /jfs/imported
juicefs import /prefix /jfs
# 如果 URI 包含了 bucket,便不支持缓存
# 在下方例子中,即使 BUCKET 正是文件系统所使用的桶,也不支持缓存
juicefs import BUCKET/prefix /jfs
使用 JuiceFS 的缓存功能加速导入文件的读取时,需要注意一致性问题:由于导入的对象本身并不受 JuiceFS 管理, 如果对象发生了修改,而没有重新导入到 JuiceFS 的话,因为可能存在旧版本的缓存,不确保能读到最新的数据 。因此如果导入 JuiceFS 以后,对象发生了变更,则需要再次导入。而已有的缓存数据,会根据导入的对象的修改时间自动失效。确保能读到修改过后的数据。
因此,对于需要反复修改的对象,建议将数据整体迁移到 JuiceFS,推荐用
juicefs sync
将数据写入 JuiceFS,但归功于 JuiceFS 的 POSIX 兼容性,你也可以用任意其他工具。
按需导入(实验性功能)
从 5.1.3 开始,JuiceFS 实验性地支持「按需导入」,该功能在上一小节 import 的基础上,允许用户将整个对象存储桶映射为一个 JuiceFS 文件系统,并且在访问时才 list 对象存储、导入元数据。相比定期 import 的流程,按需导入的模式有以下好处:
按需导入是一个实验性功能,其用法和设计都有可能面临未来调整,如果你有意进行试用和评估,请务必联系 Juicedata 工程师,和我们充分沟通,并与我 们一起开展测试。
使用流程
该功能只支持从文件系统关联的桶导入文件,因此必须在创建文件系统之初,就将待导入的桶设置为文件系统的关联桶(在 JuiceFS Web 控制台的文件系统设置页面操作)。满足这个前提以后,才能在服务器上操作挂载。
下方命令中,
--source=/
的含义是将
对象存储的根路径
映射到文件系统的根。目前这个参数的写法是固定的,不支持设为
/
以外的值。
juicefs mount myjfs /jfs --source=/
挂载完毕以后,
ls
便能看到对象存储的顶级目录:
$ cd /jfs
$ ls -alh
...
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir1
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir2
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir3
注意上方目录的
777
权限,JuiceFS 将目录权限赋予了特殊含义:凡是
777
的目录,都还没被实际访问过,因此并未完成元数据导入。但只要以任何形式访问这些目录下的内容,JuiceFS 就会立刻扫描对象存储、快速建立对应的文件目录结构。建立完成以后权限会发生变化:
# 用任何方式访问 dir1 下方的文件,既可以 cd 进入,也可以直 接访问特定文件
$ ls dir1/file.txt
# 目录访问过以后,权限会从 777 变为 555
$ ls -alh
...
dr-xr-xr-x 5 root root 16K Nov 11 17:47 dir1
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir2
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir3
因此,在按需导入的工作流中,可以依据目录权限来判断加载的情况:
777
代表尚未加载,如果不是
777
,则说明目录已经导入元数据。可以读取或者运行预热。
写入文件(不推荐)
和上方的 import 命令类似,你无法修改从对象存储中直接导入的文件。如果导入以后删除了这些文件,也只会删除他们在 JuiceFS 中的元数据索引,并不会对桶中数据产生任何影响。
不过事实上,一个按需导入的文件系统,仍然可以像正常的 JuiceFS 文件系统一样写入新文件,写入的文件并不是以完整文件格式直接上传对象存储,而是用 JuiceFS 标准的分块格式写 入到对象存储桶的
/[volume-name]/chunks/
目录下。因此如果确实进行了写入,对象存储桶里将会存在两种形式的数据:用于导入 JuiceFS 的完整文件,以及通过 JuiceFS 写入的分块格式数据块。
我们目前不推荐在按需导入的文件系统中写入文件,这不仅会增加管理复杂度,需要辨别哪些文件是导入的、不支持修改,哪些文件是通过原生的分块格式写入,还会有数据丢失的风险:待元数据过期,下次扫描、重建元数据的时候,将会删除多余的文件和目录,引发数据丢失。
元数据有效期
导入以后,对象存储一侧的文件仍可能面临增删或修改,为了能将改动反映到 JuiceFS 文件系统,你需要根据变更频率来设定元数据的有效期:
juicefs mount myjfs /jfs --source=/ --refresh-interval=1h
如此设定后,导入的元数据会在 1 小时后过期。过期以后再访问,比如运行
ls
,或者
lookup
一个不存在的文件时,都会触发更新元数据。当多个客户端同时要进行扫描时,会使用文件锁确保只有一个客户端在做扫描,避免重复扫描给对象存储造成更大压力。取决于数据量大小以及对象存储的 list 性能,扫描的过程仍然可能会卡住一段时间,元数据再次建立以后,就又恢复先前的高速访问。因此用户需要确定对象存储桶的更新频率,然后根据业务对时效性的要求来恰当设置更新间隔。
如果业务对访问延迟敏感、无法接受大目录过期以后首次访问的卡顿,那么也可以启用异步刷新,访问的时候会异步触发重新扫描,不等待重建完成。
juicefs mount myjfs /jfs --source=/ --refresh-interval=1h --refresh-in-background
需要注意,开启了异步刷新后,就不再满足由
--refresh-interval
定义的时效性,以上方的一小时设置为例,如果启用了异步更新,那么过期后 ls 不保证能看到过去一小时新增的文件,只有同步更新模式才能保证这一点。
缓存数据和预热
运行预热命令(
juicefs warmup
)会将
已经建立元数据的部分
进行预热,也就是权限为
555
的目录。如果对权限为
777
的目录进行预热,那么由于尚未扫描导入,JuiceFS 客户端不会下载任何其中的数据。这样的设计允许用户可以完全按需下载所需的数据,访问过哪些目录,预热的时候就会下载其中的数据,没访问过的目录就不会下载。这样的用法足够灵活,因此用户也必须将需要预热的目录提前进行访问、建立元数据。
用上方的案例进行示范:
$ ls -alh
...
dr-xr-xr-x 5 root root 16K Nov 11 17:47 dir1
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir2
drwxrwxrwx 2 root root 4.0K Nov 11 17:35 dir3
# 对 777 目录进行预热,不会下载任何数据
$ juicefs warmup dir3
# 只有提前访问过、建立了元数据的目录,才会真正进行预热
$ juicefs warmup dir1
和 「导入」 命令一样,按需导入的文件支持本地和分布式缓存,因此使用体验完全一致,请阅读链接所指的小节了解详细情况。
功能启用以后,文件会在指定时间后被转存成完整文件、保存在对象存储。
使用场景
文件由 JuiceFS 写入,但希望后续也能通过对象存储 直接读取 ,对接云上生态。之所以强调直接读取,是因为 JuiceFS 也提供 S3 接口,如果你只是需要为文件系统暴露 S3 接口,应该使用 S3 网关 。
希望直接使用对象存储的生命周期管理、归档功能,将冷数据进行归档处理。归档的数据要求是已经合并转存为完整文件的状态,可以脱离 JuiceFS 元数据直接使用。
注意,该场景的重点仍然是
脱离 JuiceFS 使用
,如果你仅仅是希望实现在 JuiceFS 中读写冷热数据,可以用
--storage-class
来为客户端指定存储级别,这是更合适的方式。
由于数据合规性条例,必须在对象存储上保存原始文件。
其他需要将对象存储数据方便地脱离 JuiceFS 使用的情况。
不应使用的场景
转存是针对少数极特殊场景推出的实验性功能,如果你的场景并未在上方列出,那么绝不应该使用此功能。启用转存会给文件系统带来一些重要的限制( 比如转存后的文件是只读的),继续阅读下方的功能概览以了解。
举例说明,下列场景就 不应该使用转存功能 :
juicefs auth
指定
--storage-class
参数,来指定客户端的存储级别,实现在同一个文件系统内写入不同存储级别的文件。
功能概览
从对象存储的文件列表观察转存过程,效果如下:
# 转存前
mybucket/
├── chunks
│ ├── 41
│ │ └ ── 1
│ │ ├── 1000001_0_4194304
│ │ └── 1000001_10_4194304
│ ├── 43
│ │ └── 1
│ │ ├── 1000003_0_4194304
...
# 转存后,文件会被原样写入对象存储,保留目录结构,原本分块格式的数据块会被删除
mybucket/
├── bigfile1.tar.gz
├── chunks
├── dir/bigfile2.tar.gz
由于「转存」的目的是脱离 JuiceFS 分块格式,在对象存储上进行「原样存储」,因此正如上方所示范的,文件会按照文件系统里的目录结构进行存储,因此如果开启转存, 文件系统必须要独占对象存储桶 ,决不能一桶多用(也不能将该桶用于其他 JuiceFS 文件系统),否则将容易发生冲突、甚至导致数据丢失。
转存后的文件,也不再支持内容修改,写操作会直接报错提示没有权限。虽无法编辑,但可以进行
mv
,该命令在 JuiceFS 中会被解释为「跨设备拷贝 + 删除」,相当于从兼容格式将文件正常读出,然后再以分块格式写回 JuiceFS,成为一个全新的文件,然后再删除源文件。由于
mv
将文件从兼容格式重新转为了分块格式,因此文件又可以正常编辑了,直到等待了指定时间以后,被再次转存。
启用转存的文件系统,所有目录再创建一段时间之后(包括空目录),就不能移动(
mv
),只能删除重建。