- 分布式文件系统
- 对象存储
- 存储
【免费下载链接】seaweedfs
SeaweedFS is a distributed storage system for object storage (S3), file systems, and Iceberg tables, designed to handle billions of files with O(1) disk access and effortless horizontal scaling.
导读
本文基于 test/benchmark/fuse_db 目录下的基准测试套件,完整介绍在 SeaweedFS 的 FUSE 挂载文件系统上运行MySQL(InnoDB)与 SQLite数据库时的两项核心指标:持久性(正常关闭、kill -9意外终止、写入过程中崩溃后,已提交数据是否丢失)与性能(相比本机磁盘的损耗比例)。读完本文,你将掌握这套可复现的基准测试的运行方法、验证机制与源码级实现细节,并理解为什么「小事务 fsync 提交」是 FUSE 文件系统上数据库性能的关键瓶颈。
一、为什么需要这套基准测试
SeaweedFS 的 FUSE 挂载(weed mount)可以把分布式存储以本地文件系统的形态暴露给应用,数据库(如 SQLite、MySQL)可以直接把数据文件放在挂载点上。但用户最关心两个问题:
- 持久性(Durability):数据库通过 fsync 提交的数据,在正常关闭、意外
kill -9、甚至写入过程中崩溃后,是否真的还在? - 性能(Performance):同样的工作负载(批量导入、OLTP 小事务提交、全表扫描、裸 fsync),跑在 FUSE 挂载上比跑在本机磁盘上慢多少?
这套基准套件用约 1 GB 数据集分别跑 SQLite 和 MySQL,并给出单节点(loopback)环境下可复现的答案。
二、测试设计与数据验证机制
2.1 数据载荷:确定性且不可压缩
为了避免存储层压缩「作弊」(比如全零页被 gzip 后不占实际空间),每条数据行携带:
- id:自增主键;
- payload:4 KB 确定性但不可压缩的随机字节(由 id 作为随机种子生成);
- chk:该 payload 的 CRC32 校验值。
1 GB 数据集 = 262,144 行 × 4,096 字节 payload,这些数据会真实地写入 volume 服务器,而不是被压缩掉。从 sqlite_gen.py 可以看到,载荷由random.Random(i).randbytes(4096)生成——同样算法在验证脚本里可以重新计算并与存储的 CRC 比对。
2.2 验证维度:四重检查
每次崩溃恢复后,验证器(sqlite_verify.py / MySQL 侧的mysql_verify)执行四重检查:
| 检查项 | 说明 |
|---|---|
integrity_check/CHECK TABLE | 数据库结构(B-tree / 页面)是否完好 |
| 精确行数 | 与期望值比对(exact 模式) |
| 连续 id 前缀 | 0..count-1 必须连续(atleast 模式) |
| 逐行 CRC 重算 | 每个 payload 重新计算 CRC32 并与存储值比对,检测字节级损坏 |
2.3 崩溃场景与「可靠下界」设计
持久性套件包含三个场景:
| 场景 | 做法 | 验证方式 |
|---|---|---|
| A 正常关闭 | 优雅停 DB → 卸载挂载 → 停集群 → 重启 → 验证 | exact(一行不能多、不能少) |
| B 意外终止 | kill -9同时杀掉 DB、mount、cluster → 重启恢复 → 验证 | exact |
| C 写入中崩溃 | 后台持续写入,进度过半时kill -9全部进程 → 重启 → 验证 | atleast(已提交行必须保留且成连续前缀) |
场景 C 的关键是「可靠下界(lower bound)」:加载器每提交一批事务,就把已提交行数原子写入本机磁盘上的进度文件(写临时文件 + fsync + rename,见 sqlite_gen.py 的write_progress),这样崩溃时就知道至少有多少行已经持久提交,重启后验证器要求实际行数 ≥ 该下界且 id 连续——正在写入但未提交的事务可以回滚,但任何已提交的行都不允许丢失。
三、测试环境与基线说明
原文结果基于以下环境(2026-06-15 采集):
- 单节点;
- macOS arm64 + macFUSE;
- weed 4.34;
- 本机 NVMe 磁盘。
集群以单进程形态运行(weed server同时包含 master + volume + filer),FUSE 挂载通过 loopback 访问 filer。注意:这是单节点 loopback 数字,真实多节点集群(远端 volume、复制)会在每次提交上额外叠加网络 RTT 与副本 fsync。
四、持久性结果:6/6 全部通过
| 场景 | 说明 | SQLite | MySQL |
|---|---|---|---|
| A 正常关闭 | 优雅停库、卸载、停集群、重启、验证 | PASS | PASS |
B 意外终止(kill -9) | kill -9 杀掉 db + mount + cluster,重启恢复验证 | PASS | PASS |
| C 写入中崩溃 | 加载中途 kill -9,重启恢复,验证已提交前缀 | PASS | PASS |
场景 C 验证了正确的恢复行为:正在进行的事务被回滚,所有 fsync 已提交的行以连续前缀的形式完整存活(InnoDB redo 恢复约耗时 9 秒)。
五、性能结果:FUSE vs 本机磁盘
5.1 裸文件系统
| 指标 | 本机 | FUSE | FUSE 慢多少 |
|---|---|---|---|
| 顺序写 + fsync | 2336 MB/s | 369 MB/s | 6.3x |
| 顺序读(热缓存) | 3435 MB/s | 1422 MB/s | 2.4x |
| fsync 延迟 | 0.13 ms | 1.18 ms | 9x |
对应 fsbench.py 的三项微基准:512 MB 顺序写、500 次「4 KB 随机偏移 pwrite + fsync」、热缓存顺序读。
5.2 SQLite(1 GB,journal=DELETE,synchronous=FULL)
| 指标 | 本机 | FUSE | FUSE 慢多少 |
|---|---|---|---|
| 批量导入 | 5.8 s(177 MB/s) | 11.6 s(88 MB/s) | 2.0x |
| OLTP 单行事务 | 1987 tx/s | 171 tx/s | 11.6x |
| 全表扫描(热缓存) | 316 MB/s | 427 MB/s | 基本持平(受 Python 瓶颈限制) |
5.3 MySQL / InnoDB(1 GB,trx_commit=1,flush_method=fsync)
| 指标 | 本机 | FUSE | FUSE 慢多少 |
|---|---|---|---|
| 批量导入 | 24.4 s(42 MB/s) | 32.9 s(31 MB/s) | 1.35x |
| OLTP 单行提交 | 10542 tx/s | 1144 tx/s | 9.2x |
| 全表扫描 | 2419 MB/s | 430 MB/s | 5.6x |
5.4 结论要点
成本主要由 fsync / 提交延迟决定(0.13 ms → 1.18 ms,约 9 倍):每次持久提交都要把 chunk 上传到 volume,并在 loopback 上持久化 filer 的 chunk-manifest。具体表现为:
- 小 fsync 事务:慢约 9–12 倍;
- 批量导入(提交被摊薄):慢 1.3–2 倍;
- 热缓存顺序读:接近本机水平。
六、持久性边界:这套测试测的是什么
需要特别强调的是测试的范围界定:kill -9终止的是进程,而操作系统仍在运行,因此所有已进入 OS page cache 的数据(即所有已 fsync 的数据)都会存活。这忠实地模拟了进程/守护进程崩溃,也正是 6/6 全部通过的原因;但它并不能模拟真正的断电(page cache 随电源一起丢失)。
源码层面的佐证在 weedfs_write.go:FUSE 挂载上传 chunk 时构造的operation.UploadOption不携带 fsync 语义(对照 upload_content.go 中的UploadOption结构体,字段包含Filename、Cipher、IsInputCompressed、WantMd5等,但不存在任何要求 volume 端对.dat做 fsync 的选项)。因此:
- volume 端不会对每次上传的
.dat单独 fsync; - filer 端的 leveldb 大概率也不会逐次写同步。
在真实断电场景下,那些只到达 page cache 的「刚提交」数据可能丢失。README 明确指出,在真实硬件上做 VM 硬复位 / 断电测试,是补上这一缺口的后续计划。
七、运行环境要求
weed位于$PATH(或通过WEED=/path/to/weed指定);- macOS 需要 macFUSE,Linux 需要 libfuse;
python3、sqlite3;- MySQL/MariaDB 安装,并设置
MYSQL_BASE为其安装前缀(macOS Homebrew 默认/opt/homebrew/opt/mysql,目录下需包含bin/mysqld、bin/mysql、bin/mysqladmin;Linux 上如/usr)。对非默认安装路径运行mysql_bench.py时,还需设置MYSQL_BIN=/path/to/mysql。
八、运行方法
运行时的所有产物(集群、挂载、日志)统一放在$SEAWEED_BENCH_WORK目录(默认/tmp/seaweedfs_fuse_db_bench),不会污染仓库:
cd test/benchmark/fuse_db bash bin/run_sqlite.sh > results/sqlite.log 2>&1 # SQLite 持久性套件 bash bin/run_mysql.sh > results/mysql.log 2>&1 # MySQL 持久性套件 bash bin/compare.sh > results/compare.log 2>&1 # FUSE vs 本机性能对比8.1 隔离与安全设计
测试脚手架通过 lib.sh 只管理自己启动的进程:
- 使用 pidfile 追踪进程,只在非默认端口运行(master 9555 / volume 9560 / filer 9565、filer gRPC 19565、mysqld 3308);
- 从不执行
pkill weed,机器上运行的其他 SeaweedFS 实例完全不受影响; - 提供
clean_state彻底清理本次测试状态(只清自己的目录与端口,绝不触碰用户数据)。
8.2 集群与挂载的关键参数
从 lib.sh 可以看到被测集群的启动方式:单进程weed server,-volume.max=50 -master.volumeSizeLimitMB=1024,挂载使用weed mount -dirAutoCreate -cacheDir=...。MySQL 侧固定使用严格持久化配置:innodb_flush_log_at_trx_commit=1、innodb_flush_method=fsync、innodb_doublewrite=ON,确保每次提交都通过 FUSE 层 fsync redo log,从而测出 FUSE 栈对提交路径的真实影响。
九、脚本文件速览
| 脚本 | 职责 |
|---|---|
| bin/lib.sh | 集群 / 挂载 / mysqld 生命周期助手(启动、停止、kill、状态清理) |
| bin/run_sqlite.sh | SQLite 1 GB 加载 + A/B/C 崩溃场景 |
| bin/run_mysql.sh | MySQL 1 GB 加载 + A/B/C 崩溃场景 |
| bin/compare.sh | FUSE vs 本机性能对比驱动(同一磁盘、同样的持久化设置) |
| bin/sqlite_gen.py | 确定性不可压缩 SQLite 加载器(带进度 / 参考文件) |
| bin/sqlite_verify.py | SQLite 完整性 + 行数 + 前缀 + 逐行 CRC 验证器 |
| bin/sqlite_bench.py | SQLite 加载 / OLTP / 扫描计时探针 |
| bin/mysql_bench.py | MySQL 加载 / OLTP / 扫描计时探针(经 mysql CLI 驱动) |
| bin/fsbench.py | 裸顺序写 / fsync 延迟 / 顺序读微基准 |
9.1 两个值得注意的实现细节
- SQLite 加载器(sqlite_gen.py)在把
synchronous/journal_mode拼进 PRAGMA 前会做白名单校验,拼错参数会直接报错退出,避免在不知情的情况下悄悄削弱持久化设置而让崩溃测试结论失真。 - MySQL 载荷(run_mysql.sh)用
AES_ENCRYPT(REPEAT('a',4080), SHA2(id,256))生成确定性且不可压缩的 4 KB 密文,同一表达式同时生成 payload 与 chk,保证验证端重算的 CRC32 必然等于存储值。
十、复现与基线参考
README 中给出的数据是参考基线:运行输出写入results/*.log,该目录已被仓库级*.loggitignore 规则忽略、不纳入版本控制。重新运行上述脚本即可在本机复现,并对照你自己的硬件(本机磁盘、FUSE 版本、weed 版本)观察损耗比例。
结语
这套基准测试给出了一个清晰、可复现的答案:SeaweedFS FUSE 挂载能安全地承载 SQLite / MySQL 这类数据库(6/6 持久性场景通过,含写入中崩溃恢复),性能损耗集中在 fsync 提交路径上——小事务场景慢约 9–12 倍,批量导入慢 1.3–2 倍,顺序读基本持平。如果你计划把数据库放进 FUSE 挂载,请务必理解这里的断电边界(挂载上传不做 volume 端 fsync),并结合真实集群的网络延迟与副本 fsync 开销做整体评估。
- 分布式文件系统
- 对象存储
- 存储
【免费下载链接】seaweedfs
SeaweedFS is a distributed storage system for object storage (S3), file systems, and Iceberg tables, designed to handle billions of files with O(1) disk access and effortless horizontal scaling.
相关推荐
YCSB核心工作负载详解:数据库性能测试的标准范式
YCSB核心工作负载详解:数据库性能测试的标准范式 引言 在数据库系统评估领域,YCSB(Yahoo! Cloud Serving Benchmark)提供了一
性能测试cal.com性能测试:k6负载测试与性能基准
cal.com性能测试:k6负载测试与性能基准 测试框架概述 cal.com项目采用Grafana k6构建了完整的性能测试套件,专注于验证预订流程在各种负载条
后端前端企业应用Eidos性能基准:负载测试与优化
Eidos性能基准:负载测试与优化 概述 Eidos作为离线的Notion替代方案,其性能表现直接影响用户体验。本文深入分析Eidos的性能基准测试方法、负载测
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考