想象你是一座超级图书馆的馆长。这座图书馆每天接待千万读者,藏书过亿,新书以每秒几千本的速度涌入。读者总在抱怨:“找一本书怎么要等好几秒?”“翻到第 100 万条结果时页面直接卡死!”这时你需要的不是买更多书架,而是重新设计这座图书馆的运转规则——这就是 Elasticsearch(ES)性能调优。 ES 就是这座图书馆:索引是图书编目体系,分片是书柜组,副本是热门资料的备用复印件,刷新间隔是编目员更新检索目录的节奏,translog 是那本先记账再誊写的流水账簿,doc_values 是印在磁盘上的正式索引册,而冷热分层则是“前台放畅销书、地下室存旧档案”的空间哲学。本文会用图书馆的一天,把 ES 调优的六大关键决策讲透。

岗位关联

掌握 ES 调优,你就能在海量搜索场景中快速定位瓶颈在索引、写入、内存还是架构,做出有据可依的容量规划——这是数据工程师从“会调 API”进阶到“能扛高并发”的分水岭。

目录
  1. 可视化演示
  2. 索引设计规划
  3. 分片副本规划
  4. 写入性能调优
  5. 内存管理策略
  6. 查询DSL优化
  7. 冷热分层架构
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示ES分片分配与JVM内存50/50划分、查询DSL优化(通配/脚本/深页→前缀/过滤/游标)与写入刷新间隔调优、以及冷热分层架构与HNSW向量检索参数。

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

分片分配与JVM内存 3节点集群P0/P1/P2主分片+R0/R1/R2副本分布,单分片 JVM Heap 50%+Page Cache 50%,堆内存存集群状态/查询中间结果 内存=Heap(50%)+Cache(50%) 堆与文件缓存各占50%平衡查询与写入

索引设计规划

图书馆编目员小陈每天早上最头疼的一件事:究竟该为每本书登记多少条目?书名、作者、ISBN 这些必须登记;封面颜色、页数、出版社地址……读者几乎不会按这些找书,登记了只会让条目卡片堆成山。老馆长教他一个口诀:“读者不会搜的,就不必建卡。”

在 ES 里,“给什么建卡”由 mapping(字段映射)决定。当你创建索引时,每个字段都要声明类型:keyword 适合精确匹配、过滤和排序;数值与日期要用 integer、date;全文检索的正文才用 text 并配合分词器(analyzer)。如果偷懒使用动态映射(dynamic mapping),ES 就会像新手编目员一样,给每个字段都猜类型、都建索引,索引体积瞬间膨胀。

工程上,应该预先设计好 mapping:只对需要查询、过滤、排序的字段建立索引,其余字段设置 "index": false 以节省倒排结构;需要排序的字段优先用 keyword 或数值类型,而不是 text。选择合适的分词器(如中文 ik、英文 standard)会直接影响词项规模与检索质量——分词越碎,词典越大,索引越重。

回到图书馆,小陈最终只给读者真正会检索的条目建卡,把封面颜色等垃圾条目扔进“不建索引”的清单。结果卡片柜瘦身一半,找书速度反而更快——索引体积小,倒排词典短,扫描自然快。索引设计的本质,就是“少即是多”。

Sindex=Ssource+∑f∈fieldsNdocs×(Lfterm+Lfposting)S_{\text{index}} = S_{\text{source}} + \sum_{f \in \text{fields}} N_{\text{docs}} \times (L_{f}^{\text{term}} + L_{f}^{\text{posting}})
直观理解:索引体积 = 原始文档 + 每个字段的检索卡片。字段越少、卡片越短,索引越轻。
  1. S_index(索引总占用)整个索引在磁盘上占据的空间,乘以分片与副本数就是集群总存储。
    SindexS_{\text{index}}
  2. S_source(原始文档)_source 字段保存的原始 JSON,相当于图书馆保留的原书复印本,几乎不能省。
    SsourceS_{\text{source}}
  3. 求和循环(遍历每个字段)映射中每个字段 f 都会产生独立的索引结构,字段越少越节省。
    ∑f∈fields\sum_{f \in \text{fields}}
  4. N_docs(文档总数)每篇文档都要为它登记的字段付出词项加倒排列表的开销。
    NdocsN_{\text{docs}}
  5. L_term(词项长度)分词后唯一词项占用的字节数,分词器切得越碎,词项越多。
    LftermL_{f}^{\text{term}}
  6. L_posting(倒排列表)每个词项指向的文档 ID 列表长度,它决定“搜到词之后”的遍历成本。
    LfpostingL_{f}^{\text{posting}}

