老周在街角经营一家「知书轩」书店。每天清晨,成箱的新书涌入仓库,店员把它们搬上架,读者在书架间穿梭询问。生意越来越红火,老周却开始发愁:新书什么时候上架合适?常被问到的书该放在哪里?书架上的小卡片越堆越多,读者查一本书要翻十几张卡片,越来越慢。他把这些烦恼跟一位做搜索引擎的朋友倾诉,朋友笑着说:「你说的这些,不就是 Elasticsearch 性能调优的核心吗?缓存、刷新、段合并,本质上是书店的动线设计、上架节奏和库存整理啊。」 Elasticsearch(简称 ES)是目前最流行的搜索引擎之一,它像一家超级自助图书馆,背后由 Lucene 负责「藏书」。当数据量暴涨,查询变慢、写入卡顿,往往不是机器不够强,而是「缓存命中率低」「刷新太频繁」「段合并策略不当」这三个环节失了衡。调优 ES 性能,本质上就是在读写两端找平衡:既要让读者(查询)快速找到书,又要让进货(写入)顺畅不堵车。这篇文章,我们就跟着老周一起,把 ES 性能调优的奥秘一一拆解。

岗位关联

对 AI 从业者而言,掌握 ES 性能调优意味着能驾驭海量数据的检索底座——无论是构建 RAG 知识库、日志分析平台还是电商搜索,你都能精准定位瓶颈、合理分配资源,让系统在千万级文档下依然保持毫秒级响应。

目录
  1. 可视化演示
  2. 缓存机制
  3. 刷新频率
  4. 段合并策略
  5. 写入优化
  6. 查询优化
  7. 内存分配
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示ES性能调优三大维度:缓存机制(filter cache位集缓存~85%命中/query cache查询结果~60%/request cache shard级~90%三层缓存filter代替query提升命中)、刷新与段合并(写入内存buffer每refresh_interval生成新段NRT近实时/后台段合并减少段数force_merge手动触发)、JVM堆内存调优(堆≤31GB指针压缩/堆外内存Lucene索引≥50%/ZGC适合大堆总内存不超过物理50%),三模式切换理解ES调优全貌。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+C1+'

缓存机制

老周发现,读者问得最多的总是那几类:哪些是 AI 入门书?哪些是最近上架的小说?他索性在门口设了一个「快捷书架」,把最常见问题的答案直接摆在那,读者一问就能指过去。ES 中也有类似的机制,叫做 Filter Cache(过滤器缓存)。当你用 filter 条件(比如「分类=AI」)过滤文档时,ES 会生成一张位图——像一张「座位表」,记录哪个文档满足条件、哪个不满足。这张座位表被缓存下来,下次再有相同的过滤条件,ES 直接查座位表,而不是重新扫描全部文档。这个机制非常高效,因为位图查询的时间近乎常数。

与 Filter Cache 不同,Query Cache 更像老周贴在收银台旁的「标准答案便签」。当有人问「店里一共有多少本 AI 新书」这类完整查询时,ES 会把整个查询结果缓存起来。但其触发条件非常苛刻——查询必须完全一致(包括参数顺序),任何细微差别都无法命中。因此 Query Cache 的真正命中率通常很低,ES 默认对查询结果超过一定体量的请求才启用,使用时要保持克制。

平均查询耗时可以看作「命中路径」与「未命中路径」的加权平均:

缓存命中率是 ES 查询性能的晴雨表。老周最后把「快捷书架」换成玻璃柜,读者一眼就能看到目标,店里的问询速度立刻翻倍——ES 里,合理利用 filter 缓存正是这样最直接的提速手段。

