summaryrefslogtreecommitdiff
path: root/content
diff options
context:
space:
mode:
Diffstat (limited to 'content')
-rw-r--r--content/juicefs-gc/db-delfiles.pngbin0 -> 528719 bytes
-rw-r--r--content/juicefs-gc/gc-stats.pngbin0 -> 1075359 bytes
-rw-r--r--content/juicefs-gc/index.md86
-rw-r--r--content/juicefs-gc/mem-pprof.pngbin0 -> 85166 bytes
-rw-r--r--content/juicefs-gc/oom.pngbin0 -> 117932 bytes
-rw-r--r--content/juicefs-gc/storage-incr.pngbin0 -> 58862 bytes
6 files changed, 86 insertions, 0 deletions
diff --git a/content/juicefs-gc/db-delfiles.png b/content/juicefs-gc/db-delfiles.png
new file mode 100644
index 0000000..82f3096
--- /dev/null
+++ b/content/juicefs-gc/db-delfiles.png
Binary files differ
diff --git a/content/juicefs-gc/gc-stats.png b/content/juicefs-gc/gc-stats.png
new file mode 100644
index 0000000..6abb509
--- /dev/null
+++ b/content/juicefs-gc/gc-stats.png
Binary files differ
diff --git a/content/juicefs-gc/index.md b/content/juicefs-gc/index.md
new file mode 100644
index 0000000..8f18c8f
--- /dev/null
+++ b/content/juicefs-gc/index.md
@@ -0,0 +1,86 @@
++++
+title = "JuiceFS 与如何倾倒垃圾"
+date = 2026-09-18
+authors = ["135e2 (Mole Shang)"]
+[extra]
+enable_img_caption = true
+[taxonomies]
+tags = ["技术"]
++++
+
+又名:如何优雅地删除 1.3 PB 的垃圾文件。
+
+<!--more-->
+
+## 背景
+
+> JuiceFS 是一款面向云原生设计的高性能分布式文件系统,在 Apache 2.0 开源协议下发布。提供完备的 POSIX 兼容性,可将几乎所有对象存储接入本地作为海量本地磁盘使用,亦可同时在跨平台、跨地区的不同主机上挂载读写。
+
+[东南大学开源镜像站 (Intranet)](https://mirrors.seu.edu.cn) 于 2025/09 迁移到新的 JuiceFS 存储后端,[整体架构](https://os.nju.edu.cn/media/2026/slides/03-%E9%95%9C%E5%83%8F%E7%AB%99%E4%BA%91%E5%8E%9F%E7%94%9FFS%E4%B8%8E%E5%8F%AF%E8%A7%82%E6%B5%8B%E6%80%A7%E5%AE%9E%E8%B7%B5.pdf)优化后性能符合预期。但经历过一次严重的[生产事故](https://idawnlight.com/archives/juicefs-rescue-from-meta-dump/)后,出于谨慎考虑一直没有升级版本。
+
+在 2026/08 鼓起勇气平滑升级至 1.4.1 后,后端服务器出现了稳定的存储增长,经排查后确认为 `juicefs gc` 服务没有正常定期运行。同时,由于镜像站的业务涉及每天千万级的元数据读写,一个月内产生了 >100GiB 的数据库存储占用并持续增加。
+
+![](./storage-incr.png)
+
+因为现在已经没有人收垃圾了,参考 [JuiceFS 内部实现](https://juicefs.com/docs/zh/community/internals/#delfiles)观察各个表结构,可以看到 DB 中已经积累了约4亿个待清理的文件:
+
+![哈哈,发臭了](./db-delfiles.png)
+
+当我们一如既往地尝试重新执行 `gc` 以清理过期元数据时,不出意外地出意外了:回收过程吃掉了太多内存,直接导致系统 OOM 重启。
+
+## 定位
+
+> 因为 juicefs gc 命令会扫描对象存储中的所有对象,所以对于数据量较大的文件系统执行这个命令会有一定开销。另外使用此命令之前请确保您不需要回滚到旧版本元数据,并且建议您备份对象存储数据。
+>
+> —— [官方文档提示](https://juicefs.com/docs/zh/community/administration/status_check_and_maintenance#gc)
+
+再一次地,官方文档对此讳莫如深:主线开发版的确提到了[可能的性能优化](https://github.com/juicedata/juicefs/issues/7420),但在尝试给我们的系统移植所有相关 patch 并启用了 16G swapfile 之后,依然无法解决 gc 吃掉大量内存的问题。
+
+![](./oom.png)
+
+万不得已,还是得读源码 :(
+
+简单修改一下程序,我们可以很方便地开一个用来监控的 Goroutine,在检测到 OOM 前收集一份程序的堆栈采样。不难发现大部分内存都分给了 `ScanPendingFiles` 下的 `xorm.(*Session).Find`,它的逻辑大概是这样的:
+
+![](./mem-pprof.png)
+
+```go
+func (m *dbMeta) scanPendingFiles(ctx Context, scan pendingFileScan) error {
+ // ...
+ var dfs []delfile
+ if err := m.simpleTxn(ctx, func(s *xorm.Session) error {
+ if ok, err := s.IsTableExist(&delfile{}); err != nil {
+ return err
+ } else if !ok {
+ return nil
+ }
+ return s.Find(&dfs)
+ }); err != nil {
+ return err
+ }
+
+ for _, ds := range dfs {
+ if _, err := scan(ds.Inode, ds.Length, ds.Expire); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+```
+
+根据 [XORM](https://xorm.io/docs/chapter-05/4.find/) 文档所示,`Find` 等同于直接把整个表里的内容都塞到了这个内存的 `delfile` 数组里。之前执行千万级的加载或许还看不出问题,但考虑到我们上亿的数据量,OOM 也就不足为奇了。
+
+做过优化的朋友们显然能一眼看出问题所在:这个查询完全可以是流式的,类似 JS 的 `filter`,事实上 XORM 也支持[流水线遍历](https://xorm.io/docs/chapter-05/6.iterate/);而更诡异的是在 JuiceFS 仓库的同一个文件里相隔不远的地方也正使用了[类似的 `Iterate` 模式](https://github.com/juicedata/juicefs/blob/7ac228ebca24885736521b2688ccf0478b903e9b/pkg/meta/sql.go#L4045)去处理 `chunk` 表的遍历——不禁让人善意揣测是否是故意留下的坑以推销他的企业版支持。
+
+## 恢复
+
+参考已有的实现进行[修复](https://github.com/seu-mirrors/juicefs/commit/6acb36fe6da09b9e23d82e2399b7ffcea4370672)后,内存占用终于回到了可接受的水平;但由于 `jfs_delfile` 这张表的数据量还是上亿级的,跑了近一周才最终完成 `gc`:
+
+![当然这里的1.3PB是指过期被回收的文件总大小](./gc-stats.png)
+
+而这还没完,PostgreSQL 内部的垃圾回收需要执行 `VACUUM FULL`,其要求[数据库本身的独占锁](https://www.postgresql.org/docs/current/sql-vacuum.html),显然对生产业务是不可接受的;还是使用 [`pg_repack`](https://reorg.github.io/pg_repack/) 完成了磁盘回收。
+
+---
+
+这件事情告诉我们,及时保持环境卫生还是很重要的。 \ No newline at end of file
diff --git a/content/juicefs-gc/mem-pprof.png b/content/juicefs-gc/mem-pprof.png
new file mode 100644
index 0000000..45c4b38
--- /dev/null
+++ b/content/juicefs-gc/mem-pprof.png
Binary files differ
diff --git a/content/juicefs-gc/oom.png b/content/juicefs-gc/oom.png
new file mode 100644
index 0000000..7422926
--- /dev/null
+++ b/content/juicefs-gc/oom.png
Binary files differ
diff --git a/content/juicefs-gc/storage-incr.png b/content/juicefs-gc/storage-incr.png
new file mode 100644
index 0000000..0c88973
--- /dev/null
+++ b/content/juicefs-gc/storage-incr.png
Binary files differ