缓存
对于一个由对象存储和数据库组合驱动的文件系统,缓存是本地客户端与远端服务之间高效交互的重要纽带。读写的数据可以提前或者异步载入缓存,再由客户端在后台与远端服务交互执行异步上传或预取数据。相比直接与远端服务交互,采用缓存技术可以大大降低存储操作的延时并提高数据吞吐量。
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
# 失败查询 (lookup 返回 ENOENT) 的缓存时间(秒),默认为 0,提升不存在文件或目录的 lookup 性能
--negative-entry-cache=1
让以上元数据默认在内核中缓存 1 秒,能显著提高 lookup
和 getattr
的性能。
需要注意,entry
缓存是随着文件访问逐渐建立起来的,不是一个完整列表,因此不能被 readdir
调用或者 ls
命令使用,而只对 lookup
调用有加速效果。这里的 dir-entry
含义也不等同于「目录项」的概念,他并不用来描述「一个目录下包含哪些文件」,而是和 entry
一样,都是文件,只不过对文件是否目录类型做了区分。
在实际场景中,也很少需要对 --entry-cache
和 --dir-entry-cache
进行区分设置,如果确实要精细化调优,在目录极少变动、而文件频繁变动的场景,可以令 --dir-entry-cache
大于 --entry-cache
。