Tavg=h×Thit+(1−h)×Tmiss\displaystyle T_{\rm avg} = h \times T_{\rm hit} + (1 - h) \times T_{\rm miss}
平均查询耗时 ≈ 缓存命中率 × 命中耗时 + 未命中率 × 未命中耗时。缓存命中率越高,整体查询越快。
  1. 命中率 \(h\)表示查询在缓存层直接获得答案的比例,取值范围 0 到 1。h = 0.9 意味着 90% 的请求无需扫描底层数据,直接从快捷书架取书。
    h=命中缓存的查询数总查询数h = \frac{\text{命中缓存的查询数}}{\text{总查询数}}
  2. 命中耗时 \(T_{\rm hit}\)从缓存直接读取结果的时间,通常在微秒级。就像读者一指,店员立刻从门口书架抽出书。
  3. 未命中耗时 \(T_{\rm miss}\)需要深入 Lucene 遍历倒排索引、合并多个段的结果,耗时比命中高一个数量级。这是读者少、还得在整个书店里翻找的代价。

刷新频率

新书到店后不能立刻出现在柜台——要先在仓库登记,再由店员搬上书架。这个「从仓库到书架」的过程,对应 ES 里的 refresh 操作。数据先写入内存中的 buffer 缓冲区,refresh 时 buffer 被冻结并生成一个新的 segment(段),这时文档才变得「可被搜索」。ES 默认每 1 秒自动 refresh 一次,所以新写入的文档最多 1 秒后就能搜到。

如果老周每 1 分钟就把所有新书上一遍架会怎样?读者等待上架的时间变长,但店员不用频繁跑上跑下,工作效率大大提升。ES 同理:把 refresh interval 从 1s 调到 30s,写入吞吐能显著提升,因为每次 refresh 都要生成新 segment 并消耗 CPU 和磁盘 IO。刷新越频繁,生成的「小段」就越多,而查询时需要检查每个段,段多了,查询就慢了。

在固定时间窗口内,段的数量与刷新间隔成反比:

调优 refresh interval 本质上是在「可见性」和「写入吞吐」之间做天平。老周最终选择每天只在整点大批量上新书,而不是每来一本就摆一本——对实时性要求不高的业务,把 refresh 调大一些,往往是最省力的优化。

Nseg=TwindowRrefresh\displaystyle N_{\rm seg} = \frac{T_{\rm window}}{R_{\rm refresh}}
时间窗口内产生的段数量 = 窗口时长 ÷ 刷新间隔。刷新越频繁,段越多,查询要翻的「小卡片」也越多。
  1. 窗口时长 \(T_{\rm window}\)观察的时间跨度,比如 1 小时 = 3600 秒。用来统计这段时间内产生了多少个段。
    Twindow=3600 sT_{\rm window} = 3600\,{\rm s}
  2. 刷新间隔 \(R_{\rm refresh}\)两次 refresh 之间的间隔。默认 1 秒,日志类场景常调到 15~60 秒。
  3. 段数量 \(N_{\rm seg}\)每 refresh 一次产生一个新段。窗口时长固定时,刷新间隔越大,段数量越少,查询负担越轻。

段合并策略

段(segment)是 Lucene 存储数据的基本单位,一个段就像一个装满已排好序卡片的小盒子。查询时所有段都会被检查,段越多,检查成本越高。小段数量积累到一定程度,ES 的后台线程会自动执行 merge(合并)——把小段的卡片按顺序归并成一个大段,就像老周每晚打烊后把散落的便签按拼音顺序重新编进一本大书里。

ES 默认采用 tiered(分层)合并策略:先合并小段的段,生成中等大小的段;中等段积累到一定数量再合并成更大的段,逐层升级。这个策略很像把零钱先换成整钞,再攒成更大的票子,保证任意时刻合并成本相对可控。合并过程在后台异步执行,ES 还会通过限速(merge throttling)控制合并的磁盘带宽,避免合并与写入抢资源。但对于某些场景——比如一个索引已经写完、进入只读归档阶段,可以用 force merge API 强制将所有段合并成一个大段,让查询性能达到最优。

合并策略的总体代价可以这样理解:

