Elasticsearch 从入门到精通
面向有编程基础的读者。全文以「概念 → 用法 → 原理 → 进阶」的顺序展开,代码示例均为 Elasticsearch 9.x(截至 2026 年中的稳定版本,特性以 Retriever API / ES|QL 为主)。
目录
- Elasticsearch 是什么
- 核心概念
- 快速上手:REST API 基础操作
- Mapping 与字段类型
- 查询 DSL 基础
- 聚合(Aggregations)
- 集群架构深入
- 底层数据结构
- 相关性打分与 Rank 排序
- 向量搜索(Vector Search)
- 混合搜索与 Retriever 框架
- 生产实践与性能调优
- 学习路径建议
1. Elasticsearch 是什么
Elasticsearch(简称 ES)是一个基于 Apache Lucene 构建的分布式搜索与分析引擎。理解它,最好从三个关键词入手:
- 搜索引擎:底层是倒排索引,擅长全文检索、相关性排序。
- 分布式系统:数据自动分片(shard)并可跨节点复制,具备水平扩展和高可用能力。
- 近实时(Near Real-Time)分析引擎:不仅能搜索,还能做聚合统计,因此常被用作日志分析(ELK/Elastic Stack)、可观测性、向量检索/RAG 的后端。
一句话总结:ES = 分布式的 Lucene + REST API + 集群管理能力。Lucene 负责单机上的索引与检索算法,ES 负责把多台机器上的多个 Lucene 实例组织成一个逻辑整体,并提供 JSON/REST 的开发者友好接口。
2. 核心概念
| 概念 | 类比(关系型数据库) | 说明 |
|---|---|---|
| Index(索引) | Database / Table | 具有相同 mapping 的文档集合 |
| Document(文档) | Row | 一条 JSON 数据,是 ES 中最小的可搜索单元 |
| Field(字段) | Column | 文档中的一个键值对 |
| Mapping | Schema | 定义字段类型、分词方式等 |
| Shard(分片) | 分区(Partition) | 索引被水平切分成的 Lucene 实例,是并行与扩展的基本单位 |
| Replica(副本) | 冗余备份 | 分片的复制,用于容灾和读吞吐扩展 |
| Node(节点) | 数据库实例 | 一个 ES 进程 |
| Cluster(集群) | 数据库集群 | 一组协同工作的节点,共享同一个 cluster name |
需要特别强调的一点:ES 不是关系型数据库,它没有强事务、没有 JOIN(虽然有 nested / parent-child 模拟关联),它的设计目标是"写入一次,海量并发查询",这个假设贯穿了它几乎所有的架构决策。
3. 快速上手:REST API 基础操作
ES 的一切操作都通过 HTTP + JSON 完成,方法上贴近 REST 语义。
# 创建索引
PUT /products
# 写入一条文档(自动生成 _id)
POST /products/_doc
{
"name": "机械键盘",
"price": 299,
"tags": ["电子", "外设"],
"created_at": "2026-01-10"
}
# 指定 _id 写入(存在则覆盖)
PUT /products/_doc/1
{
"name": "无线鼠标",
"price": 129
}
# 读取
GET /products/_doc/1
# 局部更新
POST /products/_update/1
{
"doc": { "price": 99 }
}
# 删除
DELETE /products/_doc/1
# 批量操作(bulk),生产环境写入必用
POST /_bulk
{ "index": { "_index": "products", "_id": "2" } }
{ "name": "机械键盘 Pro", "price": 399 }
{ "update": { "_index": "products", "_id": "1" } }
{ "doc": { "price": 89 } }
关键心智模型:写入是异步可见的。文档写入后并不会立刻可搜索,而是要等待一次 refresh(默认 1 秒一次),这也是"近实时"的来源,第 7 节会深入解释背后的机制。
4. Mapping 与字段类型
Mapping 决定了字段"怎么被索引",这直接影响能否搜索、怎么排序、怎么聚合。
PUT /products
{
"mappings": {
"properties": {
"name": { "type": "text", "analyzer": "standard" },
"name_kw": { "type": "keyword" },
"price": { "type": "float" },
"tags": { "type": "keyword" },
"created_at": { "type": "date" },
"location": { "type": "geo_point" },
"description": { "type": "text" },
"embedding": { "type": "dense_vector", "dims": 768 }
}
}
}
初学者最容易踩的坑是不理解 text 与 keyword 的区别:
text:会被分词器切分成多个 term,用于全文检索(如 "机械键盘" 被切成 "机械" "键盘")。不能直接用于精确匹配、排序、聚合。keyword:整个字符串作为一个不可分割的 term,用于精确匹配、排序、聚合、faceting。
因此实践中常用 multi-field 技巧,同一字段同时保留两种索引方式:
"name": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" }
}
}
这样既能对 name 做全文搜索,也能对 name.keyword 做精确聚合。
5. 查询 DSL 基础
ES 的查询分为两种上下文(Context),理解这个区分对后续学习打分机制至关重要:
- Query Context:回答"这个文档与查询的匹配程度如何",会计算相关性分数
_score。 - Filter Context:回答"这个文档匹配还是不匹配"(布尔),不计算分数,且结果会被缓存,性能更高。
GET /products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "description": "无线 蓝牙" } }
],
"filter": [
{ "term": { "tags": "外设" } },
{ "range": { "price": { "gte": 50, "lte": 500 } } }
],
"should": [
{ "match": { "name": "Pro" } }
],
"must_not": [
{ "term": { "tags": "停产" } }
]
}
}
}
bool 查询的四个子句语义:
must:必须匹配,参与打分(相当于 AND,且贡献分数)filter:必须匹配,不参与打分(相当于 AND,纯过滤,会被缓存)should:可选匹配,命中会加分(相当于 OR / 加分项)must_not:必须不匹配(相当于 NOT),不参与打分
性能原则:能放进 filter 的条件(如精确值、范围、时间区间)都不要放进 must,因为 filter 可以跳过打分计算并利用查询缓存。
6. 聚合(Aggregations)
聚合是 ES 区别于纯搜索引擎的关键能力,让它同时具备类似 SQL GROUP BY 的分析功能。
GET /products/_search
{
"size": 0,
"aggs": {
"by_tag": {
"terms": { "field": "tags", "size": 10 },
"aggs": {
"avg_price": { "avg": { "field": "price" } }
}
},
"price_histogram": {
"histogram": { "field": "price", "interval": 100 }
}
}
}
聚合可以嵌套(bucket 内再做 bucket 或 metric),也可以与 query 结合,即"先过滤,再聚合过滤后的结果集"。日志分析、监控看板(Kibana)背后大量依赖聚合能力。
7. 集群架构深入
7.1 节点角色
一个生产集群通常会按角色拆分节点,而不是让每个节点什么都做:
| 角色 | 职责 |
|---|---|
| Master-eligible node | 参与集群状态选举与管理(索引创建/删除、分片分配决策),不建议承担查询/写入压力 |
| Data node | 实际存储分片、执行索引和查询的计算密集型工作,进一步细分为 hot/warm/cold/frozen 分层 |
| Ingest node | 在文档写入前执行 pipeline 预处理(如字段提取、Grok 解析) |
| Coordinating node | 接收客户端请求,负责路由请求到相关分片并合并结果("scatter-gather") |
| Machine Learning node | 运行异常检测等 ML 任务 |
小集群里一个节点常常身兼多角色,但大规模生产集群会显式拆分,避免 master 选举被查询负载干扰。
7.2 分片与副本
一个 Index 被水平切分为多个 Primary Shard(分片数在创建索引时确定,ES 7+ 之后不可随意更改数量,需要 reindex 或 split/shrink API),每个 Primary Shard 可以有 0 或多个 Replica Shard。
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
- 分片数决定了写入的并行度上限和单个 Lucene 索引的数据量上限(分片过大会拖慢 merge 和恢复速度,业界经验值通常控制在几十 GB 级别)。
- 副本数决定了读吞吐扩展能力和容灾能力(副本可以被路由到其他节点分担查询压力,并在主分片所在节点故障时被提升为主分片)。
- 分片数设置过多(over-sharding)是新手最常见的错误:每个分片都有固定的内存开销(如 FST、doc values 缓存),分片过多会显著浪费堆内存并拖慢集群状态同步。
7.3 写入流程:从内存到磁盘
这是理解 ES "近实时"特性的核心,也是最常被问到的架构问题:
客户端写入
│
▼
写入内存缓冲区(in-memory buffer) + 写入 Translog(预写日志,落盘)
│
▼ (每隔 1s,即 refresh_interval)
Refresh:内存缓冲区生成一个新的 Lucene Segment,写入文件系统缓存(尚未 fsync)
│ → 此时文档【已可被搜索】,但还没有持久化保证
▼ (每隔 30 分钟,或 translog 过大时)
Flush:调用 Lucene commit,将 Segment fsync 到磁盘,并清空 Translog
│ → 此时文档【已持久化】,即使宕机也不会丢
▼ (后台持续进行)
Merge:多个小 Segment 被后台合并成更大的 Segment,删除已标记删除的文档
几个关键点:
- Segment 不可变(immutable):Lucene Segment 一旦生成就不再修改,删除/更新操作实际上是"标记删除 + 写入新文档",旧数据靠 Merge 时被真正清理。这是 Lucene 高性能读取的基础——不可变结构可以放心地做各种缓存和只读优化。
- Translog 保证了 crash safety:如果节点在 flush 之前崩溃,重启时会重放 Translog 恢复未落盘的数据。
- 如果你在做批量导入且不需要立刻可搜索,可以临时调大或关闭
refresh_interval,大幅提升写入吞吐(写完再手动 refresh 一次)。
7.4 读取流程:Query Then Fetch
1. Query 阶段:Coordinating node 把查询广播给相关的每个分片(主或副本任选其一)
每个分片在本地计算 Top-N 并返回 [doc_id, score](不返回完整文档内容)
2. Fetch 阶段:Coordinating node 合并所有分片的结果,排序取全局 Top-N
再向持有这些具体文档的分片发起请求,取回完整 _source
这解释了为什么 深分页(deep pagination)代价高:from: 10000, size: 10 意味着每个分片都要各自排出前 10010 条再由协调节点合并,分片越多、页码越深,开销越大。解决方案是用 search_after(基于游标)或 scroll/point in time(批量导出场景)代替 from/size 翻页。
8. 底层数据结构
这一节回答"ES 为什么这么快",理解这些结构对后续学习向量检索和排序原理也有帮助。
8.1 倒排索引(Inverted Index)
正排索引是 文档 → 词,倒排索引反过来是 词 → 文档列表:
词项(Term) → 倒排列表(Posting List)
"机械" → [doc1, doc3, doc7]
"键盘" → [doc1, doc3]
"无线" → [doc2, doc5]
搜索"机械键盘"时,只需取交集,不用扫描全表,这是搜索引擎比 LIKE '%xxx%' 快几个数量级的根本原因。倒排列表通常用 Frame of Reference + 差值编码(delta encoding) 压缩存储的 doc id,进一步节省磁盘和内存。
8.2 FST:词典的存储结构
倒排索引的"词" → "倒排列表指针"这部分(Term Dictionary)用 有限状态转换器(Finite State Transducer, FST) 存储。FST 把有公共前缀的字符串压缩成一张类 Trie 的图结构,既节省内存又支持前缀/范围查询的高效跳转,是 Lucene 能把海量词典常驻内存的关键数据结构。
8.3 Doc Values:面向排序和聚合的列式存储
倒排索引擅长"给定词找文档",但排序、聚合需要相反的方向——"给定文档取字段值"。为此 Lucene 引入了 Doc Values:按列(field)存储,同一列的数据物理上连续排列,天然适合压缩(如对整数列做 bit-packing)和批量扫描。
keyword、数值、日期、geo_point类型默认开启 doc values。text类型默认关闭 doc values(因为分词后不适合按列存储原始值),这也是为什么text字段不能直接排序/聚合,需要配合keyword子字段。
可以把 Doc Values 理解为 ES 内置的"列式数据库"部分,倒排索引 + Doc Values 的组合,让 ES 同时具备行式检索和列式分析的能力。
8.4 BKD 树:数值、日期与地理查询
对于数值 range 查询、geo_point/geo_shape 空间查询,Lucene 使用 BKD 树(Block K-Dimensional tree,源自 K-D 树,为磁盘/块存储优化)。它支持高效的多维范围过滤,是 range 查询和地理围栏查询快速的原因。
8.5 Roaring Bitmap:Filter 缓存
filter 上下文中的查询结果(一组 doc id 的集合)会用 Roaring Bitmap 编码缓存。这是一种根据数据稀疏/密集程度自适应切换存储方式(数组 vs 位图)的压缩位图结构,既节省内存又能做极快的 AND/OR 集合运算——这正是多个 filter 条件组合时性能优异的原因。
8.6 Analyzer:分词器的三段流水线
text 字段的索引过程由 Analyzer 完成,它总是由三部分组成:
Character Filter(字符过滤,如去 HTML 标签)
↓
Tokenizer(分词,如按空格/中文分词器切分)
↓
Token Filter(词元处理,如小写化、去停用词、同义词、词干提取)
中文场景通常需要额外安装 IK 分词器或使用内置的 ICU 插件,因为标准分词器是按空格/标点切分的,对中文几乎无效。
9. 相关性打分与 Rank 排序
9.1 BM25:默认的相关性算法
ES 默认使用 BM25(Okapi BM25 的实现)计算 _score,它是 TF-IDF 的改进版,核心思想:
- 词频(TF)饱和:一个词出现次数越多分数越高,但增益递减(出现 100 次和 101 次几乎没区别),避免关键词堆砌被过度加分。
- 逆文档频率(IDF):一个词在语料库中越罕见,包含它的文档匹配价值越高。
- 文档长度归一化:短文档中出现关键词的"含金量"通常高于长文档,BM25 会做长度惩罚。
公式的两个可调参数:
k1(默认 1.2):控制词频饱和的速度。b(默认 0.75):控制文档长度归一化的强度(0 表示完全不考虑长度)。
PUT /products
{
"settings": {
"similarity": {
"my_bm25": { "type": "BM25", "k1": 1.5, "b": 0.6 }
}
},
"mappings": {
"properties": {
"description": { "type": "text", "similarity": "my_bm25" }
}
}
}
9.2 function_score / script_score:自定义打分
当业务需要"相关性之外的因素"参与排序(如新品加权、销量、距离衰减)时:
GET /products/_search
{
"query": {
"function_score": {
"query": { "match": { "description": "蓝牙耳机" } },
"functions": [
{ "field_value_factor": { "field": "sales", "modifier": "log1p", "factor": 0.1 } },
{ "gauss": { "created_at": { "origin": "now", "scale": "30d", "decay": 0.5 } } }
],
"boost_mode": "sum",
"score_mode": "avg"
}
}
}
script_score 则允许用 Painless 脚本写任意打分公式,灵活但性能成本更高,一般只用于 rescore 阶段的少量候选集,而不是全量打分。
9.3 rank_feature / rank_features:高效的静态特征加权
如果只是想把某个数值特征(如 PageRank 值、点击率、热度分)加入排序,比起 function_score,专用的 rank_feature 字段类型效率更高——它在索引阶段就用特殊编码(基于对数刻度的饱和函数)存储特征值,查询时几乎零额外开销:
"pagerank": { "type": "rank_feature" }
{ "rank_feature": { "field": "pagerank" } }
rank_features(复数)则用于稀疏、字段名不固定的特征集合(如"标签 → 权重"这种动态映射),常见于搜索相关性调优和后面会提到的稀疏向量场景。
9.4 Rescore:在小候选集上做精细打分
rescore 允许对第一阶段查询返回的 Top-N(默认仅 10 条,由 window_size 控制)用更昂贵的模型重新打分,兼顾性能与精度:
{
"query": { "match": { "description": "无线键盘" } },
"rescore": {
"window_size": 100,
"query": {
"rescore_query": { "script_score": { "query": { "match_all": {} }, "script": { "source": "..." } } }
}
}
}
这是"粗排 + 精排"两阶段检索思想在 ES 中的原生实现,也是后面向量 rerank 的基础模式。
10. 向量搜索(Vector Search)
10.1 为什么需要向量搜索
倒排索引解决的是"词面匹配",但语义相近、字面不同的查询(如"笔记本电脑"vs"手提电脑")无法被传统检索捕捉。向量搜索把文本 / 图片 / 音频通过 Embedding 模型映射到高维向量空间,用向量间的距离/相似度衡量语义相关性,是 RAG(检索增强生成)系统的标配组件。
10.2 dense_vector 字段与 kNN 检索
PUT /articles
{
"mappings": {
"properties": {
"title": { "type": "text" },
"content_embedding": {
"type": "dense_vector",
"dims": 768,
"similarity": "cosine"
}
}
}
}
GET /articles/_search
{
"knn": {
"field": "content_embedding",
"query_vector": [0.12, -0.03, ...],
"k": 10,
"num_candidates": 100
}
}
k:最终返回的最近邻数量。num_candidates:每个分片上参与粗筛的候选数量,越大召回越准但越慢,是召回率与延迟的核心权衡旋钮。similarity:cosine(余弦相似度,最常用,对向量模长不敏感)、dot_product(点积,需要向量已归一化,计算更快)、l2_norm(欧氏距离)。
10.3 HNSW:近似最近邻算法
精确 kNN(暴力比对所有向量)在百万级以上数据量下代价极高,ES 默认使用 HNSW(Hierarchical Navigable Small World) 做近似最近邻检索:
- 核心思想是构建多层图结构:顶层节点稀疏、跳转距离大,用于快速定位大致区域;底层节点密集,用于精细搜索。检索时从顶层入口贪心地一路下降,逐层收窄范围,复杂度接近对数级别,而不是线性扫描。
- 两个关键构建参数:
m(每个节点的最大连接数,越大召回越高但索引越大越慢)、ef_construction(构建时的搜索宽度,影响索引质量和构建耗时)。
"content_embedding": {
"type": "dense_vector",
"dims": 768,
"index_options": {
"type": "hnsw",
"m": 16,
"ef_construction": 100
}
}
HNSW 是"近似"算法,意味着召回率不是 100%,但在实践中通过合理调参可以做到 95%+ 召回率的同时获得数量级的性能提升,这个权衡对绝大多数业务场景是合算的。
10.4 向量量化:用精度换内存与速度
原始 float32 向量存储和计算成本很高(768 维 float32 向量占 3KB,千万级文档就是几十 GB 仅用于向量本身)。ES 提供了多级量化方案:
| 方式 | 说明 |
|---|---|
int8 标量量化 |
每维压缩到 1 字节,内存降至约 1/4,召回损失很小 |
int4 量化 |
进一步压缩到半字节,内存降至约 1/8 |
bbq(Better Binary Quantization) |
二值化量化,压缩比最高,配合 rescore 弥补精度损失 |
bfloat16 |
半精度浮点,内存减半,精度损失比整数量化更小 |
"content_embedding": {
"type": "dense_vector",
"dims": 768,
"index_options": { "type": "bbq_hnsw" }
}
近期版本还引入了 VectorDB 索引模式,通过一个开关自动应用已调优的量化、合并策略与缓存配置,减少手工调参负担:
PUT /my_index
{ "settings": { "index": { "mode": "vectordb_document" } } }
10.5 稀疏向量与 ELSER
除了稠密向量,ES 还支持稀疏向量(sparse_vector 字段配合 Elastic 自研的 ELSER 模型),本质上是"用模型学出来的词项权重"而不是人工 TF-IDF 权重,兼具倒排索引的可解释性/效率与语义理解能力,是 Elastic 生态里"开箱即用语义检索"的默认路线。
10.6 semantic_text:把 Embedding 复杂度隐藏起来
手动管理 Embedding 生成、更新模型、维护 pipeline 相当繁琐。较新版本提供了 semantic_text 字段类型,声明字段后 ES 会自动调用配置好的推理端点(可以是 ELSER,也可以通过 Inference API 接入 OpenAI/Cohere/Anthropic 等外部模型)完成向量化:
PUT /articles
{
"mappings": {
"properties": {
"content": { "type": "semantic_text", "inference_id": "my-embedding-endpoint" }
}
}
}
之后正常写入文本即可,无需在应用层手动调用 Embedding API 再拼接向量字段——这大幅降低了语义搜索的接入门槛。
11. 混合搜索与 Retriever 框架
11.1 为什么需要混合搜索
BM25 擅长精确词匹配(型号、专有名词、报错代码),向量搜索擅长语义相近但字面不同的匹配。生产系统通常两者都要,这就是混合搜索(Hybrid Search)。
11.2 Retriever API:组合式检索的统一入口
较新版本引入了 Retriever 抽象,把"检索"这件事从单一 query 字段解耦成可组合的模块:
GET /articles/_search
{
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": { "match": { "title": "无线蓝牙耳机" } }
}
},
{
"knn": {
"field": "content_embedding",
"query_vector": [0.1, 0.2, 0.3],
"k": 10,
"num_candidates": 100
}
}
],
"rank_window_size": 50,
"rank_constant": 20
}
}
}
11.3 RRF:不比较原始分数,只比较排名
BM25 的分数和向量相似度分数量纲完全不同(一个可能是 0~30 的浮点,一个是 0~1 的余弦相似度),直接加权求和没有意义。Reciprocal Rank Fusion(倒数排名融合) 巧妙地绕开了这个问题:
RRF_score(d) = Σ 1 / (rank_constant + rank_i(d))
对文档 d,取它在每个检索列表中的排名(而不是分数),用排名的倒数之和作为最终融合分数——排名靠前的文档贡献更大,且不同检索方式的分数量纲差异被完全消除。rank_constant(常见默认值 60,示例中的 20 也是常用取值)用于平滑排名靠前几位之间的差距,值越大,靠前名次之间的分数差异被压得越平缓。
11.4 语义重排(Semantic Rerank)与 LTR
RRF 之上还可以再叠一层更精细的重排:
{
"retriever": {
"text_similarity_reranker": {
"retriever": { "rrf": { "retrievers": [ /* ... */ ] } },
"field": "content",
"inference_id": "my-rerank-model",
"rank_window_size": 20
}
}
}
这利用专门的 Cross-Encoder 重排模型(比双塔式 Embedding 更精细但更慢)只对粗排后的少量候选(如前 20 条)做精细打分,是"多路召回(BM25 + kNN)→ RRF 融合 → 语义重排"这一业内标准 RAG 检索管线在 ES 中的原生落地方式。更进一步,Elastic 也提供了 Learning to Rank(LTR) 插件,允许接入训练好的机器学习排序模型(如 LambdaMART)做重排,是从"规则打分"迈向"模型化排序"的路径。
12. 生产实践与性能调优
以下是从架构原理直接推导出的实战建议,供进阶阶段参考:
- 分片规划:单分片大小建议控制在几十 GB 内;分片数不是越多越好,过度分片会浪费堆内存和集群状态同步开销。
- 写入优化:批量导入时用
_bulk,临时调大refresh_interval(甚至设为-1)、写入期间设number_of_replicas: 0,完成后再恢复,可大幅提升吞吐。 - 避免深分页:用
search_after+point in time代替大from值。 - filter 优先于 must:可缓存、不参与打分的条件尽量放进
filter。 - Mapping 提前设计好:
text/keyword该用 multi-field 就用,避免上线后被迫 reindex。 - 向量检索先跑评测再上量化:
num_candidates、m、ef_construction、量化精度这几个参数直接决定召回率/延迟/成本的权衡点,建议用真实查询集离线评估 recall@k 后再定参数,而不是凭经验直接抄默认值上生产。 - 冷热分层(ILM + Hot/Warm/Cold/Frozen):日志、时序类数据配合 Index Lifecycle Management 自动迁移到更便宜的存储层,是控制大规模集群成本的标准做法。
- 监控关键指标:JVM 堆使用率、GC 频率、
indexing/search线程池队列长度、Segment 数量,这些是排查性能问题的第一手信息,可通过_catAPI 或 Kibana Stack Monitoring 观察。
13. 学习路径建议
- 入门(1~2 周):熟练使用 CRUD、
bool查询、基础聚合,理解text/keyword区别,能独立设计一个简单业务的 Mapping。 - 进阶(掌握架构):理解分片/副本、写入的 refresh/flush/merge 流程、Query Then Fetch 读取模型,能对一个真实业务做分片数与副本数的规划。
- 深入(原理层):理解倒排索引、FST、Doc Values、BKD 树、Roaring Bitmap 的各自适用场景,能解释"为什么 filter 比 must 快""为什么深分页慢"这类问题背后的数据结构原因。
- 相关性调优:吃透 BM25 的
k1/b,掌握function_score、rank_feature、rescore的适用场景与性能差异。 - 向量与混合搜索:动手搭一个"BM25 + kNN + RRF + 重排"的完整检索管线,用真实数据评估 recall 与延迟,理解量化方案的取舍。
- 生产运维:学习 ILM、集群监控、快照备份、滚动升级,具备独立支撑一个生产集群的能力。
建议始终以 Elastic 官方文档(elastic.co/docs)和官方 Search Labs 博客作为权威信息源来源,官方文档对每个版本的 Breaking Changes、API 变更记录非常详尽,能避免踩到过时或不准确的二手教程导致的坑。
文档完