文章目录
- 背景
- 问题1,Filesystem Cache 里放的是啥
- 问题2,哪些查询它们会受益于文件系统缓存
- 问题3 查询分析
背景
对于es 优化来说常常看到会有一条结论给,给 JVM Heap 最多不超过物理内存的 50%,且不要超过 31GB(避免压缩指针失效)。剩下的内存尽量留给操作系统做文件系统缓存。es查询重度依赖
Filesystem Cache。
问题1,Filesystem Cache 里放的是啥
文件系统缓存保存的是什么?
文件系统缓存由操作系统管理,而不是 Elasticsearch。
它缓存的是从磁盘读取的文件内容,比如 Lucene 构建的倒排索引文件(segment files)。
这些文件包括:
.fnm (field names)
.tim / .tip (term index and dictionary)
.doc / .pos / .pay (document values, positions, payloads)
.dvd / .dvm (doc values)
等等。
这些数据构成了 Elasticsearch 实现快速全文检索的核心结构 —— 倒排索引(Inverted Index)
问题2,哪些查询它们会受益于文件系统缓存
以下是一些典型的查询类型和场景,它们会受益于文件系统缓存:
🔹1. Term 查询 / Term-level 查询
示例:精确匹配某个字段值
{"query": {"term": {"status": "published"}}
}
解释:
term 查询会查找包含特定 term 的文档。
Lucene 使用 .tim(Term Index) 和 .tip(Term Dictionary) 文件快速定位 term。
如果这些文件已经在文件系统缓存中,则完全不需要磁盘 I/O。
🔹2. Terms 查询
示例:匹配多个枚举值
{"query": {"terms": {"category": ["books", "electronics", "movies"]}}
}
解释:
多个 term 的组合查询。
每个 term 都会在倒排索引中查找对应的文档列表。
只要 .tim, .tip, .doc, .pos 等文件都在缓存中,性能非常高。
🔹3. Range 查询(数值或时间范围)
示例:查询某段时间内的订单
{"query": {"range": {"timestamp": {"gte": "2024-01-01","lt": "2025-01-01"}}}
}
解释:
如果字段是 keyword 或已经构建了 doc values(.dvm, .dvd),Lucene 会利用排序结构进行快速范围扫描。
如果相关 segment 的 .dvd 文件在缓存中,范围查询非常高效。
🔹4. Filter 上下文中的查询(Query in Filter Context)
示例:
{
"query": {"bool": {"filter": [{ "term": { "status": "published" } },{ "range": { "price": { "gte": 100, "lt": 500 } } }]}}
}
解释:
filter 上下文不计算相关度分数,只关心是否匹配。
Lucene 会使用 bitset 来加速 filter 查询,如果索引文件已在缓存中,速度极快。
filter 查询非常适合利用缓存,因为结果可重复使用(适合 cache)。
🔹5. 聚合查询(Aggregations)
示例:按 category 分组统计数量
{"size": 0,"aggs": {"categories": {"terms": { "field": "category.keyword" }}}
}
解释:
聚合操作需要遍历大量文档,读取字段值。
如果字段是 keyword 类型,Lucene 使用全局序号(global ordinals)和 .gob 文件进行处理。
如果这些文件在缓存中,聚合速度非常快,否则会触发大量磁盘读取。
🔹6. Doc value 字段查询
示例:
{"query": {"range": {"price": {"gte": 100,"lt": 500}}}
}
解释:
如果 price 字段开启了 doc_values(默认开启),Lucene 使用 .dvd 和 .dvm 文件来存储列式数据。
这些文件会被操作系统缓存在内存中,所以范围查询、排序、聚合等都非常快。
🔹7. 前缀查询(Prefix Query)
示例:
{"query": {"prefix": {"name": "elasti"}}
}
问题3 查询分析
- 如果_source 是true的话,要先查系统缓存,找到文档ID ,再查磁盘找到原始文件
- 如果返回的是部分字段?
下面我们详细解释每种方式的工作原理。
🔍 一、使用 _source filtering(源过滤)
这是最常见的方法,适用于你只想返回原始文档中的某些字段。
示例:
json
{"_source": {"includes": ["title", "author"]},"query": {"term": {"status": "published"}}
}
工作流程:
Elasticsearch 仍然会从 .source 文件中加载整个原始文档。
然后在内存中进行字段过滤,只保留你需要的字段。
最终只返回这些字段给客户端。
💡 关键点:
即使你只要几个字段,Elasticsearch 仍需要加载完整 _source。
如果 .source 文件不在文件系统缓存中,就会触发磁盘 I/O。
所以:虽然减少了网络传输量,但没有减少磁盘访问。
🔍 二、使用 store: true 的 stored fields(存储字段)
如果你只需要少量字段,并希望快速获取它们而不需要加载整个 _source,可以在 mapping 中为某些字段设置 store: true。
示例 mapping:
json
{"mappings": {"properties": {"title": { "type": "text", "store": true },"author": { "type": "keyword", "store": true },"content": { "type": "text" } // 默认不存储}}
}
查询时:
json
{"stored_fields": ["title", "author"],"source": false,"query": {"term": {"status": "published"}}
}
工作流程:
Elasticsearch 从 Lucene 的 .fdt / .fdx 文件中读取存储字段(stored fields)。
这些文件是独立于 _source 的。
如果这些文件在文件系统缓存中,查询速度非常快。
💡 关键点:
没有加载 _source,所以节省了内存和磁盘 I/O。
更适合“高频访问 + 字段少”的场景。
缺点是:占用更多磁盘空间 ,因为每个字段都单独存储了一份。
🔍 三、使用 docvalue_fields(适合聚合/排序)
对于 keyword 类型或数值类型字段,Lucene 使用 doc values 来支持高效的排序和聚合。
示例:
{"docvalue_fields": ["price", "publish_date"],"source": false,"query": {"term": {"status": "published"}}
}
工作流程:
Elasticsearch 从 .dvd, .dvm 文件中读取字段值。
这些文件也是列式存储,非常适合批量读取。
如果这些文件在缓存中,性能非常高。
💡 关键点:
完全不依赖 _source。
对聚合、排序、范围查询非常有用。
不适合返回大量文本内容(如文章正文)。