段合并是后台的「藏书整理员」。老周发现,每晚定时整理书架,白天读者找书体验就特别好;但如果在读者高峰期整理书架,反而挡了路。ES 的 force merge 也正是要在业务低峰期执行,才能让合并的红利最大化。

Cmerge=N×log⁡mN\displaystyle C_{\rm merge} = N \times \log_{m} N
合并总代价 ≈ 初始小段数 × 合并层数(底数为每轮合并段数)。段越碎,整理成本越高。
  1. 初始小段数 \(N\)refresh 频繁时产生大量小段,N 越大,整理工作越繁重。就像满桌小卡片。
    N=NinitialN = N_{\rm initial}
  2. 每轮合并段数 \(m\)tiered 策略通常每轮合并 5~10 个段。m 越大,单轮合并就越彻底,层数越少。
    m=5∼10m = 5 \sim 10
  3. 合并层数 \(\log_{m} N\)把 N 个小段归并成一个大段需要的轮数。1000 个小段、每轮并 10 个,只需 3 层。
    log⁡101000=3\log_{10} 1000 = 3

写入优化

进货场景最能体现写入优化。以前老周雇了个小伙子骑三轮车,每来一批新书就单独跑一趟仓库,一天跑几十趟,累得半死。后来改成开卡车批量拉货,一趟顶十趟——这就是 ES 的 bulk 批量写入。一次 bulk 请求可以携带数百甚至上千条文档,将网络往返和解析开销摊薄到每条数据上,写入吞吐成倍提升。

写入链路上还有两个关键开关:一是 refresh interval,前面说过调大会减少小段生成;二是 translog(事务日志)。translog 像一本「进货记账本」,每次写入先记在账本上再落盘,防止断电丢数据。默认每次写入都 fsync 到磁盘,很安全但慢;在允许少量数据丢失的日志场景中,可把 index.translog.durability 设为 async 并调大同步间隔,显著提升写入速度。此外,副本数也直接影响写入——每份副本都相当于要把书再抄一份,副本越多,写入越慢,写入型索引副本不宜过多。

批量写入的核心收益是摊薄单条开销:

写入优化就像设计进货节奏:大卡车整批拉货(bulk)、控制上架次数(refresh)、账本不必每笔都当场盖章(translog)、书籍少抄几份(副本),四管齐下,入库效率自然翻倍。

Tper=Trtt+TprocN\displaystyle T_{\rm per} = \frac{T_{\rm rtt} + T_{\rm proc}}{N}
单条平均写入耗时 =(网络往返 + 处理总耗时)÷ 批次大小。批量越大,单条成本越低。
  1. 网络往返 \(T_{\rm rtt}\)客户端与 ES 之间一次请求的通信延迟。每次请求都有固定成本,批量让这个成本只付一次。
    Trtt≈0.1∼1 msT_{\rm rtt} \approx 0.1 \sim 1\,{\rm ms}
  2. 处理总耗时 \(T_{\rm proc}\)ES 解析请求、索引全部文档的总时间。
  3. 批次大小 \(N\)一次 bulk 请求携带的文档数。N 从 1 变到 500,单条耗时可能降到原来的几十分之一。
    N=500, Tper≈Trtt500+Tproc500N = 500,\ T_{\rm per} \approx \frac{T_{\rm rtt}}{500} + \frac{T_{\rm proc}}{500}

查询优化

读者进书店后的动线设计,决定了找书快不快。老周把最常被问的类别放在入口(对应 filter 查询),把热门的答案贴在墙上(对应缓存),还规定太深太偏的问题不能一次翻完整本书(对应分页限制)。ES 查询优化的核心思路与此一致。

第一条是「filter 优先」。filter 不计算相关性评分,结果可以进入 filter cache,而 query 需要计算 score,代价更高。凡是「是不是、在不在、满不满足」这类不需要打分的问题,都应写成 filter。第二条是警惕深度分页。常规 from + size 翻页在浅层没问题,但翻到第 10000 条以后,ES 需要先取出前 10000 条再丢弃,消耗呈线性增长——这正是老周不让人一次性翻完整本书的原因,改用 search_after 游标实现滚动翻页才是正解。第三条是利用 Doc Values——ES 在索引时为排序、聚合场景预先构建的列式存储,内存友好、访问高效,聚合操作应尽量依赖它。此外,_source 字段裁剪也能减少磁盘 IO 和存储成本。

