JuiceFS 目录配额功能设计详解

JuiceFS 在最近 v1.1 版本中加入了社区中呼声已久的目录配额功能。已发布的命令支持为目录设置配额、获取目录配额信息、列出所有目录配额等。完整的详细信息,请查阅文档。

在设计此功能时,对于它的统计准确性,实效性以及对性能的影响,团队内部经历过多次讨论和权衡。在本文中,我们会详述一些在设计关键功能时的不同抉择及其优缺点,并分享最终的实现方案,为想深入了解目录配额或有相似开发需求的用户提供参考。

01 需求分析

配额的设计首先需考虑以下三个要素:

  • 统计的维度:常见的是基于目录来统计用量和实现限制,其他还有基于用户和用户组的统计

  • 统计的资源:一般包括文件总容量和文件总数量

  • 限制的方式:最简单的就是当使用量达到预定值时,就不让应用继续写入。这种预定值一般称为硬阈值(Hard Limit)。还有一种常见的限制叫做软阈值(Soft Limit),在使用量达到这个值时,仅触发告警通知但不立即限制写入,而是在达到硬阈值或者经过一定的宽限时间(Grace Period)后再实施限制。

其次,也应考虑对配额统计实效性和准确性的要求。在分布式系统中,往往会有多个客户端同时访问,若要保证他们在同一时间点对配额的视图始终一致,势必会对性能有比较大的影响。最后,还应考虑是否支持复杂的配置,如配额嵌套、为非空目录设置配额等。

开发原则

我们的主要考量是尽量简单和便于管理。在实现时避免大规模代码重构,减少对关键读写路径的侵入,以期在实现新特性的同时,不会对现有系统的稳定性和性能造成较大影响。基于此,我们整理出了如下表所示的待开发功能:

值得一提的是表中标红的三项。一开始我们并不打算支持这些,因为它们的复杂性对配额功能的整体实现构成了挑战,而且也不在我们定义的核心功能之列。但在与多方用户沟通后,我们意识到缺少这些功能会导致配额功能的实用性大打折扣,许多用户确实需要这些功能来满足他们的实际需求。因此,最终我们还是决定在 v1.1 版本中就带入这些功能。

02 基础功能

1 用户接口

在设计配额功能时,首先要考虑的是用户如何设置和管理配额。这一般有两种方式:

1.使用特定的命令行工具,如 GlusterFS 使用以下命令为指定目录设置硬阈值:

$ gluster volume quota <VOLNAME> limit-usage <DIR> <HARD_LIMIT>

2.借助已有的 Linux 工具,但使用特定的字段;如 CephFS 将配额作为一项特殊的扩展属性来管理:

$ setfattr -n ceph.quota.max_bytes -v 100000000 /some/dir     # 100 MB

JuiceFS 采用了第一种方式,命令形式为:

$ juicefs quota set <METAURL> --path <PATH> --capacity <LIMIT> --inodes <LIMIT>

做出这个选择主要有以下三点理由:

  1. JuiceFS 已有现成的 CLI 工具,要添加配额管理功能只需新加一个子命令即可,非常方便。

  2. 配额通常应由管理员来进行配置,普通用户不能随意更改;自定义的命令中可要求提供 METAURL 来保证权限。

  3. 第二种方式需要提前将文件系统挂载到本地。配额设置常需对接管控平台,将目录路径作为参数直接包含在命令中可以避免此步骤,使用起来更加方便。

2 元数据结构

JuiceFS 支持三大类元数据引擎,包括 Redis,SQL 类(MySQL、PostgreSQL、SQLite 等)和 TKV 类(TiKV、FoundationDB、BadgerDB 等)。每类引擎根据其支持的数据结构有不同的具体实现,但管理的信息大体上是一致的。在上一小节我们已决定使用独立的 juicefs quota 命令来管理配额,那么元数据引擎中也同样使用独立的字段来存储相关信息。以较简单的 SQL 类为例:

// SQL table
type dirQuota struct {Inode      Ino   `xorm:”pk”`MaxSpace   int64 `xorm:”notnull”`MaxInodes  int64 `xorm:”notnull”`UsedSpace  int64 `xorm:”notnull”`UsedInodes int64 `xorm:”notnull”`
}

可见,JuiceFS 为目录配额新建了一张表,以目录索引号(Inode)为主键,保存了配额中容量和文件数的阈值以及已使用值。

3 配额更新/检查

接下来考虑配额信息的维护,主要是两个任务:更新和检查

更新配额通常牵涉到新建和删除文件或目录,这些操作都会对文件个数产生影响。此外,文件的写入操作会对配额的使用容量产生影响。实现上最直接的方式是在每个请求完成更新后,同时将更改提交到数据库。这可以确保统计信息的实时性和准确性,但很容易造成严重的元数据事务冲突。

究其原因,是因为在 JuiceFS 的架构中,没有独立的元数据服务进程,而是由多个客户端以乐观事务的形式并发将修改提交到元数据引擎。一旦它们在短时间内尝试更改同一个字段(比如配额的使用量),就会引发严重的冲突。

因此,JuiceFS 的做法是在每个客户端内存中同步维护配额相关的缓存,并将本地更新每隔 3 秒异步地提交到数据库。这样做牺牲了一定的实时性,但可以有效减少请求个数和事务冲突。此外,客户端在每个心跳周期(默认 12 秒)从元数据引擎加载最新信息,包括配额阈值和使用量,以了解文件系统全局的情况。

配额检查与更新类似,但更为简单。在执行操作之前,如有必要客户端可直接在内存中进行同步检查,并在检查通过后才继续后面的流程。

03 复杂功能设计

本章讨论目录配额中相对复杂的两个功能(即第一章需求表中标红项)的设计思路。

功能1:配额嵌套

在与用户进行沟通时,我们经常面临这样的需求:某个部门设置了一个大型的配额,但在该部门内部可能还有小组或个人,而这些个体也需要各自的配额。

这里就需要对配额增加嵌套结构。如果不考虑嵌套,每个目录只有两种状态:没有配额或者只受一个配额限制,整体维护比较简单。一旦引入嵌套结构,情况就会变得相对复杂。例如,在更新文件时,我们需要找到所有受影响的配额并对其进行检查或更改。那么在给定目录后,如何快速找到其所有受影响的配额呢?

方案一:缓存 Quota 树以及目录到最近 Quota 的映射

这个方案比较简单直接,即维护配额间相互的嵌套结构,以及每个目录到最近配额的映射信息。针对上图的数据结构如下:

// quotaTree map[quotaID]quotaID
{q1: 0, q2:0, q3: q1}
// dirQuotas map[Inode]quotaID
{d1: q1, d3: q1, d4: q1, d6: q3, d2: q2, d5: q2}

有了这些信息,在配额更新或查找时,我们可以根据操作的目录 Inode 快速找到最近的配额 ID,再根据 quotaTree 逐级找到所有受影响的配额。这个方案能实现高效的查找,从静态角度来看,是有优势的。然而,某些动态变化会难以处理。考虑如下图所示场景:

现在需要将目录 d4 从原来的 d1 移动到 d2 下。这个操作中 q3 的父配额从 q1 变成了 q2,但由于 q3 被配置在 d6 上,这个变化很难被感知到(我们可以在移动 d4 的同时遍历其下所有目录看它们是否有配额,但显然这会是个大工程)。鉴于此,这个方案并不可取。

方案二:缓存目录到父目录的映射关系

第二个方案是缓存所有目录到其父目录的映射关系,针对上图的初始数据结构如下:

// dirParent map[Inode]Inode
{d1: 1, d3: d1, d4: d1, d6: d4, d2: 1, d5: d2}

