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 內容)**仍懶加載**,不會一次性讀空磁盤。