深度分页的成本随页号加深而线性上升:

查询优化是给读者规划一条顺畅的动线:入口放 filter 捷径,深水区设游标,聚合走列式快速通道,把 _source 精简到够用为止。动线顺了,读者的满意度(查询延迟)自然会写在他们脸上。

Tpage=p×Tscan\displaystyle T_{\rm page} = p \times T_{\rm scan}
深翻页耗时 ≈ 目标页号 × 单页扫描耗时。越往后翻,越昂贵。
  1. 页号深度 \(p\)即 from 的偏移量除以每页大小。翻到第 100 页,就必须先扫描前 100 页的数据。
    p=fromsizep = \frac{\text{from}}{\text{size}}
  2. 单页扫描耗时 \(T_{\rm scan}\)扫描并丢弃一页数据的平均耗时。数据量越大,单页扫描越慢。
  3. 线性关系翻页越深,总耗时越高,且没有捷径。改用 search_after 后,每次只需从上一页的最后一个文档继续往后取,耗时恒定。
    Tsearch_after≈Tone_pageT_{\rm search\_after} \approx T_{\rm one\_page}

内存分配

老周给书店做资金管理时,会把零钱留在柜台方便日常找零,把大额现金锁进保险柜,还装了一个警报器,防止一笔大支出把账上现金掏空。ES 的内存管理与此惊人地相似:JVM 堆内存是柜台里的「零钱包」,OS 的文件缓存是「保险柜」,而熔断器(circuit breaker)就是那个警报器。

ES 官方建议:JVM 堆大小设置为物理内存的一半,且上限不超过 32GB。堆内存留给 Lucene 的索引缓冲、filter cache 等运行数据;剩下的一半内存交给操作系统,用于文件系统缓存(page cache)——ES 读磁盘时本质上是从这个「保险柜」里读,page cache 越大,读性能越好。堆设得太大,不仅挤占了 page cache,还会因超长 GC 停顿让查询卡顿;设得太小,索引缓冲不足,写入频繁触发 flush。

堆内存的推荐上限公式:

内存分配决定了整家书店的天花板。柜台零钱够用(堆合理),保险柜充足(page cache 大),警报器灵敏(熔断器防止 OOM),书店无论生意多火都能稳稳运转。ES 调优到深处,拼的就是这份从容的「资金安排」。

Hheap=min⁡(Mtotal2, 32 GB)\displaystyle H_{\rm heap} = \min\left(\frac{M_{\rm total}}{2},\ 32\,{\rm GB}\right)
推荐堆大小 = 物理内存的一半与 32GB 中的较小值。另一半留给操作系统做文件缓存。
  1. 物理内存 \(M_{\rm total}\)服务器的总内存。ES 只把一半给 JVM 堆。
    Mtotal=64 GB, Mtotal2=32 GBM_{\rm total} = 64\,{\rm GB},\ \frac{M_{\rm total}}{2} = 32\,{\rm GB}
  2. 取最小值 \(\min(\cdot)\)如果物理内存一半超过 32GB,仍取 32GB。压缩指针(compressed oops)在 32GB 以下生效,堆超过 32GB 反而因指针膨胀导致性能下降。
    min⁡(32,32)=32 GB\min(32, 32) = 32\,{\rm GB}
  3. 堆外留给 OS 缓存另一半被 OS 用作 page cache,承载热数据的文件读取。
    Hos=Mtotal−HheapH_{\rm os} = M_{\rm total} - H_{\rm heap}

AI 应用场景

日志平台写入吞吐调优

阶段:数据接入与预处理

