Files
zvfs/plan/codexplan.md
2026-03-03 14:24:17 +00:00

7.7 KiB
Raw Blame History

ZVFS 高性能框架设计(修订版)

0. 当前实现进展2026-03-03

  • 已落地:
    • stale blob 自愈open/create/unlink/rename 路径)
    • hook 层小写合并per-fd writeback buffer默认 128KB+ 关键系统调用前 flush
  • 仍待重点优化:
    • 小块读路径仍是“同步提交 + 单请求往返 + 拷贝返回”,延迟和吞吐偏弱

1. 现状代码中的关键问题(先于方案)

基于 zvfs.czvfs.hzvfs_hook.c,当前主要瓶颈和风险如下:

  1. 单全局执行上下文串行化

    • 所有 IO 都通过 global_thread + waiter() 同步等待,天然把多线程请求串到一个 SPDK thread。
    • zvfs_t 里只有一个 channel,读写都走这一个 channel无法利用多核并行。
  2. 等待模型是忙轮询CPU 成本高

    • waiter() 用紧循环 spdk_thread_poll(),没有阻塞等待/退避策略。
    • 在高并发小 IO 下,系统容易进入“高 CPU + 低有效 QD”。
  3. 全局元数据无并发保护

    • dirents/fd_table/g_dirs/g_dirfd_table/open_count/file_size 读写没有统一锁。
    • hook 层是多线程入口,当前实现有明显竞态和可见性问题。
  4. 持久化与语义不完整

    • fsync/fdatasync/sync_file_range 对 zvfs fd 基本直接返回 0和数据库预期不一致。
    • meta_load() 只读固定 4KB 文本,规模稍大就截断;meta_save() 也无崩溃一致性保证。
  5. 数据路径的放大和额外开销

    • 小块随机写依赖 read-modify-write无写回缓存、无批量提交、无 IO 合并。
    • per-file dma_buf 增长时可能反复 realloc缺少池化和复用策略。
  6. 可扩展性不足

    • dirent_find/fd_alloc 等是线性扫描。
    • 元数据、目录结构、fd 分配都偏“单点共享结构”,随着文件数/线程数增长会抖动。

2. 对 userplan.md 的补全与修正

plan/userplan.md 的方向TLS + per-thread channel + 缩小全局锁)是正确的,但有几个需要补全的点:

  1. “每个 pthread 一个 spdk_thread”要可配置

    • 对 MySQL 这类线程数可能很大的进程,严格 1:1 会导致线程对象和 channel 爆炸。
    • 建议改为:默认“线程绑定 worker 池N:M支持配置成 1:1 调试模式。
  2. 需要明确“文件句柄跨线程访问”的所有权规则

    • 同一 fd 可能被不同 pthread 使用,必须定义 offset、cache、flush 的同步策略。
  3. batch poll 需要配套“提交队列 + 背压 + 超时”

    • 仅有 pending_queue 不够,必须定义入队失败/队列满/超时处理。
  4. 必须补上 fsync/fdatasync 的严格语义

    • 尤其面向数据库fsync 成功后应保证数据页 + 必要元数据已持久化。
  5. 元数据持久化需要从“文本快照”升级为“日志+检查点”

    • 否则崩溃恢复和规模都不可靠。

3. 新框架设计(面向高性能与可重入改造)

3.1 分层与职责

  • Control Plane全局

    • 管理 mount/unmount、命名空间、inode 元数据、fd 表、恢复日志。
    • 低频操作open/create/unlink/rename/mkdir/rmdir在此层处理。
  • Data Planeworker

    • 处理 read/pread/write/pwrite/fsync 的数据 IO。
    • 每个 worker 持有:spdk_thread + io_channel + submission_queue + completion_queue
  • Persistence Plane元数据持久化

    • 元数据 WALappend-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 handleopen 实例)
    • 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)
    1. 刷新该 fd 对应 inode 的脏数据页;
    2. 若发生扩容,确保 blob 元数据同步完成;
    3. 返回前确认提交完成。
  • fsync(fd)
    • fdatasync 基础上,额外保证需要的命名空间/元数据日志落盘(如 size、rename 可见性)。

3.7 崩溃一致性与恢复

  • meta_journal.logappend-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=1
    • ZVFS_TRACE_META=1
    • ZVFS_WORKER_MODE, ZVFS_IO_WORKERS

4. 关键行为约束(必须保持)

  1. POSIX 语义不回退:openat/rename/unlink/ftruncate/fstat/fsync 的错误码与行为保持一致或更严格。
  2. 在无 root 环境下可跑功能测试(至少支持 Malloc bdev 或已有可用 SPDK 配置)。
  3. 旧接口兼容:外部仍通过 LD_PRELOAD=.../libzvfs.so 使用。
  4. 改造过程可分阶段落地,任何阶段都可独立编译、回归、继续下一阶段。

5. 性能目标(建议)

  • 与当前实现相比:
    • 多线程随机写 IOPS 提升 >= 2x4~16 线程场景)
    • P99 延迟下降 >= 30%
    • CPU busy-poll 占比显著下降(可通过 perf/top 观测)
  • test_single_file_perftest_single_file_random_perf 在同配置下持续稳定,无明显长尾抖动。