同样的修改操作,这时仅需将 d4 的值由 d1 改成 d2 即可。此方案中,在查找某个目录所有受影响的配额时,我们需要根据 dirParent 逐级往上直到根目录,在过程中检查每个路过的目录是否设置了配额。显然,这个方案的查找效率相比之前的方案略低。但好在这些信息都缓存在客户端内存中,整体效率依然在可接受范围内,因此我们最终采用了这个方案。

值得一提的是,这个目录到父目录的映射关系是常驻客户端内存的,没有设置特定的过期策略,这主要有两个角度的考虑:

  1. 通常情况下,文件系统的目录数量不会非常大,仅用少量内存即可将其全部缓存起来。

  2. 其他客户端对目录的更改,在本客户端中并不需要立即感知;当本客户端再次访问相关目录时,会通过内核下发的查找(Lookup)或读取目录(Readdir)请求更新缓存。

功能2:递归统计

在需求分析阶段,除了嵌套配额外,还出现了两个相关的问题:一是为非空目录设置配额,二是目录移动之后产生配额变化。这两个问题其实本质上是同一个,那就是 “如何快速地获取某个目录树的统计信息”。

方案一:默认为每个目录添加递归统计信息
这个方案有点像前面的配额嵌套功能,只是现在需要为每个目录都加上递归统计信息,数量上会比配额多不少。它的好处是使用时比较方便,仅需一次查询就能立即知道指定目录下整棵树的大小。这个方案的代价是维护成本较高,在修改任一文件时,都需要逐级往上修改每个目录的递归统计信息。这样越靠近根节点的目录被修改的越频繁。JuiceFS 的元数据实现均采用乐观锁机制,即在发现冲突时通过重试来解决,在高压力情况下,部分目录的修改事务会冲突得非常严重。而且随着集群规模的扩大,频繁重试还会导致元数据引擎压力急剧上升,容易导致崩溃。

方案二:平时不干预,只有在需要时,才对指定目录树进行临时扫描
这是一个很简单而直接的方案。其问题在于当目录下的文件数量庞大时,临时扫描可能会耗时非常久。同时,这也会对元数据引擎产生很高的爆发压力。因此,这个方案也不适合拿来直接使用。

方案三:平时只维护每个目录下一级子项的使用量,需要时扫描指定树下所有目录

这个方案结合了前两个方案的优点,并尽力避免了它们的缺点。在进一步说明前首先介绍两个文件系统中的现象:

  1. 在处理大部分元数据请求时,其本身就带有直接父目录的信息,因此不需要额外的操作去获取,也不会引入额外的事务冲突

  2. 通常情况下,文件系统中目录数量会比普通文件少 2 ~ 3 个数量级

基于上述两点观察,JuiceFS 实现了称之为目录统计的功能,即在平时就维护好每一个目录下一级子项的统计量。当配额功能需要使用递归统计信息时,无需遍历所有文件,而只需统计所有子目录的使用量即可。这也是 JuiceFS 最终采用的方案。

另外,在加入了目录统计功能后,我们还发现了一些额外的好处。比如原本就有的 juicefs info -r 命令,被用来代替 du 统计指定目录下的使用总量;现在这个命令的执行速度又有了数量级上的提升。还有一个是新加的 juicefs summary 命令,它可用来快速分析指定目录下的具体使用情况,如执行特定排序来找到已用容量最高的子目录等。

04 其他功能 :配额修复

在上述的介绍中,我们已经知道 JuiceFS 在实现目录配额时,为了追求稳定性和减少对性能的影响,在一定程度上牺牲了准确性。当客户端进程异常退出,或目录被频繁移动时,配额信息会有少量的丢失。随着时间的推移,这可能导致存储的配额统计值与实际情况出现较大的偏差。

因此,JuiceFS 还提供了 juicefs check 这个修复功能。它被用来重新扫描统计整棵目录树,并将结果与配额中保存的值做比对。如果发现数据不匹配,系统会向您报告存在的问题,并提供可选的修复选项。