解决的问题:监控系统每天产生数 TB 日志,默认每秒刷新一次导致大量小段、写入吞吐低,且索引持续增长拖慢查询。传统做法是盲目加机器,成本高且治标不治本。

具体案例:某运维团队管理三个 ES 集群的日志平台,将日志索引的 refresh interval 从 1s 调至 30s,translog 改为异步刷盘,并批量写入(每批 5MB)。写入吞吐从 1.2 万条/秒提升到 3.8 万条/秒;对 7 天前的只读索引执行 force merge,查询 P99 从 420ms 降到 180ms。

电商搜索筛选缓存优化

阶段:在线检索与召回

解决的问题:电商搜索中「分类」「价格区间」「品牌」等筛选条件频繁变化,每次变筛都全量扫描导致查询慢。传统做法是逐条加缓存,无法从 ES 层面根治。

具体案例:某电商平台的商品搜索页面,将筛选条件统一建模为 filter(而非 query),并利用 query cache 缓存 top 热门筛选组合。优化后 filter 缓存命中率从 55% 升至 88%,平均查询耗时从 260ms 降至 90ms,双十一零点峰值 QPS 下集群 CPU 仍有余量。

RAG 知识库容量规划

阶段:知识检索与生成增强

解决的问题:RAG 应用的向量与关键词混合检索需要 ES 支撑,但堆内存设多大、分片多少个、段合并策略如何定,直接影响检索延迟与稳定性。拍脑袋配置容易 OOM 或查询超时。

具体案例:某大模型应用团队搭建 RAG 知识库,按「堆内存 = 物理内存一半且不超 32GB」设定服务器规格,为固定知识库索引执行 force merge,并对 filter 条件的元数据查询开启缓存。最终单次检索 P95 稳定在 150ms 内,每周新增文档写入无掉队。

Python 演示

python38 行
class MergeSimulator:    def __init__(self, refresh_interval=1, merge_limit=6):        self.refresh_interval = refresh_interval        self.merge_limit = merge_limit        self.segments = []        self.clock = 0        self.merge_count = 0        self._flush_count = 0     def tick(self):        self.clock += 1        if self.clock % self.refresh_interval == 0:            self.segments.append(1)            self._flush_count += 1        self._try_merge()     def _try_merge(self):        n = len(self.segments)        if n < 2:            return        group_size = min(self.merge_limit, n)        merged = sum(self.segments[:group_size])        self.segments = [merged] + self.segments[group_size:]        self.merge_count += 1     def run(self, seconds):        for _ in range(seconds):            self.tick() def compare(seconds=3600):    for interval in [1, 5, 30]:        sim = MergeSimulator(refresh_interval=interval)        sim.run(seconds)        print("refresh=%2ds -> 段数=%3d 合并次数=%4d" % (            interval, len(sim.segments), sim.merge_count)) if __name__ == "__main__":    compare()
目的:模拟不同 refresh interval 下 ES 段数量与合并次数的变化,直观理解「刷新越频繁、段越多、合并越累」的权衡。
  1. self.segments.append(1)每次到达刷新间隔,产生一个新段(初始大小记 1)。
  2. def _try_merge(self)模拟 tiered 合并:把最前面的若干小段合并成一个大段。
  3. merged = sum(self.segments[:group_size])将一组小段的大小求和,生成合并后的大段。
  4. self.segments = [merged] + self.segments[group_size:]用新大段替换最前方那组小段。
  5. sim = MergeSimulator(refresh_interval=interval)分别用 1s、5s、30s 的刷新间隔做对比实验。
  6. compare()运行 3600 秒模拟,输出三种配置下的段数与合并次数。

常见误区

误区以为 ES 会自动缓存所有热门查询,就像老周把所有书都摆在门口一样。于是放心大胆地用 query 查询一切,指望 Query Cache 兜底。