分片副本规划

图书馆里有一万本书要上架。馆长有两种极端做法:把书分成 20 本一组的 500 个书柜组,结果读者想找一本“算法”主题的书,得把 500 个书柜组挨个问一遍;或者只分成 2 个书柜组,每组的书多到借阅登记时排队排到门口。书柜组怎么分,直接决定这座图书馆能不能喘过气来。

ES 中,索引被划分为多个分片(shard),每个分片是一个独立的 Lucene 索引,可以分布在不同节点上。分片数决定查询的并行度,也决定协调节点聚合结果的负担。副本(replica)是主分片的完整拷贝,用于故障转移与读流量分担,但每个副本都要完整存储一份数据,写入时也要同步复制。

容量规划的经典公式:主分片数 = 数据总量 ÷ 单分片建议容量并向上取整,单分片经验容量为 20-50 GB。副本数从 0 起步,按读压力与容灾要求逐步增加。特别注意:主分片数在索引创建后不可修改,所以规划时要预留未来一年的增长空间。

馆长最后决定:600 GB 分成 15 个主分片(每个约 40 GB),配 1 个副本,总共 30 个分片分布在 5 个节点上。这样哪个书柜组的原件损坏,复印件能立即顶上;借阅高峰,读者可以分流到副本区。分片与副本,是在并行度、容灾与存储成本之间的三角平衡。

N_{\text{shards}} = \left\lceil \frac{S_{\text{total}}}{S_{\text{per-shard}}} \right\rceil,\qquadS_{\text{cluster}} = N_{\text{shards}} \times (1 + R_{\text{replica}}) \times S_{\text{per-shard}}
直观理解:先定“每个书柜组放多少书”,再算出需要几个书柜组;每个柜组配上副本后,总存储翻倍。
  1. S_total(数据总量)规划期内的数据总规模,例如 600 GB,要预留至少一年的增长余量。
    StotalS_{\text{total}}
  2. S_per-shard(单分片容量)经验上每个分片保持在 20-50 GB,过小浪费资源,过大拖慢段合并与恢复。
    Sper-shardS_{\text{per-shard}}
  3. 向上取整分片必须是整数,略多分一点留出余量,不能四舍五入。
    ⌈⋅⌉\lceil \cdot \rceil
  4. R_replica(副本数)每个主分片对应的副本份数,副本不会分担写入压力,反而要复制全部写入数据。
    RreplicaR_{\text{replica}}
  5. S_cluster(集群总存储)总存储等于主分片数乘以 (1+副本数),这是硬件采购与成本核算的依据。
    SclusterS_{\text{cluster}}

写入性能调优

图书馆每天涌入几千本新书。编目员小陈原来每登记一本,就立刻去大厅更新一次检索目录,结果一天跑上百趟,新书积压成山。老馆长的建议是:攒够一摞再统一更新目录,同时准备一本“流水账簿”——每本书先进账本,再定期誊写到正式档案里。

ES 的写入链路分两步。第一步,文档先写入内存 buffer 和 translog(事务日志);第二步,每隔 refresh_interval 将 buffer 生成一个可搜索的 segment(段)。默认 refresh 为 1 秒,所以刚写入的文档约 1 秒后可见。调大 refresh_interval(如 30-60 秒),会减少 segment 数量与合并压力,显著提升写入吞吐,代价是搜索可见性延迟变大。

translog 默认每次写请求都同步刷盘(fsync)以保证断电不丢数据,但这是最大的单点开销。将 index.translog.durability 设为 async 并调大 flush 阈值,刷盘周期从“每次请求”变成“每 5 秒一次”,写入吞吐大幅提升,代价是节点故障时可能丢失最近数秒的数据。再配合 bulk 批量写入(单批 5-15 MB),写入性能才能榨干。

小陈把目录更新从“每本一跑”改成“半分钟一跑”,又把流水账簿从“每笔都立刻誊写”改成“隔几秒批量誊写”,新书 backlog 瞬间清零。当然,读者要等最多半分钟才能搜到刚还回来的书,账房在断电时可能丢失最近几秒的未誊写记录。调优,从来都是先想清楚愿意牺牲什么。

