小块写缓存优化

This commit is contained in:
2026-03-03 14:24:17 +00:00
parent c33a694bd8
commit 975afaf3f0
14 changed files with 690 additions and 1298 deletions

108
plan/userplan.md Normal file
View File

@@ -0,0 +1,108 @@
### 架构目标
- 通过 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 |