正解Query Cache 的触发条件极其苛刻——必须查询字符串完全一致(包括参数顺序),稍有变化就命中不了。真正高命中率的是 Filter Cache,它缓存的是「文档是否满足条件」的位图,与具体查询词无关,复用价值高得多。日常开发应把无需算分的条件写成 filter,而不是指望 Query Cache。

为何常见:名字里有「Cache」就容易让人以为它能缓存一切,加上官方文档示例少,很多初学者从未实际统计过 Query Cache 的命中率。

误区把 refresh interval 从 1s 调到 30s,担心这 29 秒内写入的数据会丢,仿佛新书在仓库里放久了就会消失一样。

正解refresh 只决定文档何时「可见可搜」,与数据可靠性无关。真正保证不丢数据的是 translog——每次写入先写 translog 落盘,即使进程宕机,重启后也能从 translog 恢复。refresh 调大只是让新文档更晚被搜索到,适合日志、指标等实时性要求不高的场景。

为何常见:人们容易把「搜不到」等同于「丢了」,其实 ES 的写入链路中,可见性与持久化是两条独立的通道。

误区执行完 force merge,所有索引都只剩一两个大段,觉得从此查询永远快,再也不管索引了。

正解force merge 对只读索引效果好,但只要有新写入,新的小段会不断产生,merge 后的「完美状态」很快被打破。对持续写入的索引频繁 force merge,反而因反复大合并而浪费磁盘 IO。正确做法是:冷数据(如历史日志、归档数据)做 force merge 然后置为只读;热数据交给后台 tiered 合并自动管理。

为何常见:force merge 效果立竿见影,容易让人误以为它是一个「终局操作」,忽略了 ES 的动态写入模型。

误区服务器有 64GB 内存,把 ES 堆设成 48GB,觉得堆越大缓存越多、查询肯定更快。

正解堆内存设置需要克制。堆过大,一是挤压了留给 OS 的 page cache——而 ES 读文件依赖它,反而拖慢查询;二是堆超过 32GB 后,JVM 无法使用压缩指针,内存指针膨胀导致实际可用空间不升反降。官方建议堆设为物理内存一半且不超过 32GB,把另一半交给操作系统做文件缓存。

为何常见:「内存越大越快」是通用直觉,但 ES 的堆与 OS 缓存是此消彼长的关系,违反直觉。

面试题

Q1. 在 ES 中,filter 条件查询相比 query 查询的主要优势是什么?

单选题 · easy

  • A.不计算相关性评分且结果可缓存← 正确答案
  • B.只能用于精确匹配
  • C.返回结果数量固定
  • D.不需要索引参与

答案:A。filter 只判断文档是否满足条件,不计算相关性评分,因此计算量小,且结果位图可进入 filter cache 复用。就像老周直接指向「快捷书架」而不用逐本翻看打分。B 错在 filter 支持范围条件等多种语义;C 错在返回数量由查询决定;D 错在检索仍依赖倒排索引。

Q2. 将 refresh interval 从 1 秒调大到 30 秒,最直接的影响是什么?

单选题 · easy

  • A.新写入文档可见延迟变高← 正确答案
  • B.查询结果永远不更新
  • C.数据丢失风险增加
  • D.段数量成倍增加

答案:A。refresh 决定文档从 buffer 转为可搜索段的时间,间隔越大,新文档越晚被搜到,这是「可见性」的延迟,而非数据丢失。调大 refresh 反而减少段数量、提升写入吞吐,所以 B、C、D 均不正确。

Q3. force merge 最适合应用于哪种类型的索引?

单选题 · mid

  • A.不再接收新写入的只读归档索引← 正确答案
  • B.每秒都有大量写入的热索引
  • C.副本数超过三份的索引
  • D.刚创建还未写入数据的空索引

答案:A。force merge 把段合并到极少数量,查询性能最优,但新写入会产生新段、打破合并成果,所以只适合只读归档索引。热索引反复 force merge 会导致频繁大合并、浪费 IO。副本数和空索引与合并策略无直接关系。

Q4. 官方推荐 ES 的 JVM 堆内存大小如何设置?

