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

109 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### 架构目标
- 通过 LD_PRELOAD hook POSIX 文件操作open/read/write/pread/pwrite/close/fsync 等),将 MySQL 的数据文件 IO 重定向到 SPDK Blobstore。
- 最大化性能:绕过内核、利用多核并发、低延迟、小块写合并。
- 核心原则:**每个 pthread 拥有独立的 SPDK 执行上下文**,全局共享底层存储资源。
### 全局资源(进程级别,唯一一份)
- `zvfs_t *g_fs`:文件系统实例,包含:
- `struct spdk_blob_store *bs`:全局 Blobstore通过 spdk_bs_load/init 创建)。
- bdevNvme0n1 或 Malloc0通过 JSON 配置加载)。
- 全局元数据dirents 数组zvfs_dirent_t *[]、fd_tablezvfs_file_t *[]、openfd_count。
- 保护全局元数据的锁pthread_rwlock_t g_meta_lock读多写少场景
- 全局初始化标志:`bool g_mounted``bool g_env_inited`
- pthread_key_t 用于线程本地存储:`g_thread_local_key`(带 destructor
### 线程本地资源(每个 pthread 独占一份,通过 TLS 实现)
每个 pthread 拥有以下私有状态,存储在结构体 `thread_local_zvfs_t` 中:
```c
typedef struct {
struct spdk_thread *thread; // 本线程专属的 SPDK thread
struct spdk_io_channel *channel; // 本线程专属的 IO channel绑定到 g_fs->bs
TAILQ_HEAD(, io_ctx) pending_queue; // 本线程的 pending IO 队列,用于 batch poll
// 可选扩展:
// struct dma_buf_pool *dma_pool; // per-thread DMA buf 复用池
// struct page_cache *local_cache; // 如果需要 per-thread cache
} thread_local_zvfs_t;
```
- **创建时机**lazy第一次 IO 时调用 `get_thread_local()`)。
- **存储方式**:通过 `pthread_setspecific(g_thread_local_key, tl)` 绑定到当前 pthread。
- **销毁时机**pthread 退出时TLS destructor 自动调用:
- spdk_bs_free_io_channel(channel)
- spdk_thread_exit + poll until exited + spdk_thread_destroy
### 核心函数get_thread_local()
```c
thread_local_zvfs_t *get_thread_local(void) {
// 确保 key 已创建(只执行一次)
pthread_once(&g_key_once, init_thread_key);
thread_local_zvfs_t *tl = pthread_getspecific(g_thread_local_key);
if (tl == NULL) {
tl = calloc(1, sizeof(*tl));
tl->thread = spdk_thread_create("zvfs_worker", NULL);
tl->channel = spdk_bs_alloc_io_channel(g_fs->bs);
TAILQ_INIT(&tl->pending_queue);
pthread_setspecific(g_thread_local_key, tl);
}
return tl;
}
```
### 工作流程(每个 pthread 独立执行)
1. **线程首次进入 IO 操作**
- 调用 `get_thread_local()` → 创建并绑定 thread + channel。
- 如果 !g_mounted → 调用 zvfs_ensure_mounted()(使用当前 thread 进行 poll 完成 mount
2. **元数据操作open/unlink/mkdir/rmdir/rename 等)**
- 加读锁g_meta_lock检查/修改全局 dirents、dirs、fd_table。
- 创建/查找 zvfs_file_t调用 zvfs_create/zvfs_open使用当前 thread 同步等待)。
- 分配伪 fd记录到全局 fd_table。
- 释放锁。
3. **读操作read/pread**
- 获取当前 tl = get_thread_local()。
- spdk_set_thread(tl->thread)。
- 如果小读 + cache hit → 直接 memcpy 返回。
- 否则:创建 io_ctx加入 tl->pending_queue。
- 调用 spdk_blob_io_read(..., tl->channel, ...)。
- 执行 batch_poll(tl, my_ctx)
- while (!my_ctx->done) spdk_thread_poll(tl->thread, 0, 0);
- 从 dma_buf 拷贝到用户 buf。
4. **写操作write/pwrite**
- 获取 tl。
- spdk_set_thread(tl->thread)。
- 如果小写 → patch per-file page cachedirty标记 dirty返回延迟写
- 如果 cache 满或大写 → flush dirty pagesbatch spdk_blob_io_writev用 tl->channel
- 创建 io_ctx → 加入 pending_queue → submit write → batch_poll。
5. **fsync**
- flush per-file dirty cachebatch writev + spdk_blob_sync_md
- 使用当前 tl->thread poll 等待完成。
6. **close**
- fsyncflush cache
- zvfs_close用当前 tl->thread 同步)。
- 释放 fd加锁更新全局 fd_table
### 性能关键机制
- **独立 poll**:每个 pthread 用自己的 spdk_thread 独立 poll无跨线程消息。
- **batch poll**:一个 poll 循环可完成多个 pending IO提升有效 QD。
- **page cache**per-file 4K dirty pageshashmap合并小写减少 write amplification。
- **channel per-thread**:避免全局 channel 争用,每个线程独立提交 IO。
- **最小全局锁**只在元数据修改时短时加锁rwlockIO 操作无锁。
### 资源所有权总结表
| 资源类型 | 所有权 | 数量 | 创建时机 | 销毁时机 |
|----------------------|--------------|------------|------------------------|------------------------------|
| bdev | 全局 | 1 | zvfs_ensure_mounted | zvfs_umount |
| blobstore (bs) | 全局 | 1 | zvfs_ensure_mounted | zvfs_umount |
| zvfs_t / g_fs | 全局 | 1 | zvfs_ensure_mounted | zvfs_umount + free |
| dirents / fd_table | 全局 | 1 | meta_load | zvfs_umount + free |
| spdk_thread | per-pthread | = pthread 数 | 首次 get_thread_local | pthread 退出destructor |
| io_channel | per-pthread | = pthread 数 | 首次 get_thread_local | pthread 退出destructor |
| pending_queue | per-pthread | = pthread 数 | 首次 get_thread_local | pthread 退出 |
| page cache | per-file | per open fd| open 时 lazy | close 时 flush + free |