希望这篇内容能够对你有一些帮助,如果有其他疑问欢迎加入 JuiceFS 社区与大家共同交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/99173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端uniapp如何修改下拉框uni-data-select下面的uni-icons插件自带的图片【修改uniapp自带源码图片/图标】

目录 未改前图片未改前源码未改前通过top和bottom 和修改后图片转在线base64大功告成最后 未改前图片 未改前源码 然后注释掉插件带的代码&#xff0c;下面要的 未改前通过top和bottom 和修改后 找到uni-icons源码插件里面样式 图片转在线base64 地址 https://the-x.cn/b…

RK3288:BT656 RN6752调试

这篇文章主要想介绍一下再RK3288平台上面调试BT656 video in的注意事项。以RN6752转接芯片&#xff0c;android10平台为例进行介绍。 目录 1. RK3288 VIDEO INPUT 并口 2. 驱动调试 2.1 RN6752 驱动实现 ①rn6752_g_mbus_config总线相关配置 ②rn6752_querystd配置制式 …

【单片机】18-红外线遥控

一、红外遥控背景知识 1.人机界面 &#xff08;1&#xff09;当面操作&#xff1a;按键&#xff0c;旋转/触摸按键&#xff0c;触摸屏 &#xff08;2&#xff09;遥控操作&#xff1a;红外遥控&#xff0c;433M/2.4G无线通信【穿墙能力强】&#xff0c;蓝牙-WIFI-Zigbee-LoRa等…

基于WTMM算法的图像多重分形谱计算matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、WTMM算法概述 4.2、WTMM算法原理 4.2.1 二维小波变换 4.2.2 模极大值检测 4.2.3 多重分形谱计算 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部…

核货宝:服装店收银系统如何选择?收银系统选购指南!

对于各行各业而言&#xff0c;收银系统都是必备的工具。特别是对于像服装店这样的零售门店来说&#xff0c;选择一套适合的收银系统尤为重要。在选择收银系统时&#xff0c;有一些关键的技巧需要注意&#xff0c;以达到软硬件合理搭配、节省开支的目的。下面将分享四个选购服装…

【Java】微服务——微服务介绍和Eureka注册中心

目录 1.微服务介绍2.服务拆分和远程调用2.1.提供者与消费者 3.Eureka注册中心3.1.Eureka的结构和作用3.2.Eureka的结构3.3.搭建Eureka服务3.3.1.引入eureka依赖3.3.2.编写配置文件 3.4.服务注册及拉1&#xff09;引入依赖2&#xff09;配置文件3&#xff09;启动多个user-servi…

C语言编程实现只有一个未知数的两个多项式合并的程序

背景&#xff1a; 直接看题目把&#xff01;就是C语言写两个多项式多项式合并 题目要求&#xff1a; 1. 题目&#xff1a; 编程实现只有一个未知数的两个多项式合并的程序。如&#xff1a; 3x^26x7 和 5x^2-2x9合并结果为8x^24x16。 2. 设计要求 &#xff08;1&#xff09…

【kubernetes】带你了解k8s中PV和PVC的由来

文章目录 1 为什么需要卷(Volume)2 卷的挂载2.1 k8s集群中可以直接使用2.2 需要额外的存储组件2.3 公有云 2 PV(Persistent Volume)3 SC(Storage Class) 和 PVC(Persistent Volume Claim)4 总结 1 为什么需要卷(Volume) Pod是由一个或者多个容器组成的&#xff0c;在启动Pod中…

JavaScript中的模块化编程,包括CommonJS和ES6模块的区别。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 模块化编程概述⭐ CommonJS 模块⭐ ES6 模块⭐ 区别⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、…

git的基础操作

https://blog.csdn.net/a18307096730/article/details/124586216?spm1001.2014.3001.5502 1&#xff1a;使用场景 SVN&#xff0c;如果服务器里面的东西坏掉了&#xff0c;那么就全线崩盘了。 1:基本配置 git config --global user.name “luka” (自己的名字就行) git co…

