小块写缓存优化
This commit is contained in:
108
plan/userplan.md
Normal file
108
plan/userplan.md
Normal 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 创建)。
|
||||
- bdev(Nvme0n1 或 Malloc0,通过 JSON 配置加载)。
|
||||
- 全局元数据:dirents 数组(zvfs_dirent_t *[])、fd_table(zvfs_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 cache(dirty),标记 dirty,返回(延迟写)。
|
||||
- 如果 cache 满或大写 → flush dirty pages(batch spdk_blob_io_writev,用 tl->channel)。
|
||||
- 创建 io_ctx → 加入 pending_queue → submit write → batch_poll。
|
||||
|
||||
5. **fsync**
|
||||
- flush per-file dirty cache(batch writev + spdk_blob_sync_md)。
|
||||
- 使用当前 tl->thread poll 等待完成。
|
||||
|
||||
6. **close**
|
||||
- fsync(flush 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 pages(hashmap),合并小写,减少 write amplification。
|
||||
- **channel per-thread**:避免全局 channel 争用,每个线程独立提交 IO。
|
||||
- **最小全局锁**:只在元数据修改时短时加锁(rwlock),IO 操作无锁。
|
||||
|
||||
### 资源所有权总结表
|
||||
|
||||
| 资源类型 | 所有权 | 数量 | 创建时机 | 销毁时机 |
|
||||
|----------------------|--------------|------------|------------------------|------------------------------|
|
||||
| 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 |
|
||||
Reference in New Issue
Block a user