7.7 KiB
7.7 KiB
ZVFS 高性能框架设计(修订版)
0. 当前实现进展(2026-03-03)
- 已落地:
- stale blob 自愈(open/create/unlink/rename 路径)
- hook 层小写合并(per-fd writeback buffer,默认 128KB)+ 关键系统调用前 flush
- 仍待重点优化:
- 小块读路径仍是“同步提交 + 单请求往返 + 拷贝返回”,延迟和吞吐偏弱
1. 现状代码中的关键问题(先于方案)
基于 zvfs.c、zvfs.h、zvfs_hook.c,当前主要瓶颈和风险如下:
-
单全局执行上下文串行化
- 所有 IO 都通过
global_thread+waiter()同步等待,天然把多线程请求串到一个 SPDK thread。 zvfs_t里只有一个channel,读写都走这一个 channel,无法利用多核并行。
- 所有 IO 都通过
-
等待模型是忙轮询,CPU 成本高
waiter()用紧循环spdk_thread_poll(),没有阻塞等待/退避策略。- 在高并发小 IO 下,系统容易进入“高 CPU + 低有效 QD”。
-
全局元数据无并发保护
dirents/fd_table/g_dirs/g_dirfd_table/open_count/file_size读写没有统一锁。- hook 层是多线程入口,当前实现有明显竞态和可见性问题。
-
持久化与语义不完整
fsync/fdatasync/sync_file_range对 zvfs fd 基本直接返回 0,和数据库预期不一致。meta_load()只读固定 4KB 文本,规模稍大就截断;meta_save()也无崩溃一致性保证。
-
数据路径的放大和额外开销
- 小块随机写依赖 read-modify-write;无写回缓存、无批量提交、无 IO 合并。
- per-file
dma_buf增长时可能反复 realloc,缺少池化和复用策略。
-
可扩展性不足
dirent_find/fd_alloc等是线性扫描。- 元数据、目录结构、fd 分配都偏“单点共享结构”,随着文件数/线程数增长会抖动。
2. 对 userplan.md 的补全与修正
plan/userplan.md 的方向(TLS + per-thread channel + 缩小全局锁)是正确的,但有几个需要补全的点:
-
“每个 pthread 一个 spdk_thread”要可配置
- 对 MySQL 这类线程数可能很大的进程,严格 1:1 会导致线程对象和 channel 爆炸。
- 建议改为:默认“线程绑定 worker 池(N:M)”,支持配置成 1:1 调试模式。
-
需要明确“文件句柄跨线程访问”的所有权规则
- 同一 fd 可能被不同 pthread 使用,必须定义 offset、cache、flush 的同步策略。
-
batch poll 需要配套“提交队列 + 背压 + 超时”
- 仅有
pending_queue不够,必须定义入队失败/队列满/超时处理。
- 仅有
-
必须补上 fsync/fdatasync 的严格语义
- 尤其面向数据库:fsync 成功后应保证数据页 + 必要元数据已持久化。
-
元数据持久化需要从“文本快照”升级为“日志+检查点”
- 否则崩溃恢复和规模都不可靠。
3. 新框架设计(面向高性能与可重入改造)
3.1 分层与职责
-
Control Plane(全局)
- 管理 mount/unmount、命名空间、inode 元数据、fd 表、恢复日志。
- 低频操作(open/create/unlink/rename/mkdir/rmdir)在此层处理。
-
Data Plane(worker)
- 处理 read/pread/write/pwrite/fsync 的数据 IO。
- 每个 worker 持有:
spdk_thread + io_channel + submission_queue + completion_queue。
-
Persistence Plane(元数据持久化)
- 元数据 WAL(append-only)+ 周期 checkpoint。
- 保障崩溃恢复和 fsync 语义。
3.2 全局运行时结构
typedef struct {
// init/mount 生命周期
pthread_once_t init_once;
pthread_mutex_t mount_mu;
_Atomic int mount_state; // UNINIT/INITING/READY/FAILED/STOPPING
// core spdk objects
struct spdk_blob_store *bs;
struct spdk_bs_dev *bs_dev;
// metadata indexes
pthread_rwlock_t inode_rwlock;
inode_table_t *inode_by_path; // hash map: path -> inode
inode_table_t *inode_by_blobid; // hash map: blobid -> inode
pthread_rwlock_t fd_rwlock;
fd_table_t *fd_table; // pseudo fd -> file handle
// durability
meta_journal_t *journal; // WAL + checkpoint
// worker routing
worker_pool_t *workers; // configurable N workers
} zvfs_runtime_t;
3.3 worker 模型(建议默认 N:M,可切 1:1)
- 默认:
worker_count = min(online_cpu, ZVFS_IO_WORKERS)。 - 线程第一次进入时做 TLS 绑定:
pthread_id -> worker_id(固定绑定,减少迁移)。 - 每个 worker 独占一个 io_channel,避免全局 channel 争用。
- 等待机制:优先
eventfd/futex + poll混合,避免纯忙轮询。
说明:若用户确认线程数有限,可配置
ZVFS_WORKER_MODE=THREAD_LOCAL切 1:1,以追求极致低延迟。
3.4 元数据模型
inode(文件级共享对象)blob_id, logical_size, allocated_clusters, link/open_ref, flags- 每 inode 一把细粒度锁(mutex/spin + 原子字段)。
file handle(open 实例)inode*,flags,current_offset,handle-local state。
- 路径索引与 blob 索引用哈希表替代线性数组。
- 目录树从
g_dirs[]升级为前缀树或 hash+parent 索引,避免全表扫描。
3.5 IO 路径设计
Read/Pread
- 快路径:命中页缓存(clean/dirty)直接拷贝。
- 慢路径:提交到绑定 worker。
- 对齐大读支持直接 DMA 到用户对齐缓冲(满足约束时)。
Write/Pwrite
- 小块随机写:写入 per-inode 页缓存(4KB 粒度),标记 dirty。
- 大块或顺序写:绕过缓存直写(或写穿策略),减少二次拷贝。
- 扩容策略:按 chunk 预分配(例如 1~8MB)减少
resize + sync_md频率。 - flush 策略:
- 后台刷脏(阈值/时间)
- 前台 fsync 强制刷
- 合并连续页为 writev/batch IO
3.6 fsync/fdatasync 语义(数据库场景)
fdatasync(fd):- 刷新该 fd 对应 inode 的脏数据页;
- 若发生扩容,确保 blob 元数据同步完成;
- 返回前确认提交完成。
fsync(fd):- 在
fdatasync基础上,额外保证需要的命名空间/元数据日志落盘(如 size、rename 可见性)。
- 在
3.7 崩溃一致性与恢复
meta_journal.log(append-only,带 magic/version/CRC/seq)。- 操作记录:
CREATE/UNLINK/RENAME/TRUNCATE/SIZE_UPDATE/ALLOC_UPDATE。 - 启动恢复:
checkpoint -> replay WAL。 - 周期 checkpoint(按时间或日志大小触发),避免恢复时间无限增长。
3.8 锁策略与死锁规约
- 固定锁顺序:
fd_table lock -> inode lock -> journal lock。 - IO 快路径不拿全局写锁。
- 元数据读多写少:读写锁 + inode 细粒度锁组合。
3.9 可观测与调优
- 统计项(至少):
- read/write IOPS、带宽、P50/P99 延迟
- cache hit ratio、dirty page 数
- flush 次数、merge 比例、resize 次数
- queue depth、排队延迟
- debug 开关:
ZVFS_TRACE_IO=1ZVFS_TRACE_META=1ZVFS_WORKER_MODE,ZVFS_IO_WORKERS
4. 关键行为约束(必须保持)
- POSIX 语义不回退:
openat/rename/unlink/ftruncate/fstat/fsync的错误码与行为保持一致或更严格。 - 在无 root 环境下可跑功能测试(至少支持 Malloc bdev 或已有可用 SPDK 配置)。
- 旧接口兼容:外部仍通过
LD_PRELOAD=.../libzvfs.so使用。 - 改造过程可分阶段落地,任何阶段都可独立编译、回归、继续下一阶段。
5. 性能目标(建议)
- 与当前实现相比:
- 多线程随机写 IOPS 提升 >= 2x(4~16 线程场景)
- P99 延迟下降 >= 30%
- CPU busy-poll 占比显著下降(可通过 perf/top 观测)
test_single_file_perf、test_single_file_random_perf在同配置下持续稳定,无明显长尾抖动。