单选题 · mid

  • A.总内存一半且不超过 32GB← 正确答案
  • B.总内存全部
  • C.越大越好上不封顶
  • D.与查询量负相关

答案:A。堆设总内存一半且 ≤32GB,是为了兼顾 JVM 堆与 OS page cache 的平衡,同时 32GB 以内可启用压缩指针省内存。全给堆会导致文件缓存不足,超过 32GB 反而因指针膨胀效率下降,故 B、C 错误;D 表述无依据。

Q5. 在 ES 中翻页到第 10 万条之后,哪种方案能解决深分页的性能问题?

单选题 · mid

  • A.使用 search_after 游标滚动← 正确答案
  • B.继续调大 from 深度
  • C.一次取回全部结果
  • D.关闭分页结果缓存

答案:A。from 深度越大,ES 需先取出并丢弃前 N 条,耗时线性增长,所以要改用 search_after——它从上一页最后一条继续往后取,耗时恒定。调大 from 只会更慢,一次取全部不现实,关闭缓存与分页语义无关。

Q6. translog 在 ES 写入链路中的核心作用是什么?

单选题 · easy

  • A.保证写入数据不丢失← 正确答案
  • B.加快查询响应速度
  • C.压缩索引磁盘占用
  • D.管理节点间通信

答案:A。translog 像进货记账本,每次写入先记在账本上再落盘,进程宕机后可从 translog 恢复未刷盘的数据,保证数据可靠。它服务于写入链路而非查询、压缩或集群通信,因此 B、C、D 错误。

Q7. 关于 ES 的 Query Cache,以下描述正确的是?

单选题 · hard

  • A.要求查询条件完全相同才会命中← 正确答案
  • B.缓存的是文档过滤位图
  • C.任意查询都会被缓存
  • D.缓存内容为倒排索引

答案:A。Query Cache 缓存完整查询结果,但必须查询字符串完全一致才可能命中,是 LRU 淘汰的。Filter Cache 缓存的是文档位图,故 B 错;不是任意查询都能缓存,C 错;缓存的是查询结果而非倒排索引结构,D 错。

Q8. 以下哪些因素会触发 ES 执行段合并(merge)?

多选题 · mid

  • A.段数量超过阈值← 正确答案
  • B.段大小超过阈值← 正确答案
  • C.用户手动执行 force merge← 正确答案
  • D.磁盘空间不足导致索引只读

答案:ABC。tiered 合并策略通过段数量与段大小的阈值判断是否需要合并,二者都满足触发条件;force merge 是用户主动触发的合并操作,也算合并的一种。磁盘只读是磁盘保护机制,与段合并无关,D 不选。

Q9. 以下哪些措施能有效提升 ES 的写入性能?

多选题 · hard

  • A.使用 bulk 批量写入← 正确答案
  • B.调大 refresh interval← 正确答案
  • C.减少副本数量← 正确答案
  • D.增加索引中字段数量

答案:ABC。bulk 批量摊薄单条网络与解析开销,refresh 调大减少小段生成与刷盘压力,副本减少降低数据复制成本,三者都直接提升写入吞吐。增加字段数量只会加大索引构建成本,D 错误。

Q10. 关于 ES 缓存与内存管理,下列说法正确的有哪些?

多选题 · extreme

  • A.Filter Cache 存储的是文档匹配位图← 正确答案
  • B.堆内存设得越大留给 page cache 空间越小← 正确答案
  • C.circuit breaker 防止查询吃爆堆内存← 正确答案
  • D.Query Cache 适合缓存带随机排序的查询

答案:ABC。Filter Cache 的本质是位图,A 正确;JVM 堆与 OS page cache 共享物理内存,此消彼长,B 正确;熔断器在查询或聚合开销过大时直接拒绝请求以避免 OOM,C 正确。Query Cache 只适用于结果确定、可完全重放的查询,带随机排序的查询无法命中缓存,D 错误。