Twrite≈Nbulk×Sbulktrefresh+ttranslogT_{\text{write}} \approx \frac{N_{\text{bulk}} \times S_{\text{bulk}}}{t_{\text{refresh}} + t_{\text{translog}}}
直观理解:写入吞吐 = “一批多少书 × 每秒搬几批” ÷ “更新目录加誊写账本的总耗时”。
  1. N_bulk(每秒批量批次数)由客户端并发连接数决定,通常在 4-8 个并发。
    NbulkN_{\text{bulk}}
  2. S_bulk(单批数据量)bulk 请求单批携带的字节数,推荐 5-15 MB,太大会导致内存压力。
    SbulkS_{\text{bulk}}
  3. t_refresh(刷新间隔)每次 refresh 都会形成新 segment 并触发后续合并,间隔越短,合并越频繁。
    trefresht_{\text{refresh}}
  4. t_translog(刷盘时间)translog 同步落盘耗时,改成 async 后接近零,但引入了数据丢失窗口。
    ttranslogt_{\text{translog}}
  5. 整体关系吞吐量与批量大小成正比,与刷新加刷盘总耗时成反比,可以用它估算调优收益。
    Nbulk×Sbulktrefresh+ttranslog\frac{N_{\text{bulk}} \times S_{\text{bulk}}}{t_{\text{refresh}} + t_{\text{translog}}}

内存管理策略

图书馆曾经尝试在管理员工作台上摆满“速查便利贴”——把每本书的所有排序、分组信息都抄在便利贴上,贴满整个桌面。结果查得是快,可桌子一满,新人来借书时系统直接崩溃。老馆长称之为“桌面爆炸”。

这就是 fielddata 的故事。老版本 ES 用 fielddata 在 JVM 堆内存中加载 text 字段的全部唯一词项,以支持排序与聚合。字段基数一大,比如几千万个不同词项,堆内存直接溢出(OOM)。而 doc_values 是“印刷在磁盘上的正式索引册”——列式存储的正排索引,只在真正需要时才把相关块读入内存,用来支撑 keyword、数值字段的排序与聚合,内存友好得多。

现代最佳实践很明确:所有排序与聚合字段使用 doc_values(keyword、数值、日期类型默认开启,text 默认关闭);除非万不得已,不要对 text 做 terms 聚合。JVM 堆内存设置为物理内存的一半且不超过 32 GB(超过 32 GB 会失去压缩指针优化),剩余内存留给操作系统 page cache 做文件缓存。

撤掉桌面上的便利贴,换上磁盘印刷的正式索引册,工作台瞬间清爽了——只有真正被读到的索引块才会短暂进驻内存。内存管理的心法就是:把易爆的“临时卡片”赶下桌,用可预测的“磁盘索引册”替代,再给操作系统留足文件缓存的空间。

Mfielddata≈U×8 Bytes,JVM≤min⁡(Smem2, 32 GB)M_{\text{fielddata}} \approx U \times 8\ \text{Bytes}, \qquad JVM \le \min\left(\frac{S_{\text{mem}}}{2},\ 32\ \text{GB}\right)
直观理解:堆内存是管理员的工作台,doc_values 是磁盘上的正式索引册;工作台永远不该被便利贴塞满。
  1. M_fielddata(堆内存占用)使用 fielddata 聚合或排序时,堆内存被全部唯一词项占满的近似值。
    MfielddataM_{\text{fielddata}}
  2. U(唯一词项数)text 字段分词后所有不同词项的总数,它与文档数正相关且难以压缩。
    UU
  3. 8 Bytes(每词项指针)每个词项至少占 8 字节对象引用,一亿词项就是 800 MB 堆内存。
    U×8U \times 8
  4. JVM 封顶 32GB超过 32 GB 后 JVM 放弃压缩指针,同样内存能装的对象反而变少,不能无脑加大堆。
    min⁡(Smem2, 32 GB)\min\left(\frac{S_{\text{mem}}}{2},\ 32\ \text{GB}\right)

查询DSL优化

读者来借书有两种问法。一种问“帮我找找提到'人工智能'的书,按相关度排个序”——这要逐字比对全文,还得算相关分,费时费力;另一种问“我就要 2023 年出版、分类码 T 开头的”——这只是打个勾筛选,答案还能被记下来复用。

