缓存
对于一个由对象存储和数据库组合驱动的文件系统,缓存是本地客户端与远端服务之间高效交互的重要纽带。读写的数据可以提前或者异步载入缓存,再由客户端在后台与远端服务交互执行异步上传或预取数据。相比直接与远端服务交互,采用缓存技术可以大大降低存储操作的延时并提高数据吞吐量。
JuiceFS 提供包括元数据缓存、数据读写缓存等多种缓存机制。
数据缓存可以有效地提高随机读的性能,对于像 Elasticsearch、ClickHouse 等对随机读性能要求更高的应用,建议将缓存路径设置在速度更快的存储介质上并分配 更大的缓存空间。
然而缓存能提升性能的前提是,你的应用需要反复读取同一批文件。如果你确定你的应用对数据是「读取一次,然后再也不需要」的访问模式(比如大数据的数据清洗常常就是这样),可以关闭缓存功能,省去缓存不断建立,又反复淘汰的开销。
数据一致性
分布式系统,往往需要在缓存和一致性之间进行取舍。JuiceFS 由于其元数据分离架构,需要从元数据、文件数据(对象存储)、文件数据本地缓存三方面来思考一致性问题:
对于元数据缓存,JuiceFS 默认的挂载设置满足「关闭再打开(close-to-open)」一致性,也就是说一个客户端修改并关闭文件之后,其他客户端重新打开这个文件都会看到最新的修改。与此同时,默认的挂载参数设置了 1 秒的内核元数据缓存,满足了一般场景的需要。但如果你的应用需要更激进的缓存设置以提升性能,可以阅读下方章节,对元数据缓存进行针对性的调优。特别地,发起修改的客户端(挂载点)能享受到更强的一致性,阅读一致性例外详细了解。
对于对象存储,JuiceFS 将文件分成一个个数据块(默认 4MiB),赋予唯一 ID 并上传至对象存储服务。文件的任何修改操作都将生成新的数据块,原有块保持不变,所以不用担心数据缓存的一致性问题,因为一旦文件被修改过了,JuiceFS 会从对象存储读取新的数据块。而老的失效数据块,也会随着回收站或碎片合并机制被删除,避 免对象存储泄露。
本地数据缓存缓存也是以对象存储的数据块做为最小单元。一旦文件数据被下载到缓存盘,一致性就和缓存盘可靠性相关,如果磁盘数据发生了篡改,客户端也会读取到错误的数据。对于这种担忧,可以配置合适的 --verify-cache-checksum
策略,确保缓存盘数据完整性。
元数据缓存
作为用户态文件系统,JuiceFS 元数据缓存既通过 FUSE API,以内核元数据缓存的形式进行管理,同时也直接在客户端内存中维护。
内核元数据缓存
JuiceFS 客户端可以控制这些内核元数据缓存:文件属性(attribute,包含文件名、大小、权限、修改时间等信息)、文件项(entry 和 direntry,用来区分文件和目录类型的文件),在挂载时,可以使用下方参数,通过 FUSE 控制这些元数据的缓存时间:
# 文件属性缓存时间(秒),默认为 1,提升 getattr 性能
--attr-cache=1
# 文件类型的缓存时间(秒),默认为 1,提升文件 lookup 性能
--entry-cache=1
# 目录类型文件的缓存时间(秒),默认为 1,提升目录的 lookup 性能
--dir-entry-cache=1
让以上元数据默认在内核中缓存 1 秒,能显著提高 lookup
和 getattr
的性能。
需要注意,entry
缓存是随着文件访问逐渐建立起来的,不是一个完整列表,因此不能被 readdir
调用或者 ls
命令使用,而只对 lookup
调用有加速效果。这里的 dir-entry
含义也不等同于「目录项」的概念,他并不用来描述「一个目录下包含哪些文件」,而是和 entry
一样,都是文件,只不过对文件是否目录类型做了区分。
在实际场景中,也很少需要对 --entry-cache
和 --dir-entry-cache
进行区分设置,如果确实要精细化调优,在目录极少变动、而文件频繁变动的场景,可以令 --dir-entry-cache
大于 --entry-cache
。
客户端内存元数据缓存
JuiceFS 客户端在 open
操作即打开一个文件时,其文件属性会被自动缓存在客户端内存中,这里的属性缓存,不仅包含内核元数据中的文件属性比如文件大小、修改时间信息,还包含 JuiceFS 特有的属性,如文件和 chunk、slice 的对应关系。
为保证「关闭再打开(close-to-open)」一致性,open
操作默认需要直接访问元数据引擎,不会利用缓存。也就是说,客户端 A 的修改在客户端 B 不一定能立即看 到。但是,一旦这个文件在 A 写入完成并关闭,之后在任何一个客户端重新打开该文件都可以保证能访问到最新写入的数据,不论是否在同一个节点。文件的属性缓存也不一定要通过 open
操作建立,比如 tail -f
会不断查询文件属性,在这种情况下无需重新打开文件,也能获得最新文件变动。
如果要利用上客户端内存的元数据缓存,需要设置 --open-cache
,指定缓存的有效时长。在缓存有效期间执行的 getattr
和 open
操作会从内存缓存中立即返回 slice 信息。有了这些信息,就能省去每次打开文件都重新访问元数据服务的开销。
使用 --open-cache
选项设置了缓存时间以后,文件系统就不再满足 close-to-open 一致性了,不过与内核元数据类似,发起修改的客户端同样能享受到客户端内存元数据缓存主动失效,其他客户端就只能等待缓存自然过期。因此为了保证文件系统语义,--open-cache
默认关闭。如果文件很少发生修改,或者只读场景下(例如 AI 模型训练),则推荐根据情况设置 --open-cache
,进一步提高读性能。
作为对比,JuiceFS 商业版提供更丰富的客户端内存的元数据缓存功能,并且支持主动失效,阅读商业版文档以了解。
一致性例外
当文件发生变动时,发起修改的挂载点能够享受到更强的一致性,具体而言: