【lucene】MMapDirectory 在FSDirectory基础上干了啥?

一句话:  
**MMapDirectory 只是把 “读写文件” 从普通的 FileChannel I/O 换成了 mmap(内存映射),其余所有逻辑(文件创建、删除、锁、pending_segments 处理等)都和 FSDirectory 共用同一套代码**。

---

### 1. FSDirectory 已经做了什么  
FSDirectory 提供了:  
- 创建/打开/删除文件的统一实现(createOutput / openInput / deleteFile)  
- 对 pendingDeletes、文件锁、临时文件的处理  
- 可插拔的 `FSIndexOutput`/`FSIndexInput` 实现(用 `FileChannel.read/write` 做真正的 I/O)  

---

### 2. MMapDirectory 只改了 I/O 实现  
```java
public final class MMapDirectory extends FSDirectory {
    ...
    @Override
    public IndexInput openInput(String name, IOContext context) throws IOException {
        ensureOpen();
        Path path = directory.resolve(name);
        // 用 mmap 而不是 FileChannel.read
        return new MMapIndexInput(name, path, chunkSizePower, preload, useUnmapHack);
    }

    /* IndexOutput 仍然是 FSIndexOutput(FileChannel.write),
       因为 mmap 写文件需要 RandomAccessFile + rw,且性能不一定更好,
       Lucene 只在“读”路径用 mmap,“写”路径保留 FileChannel。 */
}
```

- **读文件** → `MMapIndexInput`:把文件映射到 `MappedByteBuffer`,后续读操作就是内存访问。  
- **写文件** → 仍用 `FSIndexOutput`(FileChannel.write),因为:  
  - mmap 写需要 `RandomAccessFile` + `MapMode.READ_WRITE`,且每次扩展文件长度要重新映射,成本高;  
  - 写通常是顺序大块写入,FileChannel 反而更快。  

---

### 3. 额外提供的调优参数  
MMapDirectory 额外暴露:  
- `setPreload(true)`:在 map 后立即 `madvise(MADV_WILLNEED)`,减少首次访问缺页。  
- `setUseUnmapHack(true)`:用 Cleaner 或 Unsafe 手动 `unmap`,避免 64-bit JVM 下映射残留导致 “无法删除文件” 的问题(Windows 上尤其重要)。  

---

### 总结对比

| 维度            | FSDirectory 默认实现 | MMapDirectory                      |
|-----------------|----------------------|------------------------------------|
| Directory 框架  | ✅ 同一套             | ✅ 继承 FSDirectory                |
| 读文件          | FileChannel.read     | mmap(MappedByteBuffer)          |
| 写文件          | FileChannel.write    | 同 FSDirectory(未用 mmap)       |
| 额外调优        | 无                   | preload、unmapHack、chunkSizePower |
| 适用场景        | 通用                 | 读多写少,64-bit、大索引、随机查  |

因此:**MMapDirectory = FSDirectory + 读路径换成 mmap,其余完全复用**。

对,**在 open reader 的环节差异最大**:

1. 当你调用  
   `DirectoryReader.open(directory)` 或 `IndexReader.open(...)`  
   最终都会走到 `SegmentCoreReaders` → `openInput(name, IOContext.READONCE / RANDOM / MERGE)` 去为每个段文件(`.cfs`, `.fdx`, `.tim`, `.doc`, …)创建 `IndexInput`。

2. 如果目录是:
   - **FSDirectory** → 返回 `FSIndexInput`,底层是 `FileChannel.read`  
   - **MMapDirectory** → 返回 `MMapIndexInput`,底层是 `MappedByteBuffer`(mmap)

3. **差异只在读阶段**  
   - 写文件时两者都走 `FSIndexOutput`(FileChannel.write),没有 mmap。  
   - 因此只有 **reader 打开后** 的随机访问、倒排表遍历、跳表查询等读操作,才会体验到 mmap 带来的:
     - 地址空间直接映射,减少一次内核→用户空间拷贝  
     - 大文件分段映射,降低 GC 压力  
     - 预读/缓存效果由 OS 页缓存自动完成

4. **对上层 API 没区别**  
   上层代码(`IndexSearcher.search`, `TermsEnum`, `StoredFields`…)感知不到,只是底层 `IndexInput` 实现不同。

一句话:**writer 阶段无差异;reader 阶段因 mmap 而提速,尤其随机读多的查询**。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值