在 ES 查询 DSL 中,query 上下文会计算相关性分数(_score),对全部候选文档做评分排序,代价高;filter 上下文只做布尔过滤,不计算分数,且结果会自动进入 filter cache 被复用。因此,范围、精确匹配、存在性判断应尽量放进 filter;只有真正的全文检索与相关度排序才需要 query。

深分页是另一个隐形陷阱。from + size 翻页时,协调节点必须从每个分片取回 from + size 条结果再做全局排序,翻得越深代价越高,默认超过 10000 条会被拒绝。正确做法是 search_after(游标翻页)或 PIT(point in time),只记录上一条的位置继续向后取。此外,高基数聚合(如对几十亿唯一值做 cardinality)会耗尽协调节点内存,应降精度或改用近似聚合。

聪明的读者学会先报“分类、年份”这些硬条件(filter),让图书系统先筛出一小撮书,再对其中做“相关度排序”(query);想看后面几百页,就请工作人员拿着上一本书的书签继续找(search_after),而不是把整个前厅的资料全部倒出来重排。查询优化的本质是让系统少干活。

Cdeep=Nshards×(F+S)C_{\text{deep}} = N_{\text{shards}} \times (F + S)
直观理解:深翻页的代价 = 所有书柜组都要各自排出“前 F 条”,再汇总排序,翻得越深越恐怖。
  1. C_deep(深分页总代价)协调节点为返回第 F 页所需拉取并排序的文档总数。
    CdeepC_{\text{deep}}
  2. N_shards(分片数)每个分片都可能包含匹配文档,一个都不能漏,必须全部扫描取回。
    NshardsN_{\text{shards}}
  3. F(from 偏移)跳过的条数,比如想看第 100 万条时 F 就是 1000000。
    FF
  4. S(size 条数)每页返回条数,默认 10。F 越大,代价随翻页深度线性放大。
    SS
  5. 为什么令人崩溃翻到最后一页时,所有分片都要先把“前一百万个结果”排好序,然后全部丢弃,只留最后一页。
    Nshards×(F+S)N_{\text{shards}} \times (F + S)

冷热分层架构

图书馆的空间哲学:前台书架放新书和畅销书,读者排着长队也要用最好的位置;二楼普通书架放常借的书籍;地下室恒温库房放三年以上的旧期刊。关键在于——不能让前台书架的昂贵空间堆满没人碰的旧书。

ES 冷热分层(hot-warm-cold)按数据温度分配节点:热节点(hot)通常配 SSD 与大内存,承载最新、最热的数据;温节点(warm)配普通磁盘;冷节点(cold)配廉价大容量磁盘,承载只读历史数据。实现方式是通过节点属性打标签(如 node.attr.data=hot),再用 ILM(Index Lifecycle Management)策略自动完成迁移。

ILM 生命周期通常有四个阶段:hot(热)、warm(温)、cold(冷)、delete(删除)。迁移依据是索引年龄(min_age)或 rollover 滚动阈值。Rollover 让索引在满足条件(如大小 50 GB、文档数或时长)时自动创建新索引,旧索引继续老化迁移。配合 force merge 压缩段、shrink 收缩分片,冷层存储成本降到最低。

馆长在新书进馆时写下“前台放置 7 天,二楼放置至 30 天,然后转地下室,两年后销毁”的自动规则,从此再也没有人手动搬书。前台永远只有畅销书,查询飞快;地下室堆满旧档案,成本极低。这就是规模化场景下 ES 的终极形态:让每本书住进它该住的地方。

hot(t<t1)  →  ILM    warm(t1≤t<t2)  →  ILM    cold(t≥t2)  →  ILM    delete\text{hot}(t < t_1) \;\xrightarrow{\;ILM\;}\; \text{warm}(t_1 \le t < t_2) \;\xrightarrow{\;ILM\;}\; \text{cold}(t \ge t_2) \;\xrightarrow{\;ILM\;}\; \text{delete}
直观理解:新书进馆自动放前台 7 天,30 天后转二楼,半年后送地下室,两年后销毁,全程无人值守。
  1. t(索引年龄)从索引创建或 rollover 后经过的时间,ILM 用它作为迁移触发的标准。
    tt
  2. t_1(热转温阈值)例如 7 天,配置 min_age: 7d,达到后自动把分片迁移到 warm 节点。
    t1t_1
  3. t_2(温转冷阈值)例如 30 天,进入 cold 的索引被标记只读,进一步压缩存储成本。
    t2t_2
  4. ILM 箭头(自动迁移动作)迁移时执行 allocate 指定节点属性、forcemerge 合并段、shrink 收缩分片。
    →  ILM  \xrightarrow{\;ILM\;}
  5. delete(终态)索引超过保留期自动删除,数据生命周期闭环,存储不会无限增长。
    delete\text{delete}

AI 应用场景

日志检索平台(ELK)写入与存储调优

阶段:数据接入与存储优化

解决的问题:日志平台每天涌入数 TB 数据,默认 1 秒刷新导致写入积压、丢数据告警不断;同时历史日志查询频率低,却占用了大量昂贵的 SSD 存储。这就像图书馆每天收到的书全堆在前台,前台再大也会被压垮。

具体案例:某运维监控平台将集群改为 hot-warm-cold 三层架构,热节点只保留 7 天并配 SSD;warm 节点保留 30 天;cold 节点保留半年后自动删除。同时开启 translog async、bulk 批量 5 MB 写入,配合 index lifecycle 自动迁移。改造后写入吞吐提升约 3 倍,存储成本下降 60%,历史日志仍可随时检索。

电商商品搜索的查询与内存优化

阶段:在线检索与推理服务优化

解决的问题:电商商品池有数亿商品,筛选项多(价格区间、品牌、库存、上架时间),若全部字段用 query 参与评分,延迟高居不下;按价格、销量排序时,text 字段的 fielddata 曾直接导致节点 OOM。就像读者每次都用“全文相关度”来筛“价格区间”,浪费且危险。

具体案例:某电商搜索团队把价格、库存、上架时间等条件全部移入 filter 上下文,利用 filter cache 复用结果,QPS 提升约 3 倍;排序字段统一改为数值类型并开启 doc_values,OOM 告警清零;用户翻页超过 1000 条时自动切换 search_after 游标,不再使用深分页。

Python 演示

python37 行
"""ES 容量规划与性能预估小工具""" import math  class ESPlanner:    def __init__(self, total_gb, jvm_heap_gb=None):        self.total_gb = total_gb        self.jvm_heap_gb = jvm_heap_gb or max(4, min(total_gb // 4, 30))     def shard_plan(self, per_shard_gb=40):        return math.ceil(self.total_gb / per_shard_gb)     def total_storage_gb(self, replicas=1):        return self.total_gb * (1 + replicas)     def write_mb_per_s(self, bulk_mb, concurrency, refresh_s, translog_s):        return bulk_mb * concurrency / (refresh_s + translog_s)     def deep_page_cost(self, shards, from_, size):        return shards * (from_ + size)  def main():    es = ESPlanner(total_gb=600)    shards = es.shard_plan(per_shard_gb=40)    print("主分片数:", shards)    print("1副本总存储(GB):", es.total_storage_gb(replicas=1))    print("JVM建议(GB):", es.jvm_heap_gb)    print("写入吞吐(MB/s):",          es.write_mb_per_s(bulk_mb=10, concurrency=4,                            refresh_s=1, translog_s=0.5))    print("深分页代价:", es.deep_page_cost(shards, 100000, 20))  if __name__ == "__main__":    main()
目的:用一个小工具把本文的分片规划、存储估算、写入吞吐与深分页代价公式变成可执行的容量评估脚本。
  1. es = ESPlanner(total_gb=600)创建规划器,传入预计数据总量 600 GB,就像馆长先告诉系统“我们一共要装多少书”。
  2. self.jvm_heap_gb = jvm_heap_gb or max(4, min(total_gb // 4, 30))自动推算 JVM 堆建议值:按数据量的四分之一估算,封顶 30 GB。
  3. return math.ceil(self.total_gb / per_shard_gb)用“数据总量 ÷ 单分片容量”向上取整得到主分片数。
  4. return self.total_gb * (1 + replicas)总存储等于数据量乘以“1+副本数”,这是采购磁盘的依据。
  5. return bulk_mb * concurrency / (refresh_s + translog_s)复现写入吞吐公式:批量大小 × 并发 ÷ 刷新与刷盘总耗时。
  6. return shards * (from_ + size)复现深分页代价公式:分片数 × 偏移量与页大小之和。
  7. if __name__ == "__main__":让脚本既能被 import 调用,也能直接运行输出容量报告。

常见误区

误区分片越多并行度越高,所以把每个分片都设成 1 GB,一台机器放几十个分片,查询肯定更快。

正解分片过多时,每个请求都要广播到全部分片去执行,协调节点合并结果的开销、segment 管理、GC 压力都显著上升;分片过小还会浪费内存与文件句柄。经验上单分片 20-50 GB 是甜点区间。这就像图书馆的书柜组太多,读者每找一本书都要挨个问 500 个书柜组。

为何常见:我们在关系型数据库里习惯了“分区越多越细”能提高并行扫描效率;但 ES 的每个分片都是一个独立搜索引擎,搜索要广播到所有分片,边际收益会快速反转。

误区把 `translog.durability` 改成 async 是无痛的优化,写入性能翻倍还没有任何副作用,生产环境直接上。

正解async 意味着 translog 不再每请求 fsync,而是周期性批量刷盘;一旦节点宕机或断电,缓冲区里最近几秒尚未刷盘的写入会丢失。它本质是用“数据安全”换“写入速度”。如果业务对丢失几秒写入零容忍(比如订单、支付流水),必须保持同步模式。就像图书馆的流水账簿改成“隔几秒才誊写”,一旦停电,这几秒的借书记录就没了。

为何常见:许多教程只强调 async 能提升性能,却把“可能丢数据”放在脚注里,读者容易只记住性能而忽略代价。

误区text 字段也能排序和分组,ES 默认就能对文本做 terms 聚合,挺方便的。

正解text 字段做排序或聚合时会启用 fielddata,把分词后的全部唯一词项加载进堆内存,基数一大直接 OOM,甚至引发集群雪崩。正确做法是对需要排序聚合的字段额外建 keyword 子字段(或直接映射为 keyword),用 doc_values 落盘存列式索引。就像在管理员桌面上贴满便利贴,最初很爽,直到桌面被压垮。

为何常见:刚接触 ES 时,在 Kibana 里拖拽字段做饼图,界面上的 text 字段也能选,于是误以为无成本;直到数据量上去才发现内存爆了。

误区想看第 1000 页,把 from 设成 9990 不就行了?SQL 里 order by + offset 一直这么干。

正解ES 是分布式系统,from+size 会让协调节点从每个分片都取回前 from+size 条,再全局排序;深度翻页代价巨大,且默认 max_result_window=10000 会直接拒绝请求。正确做法是用 search_after(游标)或 PIT 才能高效翻页。这就像让每个书柜组都把前 10 万本书搬出来排序,只为了给读者第 1001 页的那一本。

为何常见:关系型数据库的 offset 分页思维先入为主,没有意识到 ES 底层要在多分片之间取全量候选再排序。

误区副本越多越好,副本既能容灾又能提升读性能,那就把副本设成 3 份,读写都稳了。

正解副本确实是读流量的横向扩展和容灾手段,但它不是免费的:每个副本都要存储完整数据,写入时每条文档要复制到所有副本才返回,副本越多写入放大越严重。写多读少的场景应从 0 副本起步;读多且要高可用的场景,1-2 个副本足够。就像热门书复印三份放在三个分馆,读者取书更快了,但每本新书都要往三个分馆各送一份。

为何常见:直觉上把“副本”当成“备份”的单纯安全措施,忽略了 ES 写路径上的副本同步成本。

面试题

Q1. 在 Elasticsearch 中,决定每个字段如何被索引和存储的定义叫做?

单选题 · easy

  • A.分词器(Analyzer)
  • B.映射(Mapping)← 正确答案
  • C.分片(Shard)
  • D.段(Segment)

答案:B。Mapping 相当于图书馆的编目规则,规定每个“条目”的类型与索引方式;分词器只是其中负责拆词的工具,分片是书柜组,段是磁盘上的文件组织形式。所以答案是 B。

Q2. ES 中刚写入的文档默认约多久后才能被搜索到?

单选题 · easy

  • A.1 秒← 正确答案
  • B.1 毫秒
  • C.30 秒
  • D.立即可见

答案:A。默认 refresh_interval 为 1 秒,新文档最多 1 秒内进入可搜索的 segment,类似编目员每秒统一刷新一次检索目录。毫秒级不可靠,30 秒需要显式配置,“立即可见”不成立。

Q3. 单分片的数据量经验建议保持在什么范围?

单选题 · easy

  • A.1-5 GB
  • B.5-10 GB
  • C.20-50 GB← 正确答案
  • D.200-500 GB

答案:C。20-50 GB 是分片大小的甜点区:太小会导致分片文件与查询广播开销上升,太大则会拖慢恢复与段合并。对应图书馆书柜组“不大不小刚刚好”。

Q4. 关于 fielddata 和 doc_values,下列说法正确的是?

单选题 · mid

  • A.二者都存储在磁盘上
  • B.都使用堆内存做缓存
  • C.fielddata 吃堆内存易 OOM← 正确答案
  • D.doc_values 吃堆内存易 OOM

答案:C。fielddata 把全部唯一词项加载进 JVM 堆,基数大时内存溢出;doc_values 是磁盘上的列式索引,内存友好。选项 A、B、D 的描述都与实际相反,只有 C 正确。

Q5. 配置 ES 的 JVM 堆内存,下列哪个做法是正确的?

单选题 · mid

  • A.设得越大自然越好
  • B.一半物理内存封顶 32 GB← 正确答案
  • C.与分片数量保持一致
  • D.设置为数据总量大小

答案:B。堆内存建议不超过物理内存的一半,且不超过 32 GB(超过后压缩指针失效,性价比反而下降)。剩余内存留给操作系统 page cache 做文件缓存,B 正确。

Q6. 深度分页的正确替代方案是?

单选题 · mid

  • A.无限增大 from 参数
  • B.增加分片数量即可
  • C.改用 search_after 游标← 正确答案
  • D.调大窗口返回上限

答案:C。search_after 保存上一条排序值作为游标,只取其后数据,代价与翻页深度无关;from 深翻页和调大 max_result_window 都会放大协调节点的排序压力,分片数量与深分页无关。

Q7. 将 refresh_interval 从 1 秒调到 30 秒,最直接的影响是什么?

单选题 · hard

  • A.查询相关性大幅提升
  • B.写入吞吐提升但延迟变大← 正确答案
  • C.分片数量自动减少
  • D.分片数量自动增加

答案:B。refresh 间隔增大后,生成 segment 的频率降低、合并压力减小,写入吞吐提升;但新文档最长要 30 秒才可见,搜索可见性延迟变大。相关性由分析器与评分模型决定,分片数量与刷新间隔无关。

Q8. 下列哪些手段可以降低 ES 的内存压力?

多选题 · hard

  • A.排序聚合字段开启 doc_values← 正确答案
  • B.避免大基数 text 字段聚合← 正确答案
  • C.尽量用 filter 代替 query← 正确答案
  • D.调大 refresh 间隔降低成本

答案:ABC。doc_values 把索引落盘,省堆内存;避免大基数 text 聚合可以防止 fielddata 膨胀;filter 走缓存且不评分,减少计算与内存。D 选项调大 refresh 会牺牲写入可见性,并非降低内存压力的通用手段。

Q9. 关于 ES 分片与副本,下列说法正确的是?

多选题 · hard

  • A.主分片数创建后不可修改← 正确答案
  • B.副本数可以动态调整← 正确答案
  • C.副本写入会额外消耗资源← 正确答案
  • D.分片越多查询一定越快

答案:ABC。主分片数在创建索引时确定,之后只能通过 split/shrink 间接调整;副本数可以随时动态调整;副本要同步接收全部写入并占用存储与带宽。分片过多会因广播开销变慢,因此 D 错误。

Q10. 关于 hot-warm-cold 冷热分层架构,下列说法正确的是?

多选题 · extreme

  • A.热节点需高性能 SSD 存储← 正确答案
  • B.冷节点可用廉价机械盘← 正确答案
  • C.ILM 可自动执行删除阶段← 正确答案
  • D.Rollover 需手动触发执行

答案:ABC。热节点放最新热数据,用 SSD 保证性能;冷节点只读历史数据,用机械盘降低成本;ILM 生命周期包含 delete 阶段,可自动清理过期索引。Rollover 由索引策略自动触发,不需要手动执行,因此 D 错误。