readers[i] =
new SegmentReader(sis.info(i), sis.getIndexCreatedVersionMajor(), IOContext.READ);
这个方法已经把所有的文件都读完了么?
没有“读完”,但已经**全部“打开”**了。
| 动作 | 是否发生 |
|---|---|
| **打开文件句柄 / mmap** | ✅ 立即完成 |
| **读取文件头、校验魔数、版本** | ✅ 立即完成 |
| **把整个文件内容拷贝到内存** | ❌ **不会** |
| **读取所有倒排表、doc values、stored fields 内容** | ❌ **按需 lazily** |
### 具体发生了什么
1. `new SegmentReader(...)` → `new SegmentCoreReaders(...)`
这一步对 **每一个涉及的索引文件**(`.tim/.tip/.doc/.fdt/.fdx/.dim/.dvd/.vex/.vec…`)执行:
- `directory.openInput(...)` 或 `cfsDir.slice(...)`
- 读文件头 4~16 字节验证魔数、版本号、校验和
- 把 `IndexInput`(文件句柄或 mmap 区域)包装成对应的 `*Reader` 对象
→ **文件已打开,指针处于文件起始。**
2. 真正的倒排列表、文档值、向量、stored fields **内容**
只有在搜索阶段调用:
- `TermsEnum.next()`
- `StoredFieldsVisitor.visitDocument(docID)`
- `PointValues.intersect(...)`
- `VectorValues.vectorValue(docID)`
时,才从刚才打开的 `IndexInput` 中 **按需 seek / 顺序读取** 对应块。
### 一句话总结
> `SegmentReader` 构造阶段 **只完成“打开文件 + 读取头部/元数据”**;
> 所有数据内容 **按需惰性读取**,不会一次性把整个文件读进内存。
只是把一些元数据文件读了是吧?
**不是“只是”读元数据文件,而是把所有索引文件的“头部/元数据”都读了,并把底层文件句柄全部打开**;真正的数据主体仍留在磁盘,按需懒加载。
| 文件类型 | 构造阶段已读取 | 构造阶段已打开 | 数据主体是否一次性读入 |
| --- | --- | --- | --- |
| `.fnm` | ✅ 全部 | ✅ | ✅(很小) |
| `.fdt/.fdx` | ✅ 文件头 | ✅ | ❌ |
| `.tim/.tip/.doc` 等倒排 | ✅ 文件头 | ✅ | ❌ |
| `.dim/.dii`(BKD) | ✅ 文件头+根节点元数据 | ✅ | ❌ |
| `.dvd/.dvm`(DocValues) | ✅ 文件头+元数据 | ✅ | ❌ |
| `.vex/.vec`(HNSW) | ✅ 文件头+元数据 | ✅ | ❌ |
所以:
- **元数据 + 文件头** 已读(为了校验、建立内存结构)。
- **所有文件句柄已打开**(后续 seek/读取零成本)。
- **数据主体**(倒排表、BKD 块、向量、stored fields 内容)**仍懒加载**,不会一次性读空磁盘。