一篇短小精悍的文章让你彻底明白KMP算法中next数组的原理

以后保持每日一更&#xff0c;由于兴趣较多&#xff0c;更新内容不限于数据结构&#xff0c;计算机组成原理&#xff0c;数论&#xff0c;拓扑学......&#xff0c;所谓&#xff1a;深度围绕职业发展&#xff0c;广度围绕兴趣爱好。往下看今日内容 一.什么是KMP算法 KMP&#x…

ARM-day5作业

.text .global _start _start: 1、设置GPIOE、GPIOF寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28 LDR R0,0x50000a28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) STR R1,[R0]2、设置PE10、PF10、PE8管脚为输出模式 GPIOE_MODER[21:20]->01 0x50006000…

Windows环境下下载安装Elasticsearch和Kibana

Windows环境下下载安装Elasticsearch和Kibana 首先说明这里选择的版本都是7.17 &#xff0c;为什么不选择新版本&#xff0c;新版本有很多坑&#xff0c;要去踩&#xff0c;就用7就够了。 Elasticsearch下载 Elasticsearch是一个开源的分布式搜索和分析引擎&#xff0c;最初由…

基于SVM+TensorFlow+Django的酒店评论打分智能推荐系统——机器学习算法应用(含python工程源码)+数据集+模型(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境方法一方法二 安装其他模块安装MySQL 数据库 模块实现1. 数据预处理1&#xff09;数据整合2&#xff09;文本清洗3&#xff09;文本分词 相关其它博客工程源代码下载其它资料下载 前言 本项目以支…

C++对象模型(7)-- 数据语义学:成员变量偏移值、地址

1、成员变量偏移值 &#xff08;1&#xff09; 成员变量偏移值&#xff0c;就是指这个成员变量的地址离对象首地址偏移了多少字节&#xff0c;这个偏移值在编译完成后是不变的。 class Base { public:int b_i;int b_j; };int main() {Base base;printf(" b_i的偏移值:%…

计算机专业毕业设计项目推荐14-文档编辑平台(SpringBoot+Vue+Mysql)

文档编辑平台&#xff08;SpringBootVueMysql&#xff09; **介绍****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程以及模式&#xff0c;在编写的…

Linux 基金会分叉 Terraform,正式推出 OpenTofu

Linux 基金会宣布推出 OpenTofu&#xff0c;这是一个 Terraform 的开源替代方案&#xff0c;并且分叉自 Terraform。OpenTofu 原名 OpenTF&#xff0c;为所有人提供了一个在中立治理模式下的可靠的开源替代方案。 Terraform 是 HashiCorp 开源的一个安全和高效的用来构建、更改…

内网渗透——哈希传递

文章目录 哈希传递1. 概念1.1 LM1.2 NTLM 2. 原理3. 利用3.1 hash传递浏览上传文件3.2 hash传递获取域控RDP 4. 总结 哈希传递 哈希传递攻击&#xff08;Pass The Hash&#xff09;是基于 NTLM 认证缺陷的一种攻击方式&#xff0c;攻击者可以利用用户的密码哈希值来进行 NTLM …

提升微信小程序MAU,这些方法你需要落地执行

面对激烈的微信小程序竞争,如何提升微信小程序的月活跃用户量(MAU)是每个小程序运营者需要思考的问题。经过分析,提升微信小程序MAU可以从以下几个方面着手。更多提升微信小程序MAU相关&#xff0c;可某薇找我名字。 提升微信小程序MAU 第一,丰富小程序功能,提升用户黏性。可以…

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)

flutter开发实战-video_player插件播放抖音直播实现&#xff08;仅限Android端&#xff09; 在之前的开发过程中&#xff0c;遇到video_player播放视频&#xff0c;通过查看video_player插件描述&#xff0c;可以看到video_player在Android端使用exoplayer&#xff0c;在iOS端…