跳至主要内容

Elasticsearch从入门到精通指南

Elasticsearch 从入门到精通

面向有编程基础的读者。全文以「概念 → 用法 → 原理 → 进阶」的顺序展开,代码示例均为 Elasticsearch 9.x(截至 2026 年中的稳定版本,特性以 Retriever API / ES|QL 为主)。


目录

  1. Elasticsearch 是什么
  2. 核心概念
  3. 快速上手:REST API 基础操作
  4. Mapping 与字段类型
  5. 查询 DSL 基础
  6. 聚合(Aggregations)
  7. 集群架构深入
  8. 底层数据结构
  9. 相关性打分与 Rank 排序
  10. 向量搜索(Vector Search)
  11. 混合搜索与 Retriever 框架
  12. 生产实践与性能调优
  13. 学习路径建议

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 }
    }
  }
}

初学者最容易踩的坑是不理解 textkeyword 的区别

  • 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:每个分片上参与粗筛的候选数量,越大召回越准但越慢,是召回率与延迟的核心权衡旋钮
  • similaritycosine(余弦相似度,最常用,对向量模长不敏感)、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_candidatesmef_construction、量化精度这几个参数直接决定召回率/延迟/成本的权衡点,建议用真实查询集离线评估 recall@k 后再定参数,而不是凭经验直接抄默认值上生产。
  • 冷热分层(ILM + Hot/Warm/Cold/Frozen):日志、时序类数据配合 Index Lifecycle Management 自动迁移到更便宜的存储层,是控制大规模集群成本的标准做法。
  • 监控关键指标:JVM 堆使用率、GC 频率、indexing/search 线程池队列长度、Segment 数量,这些是排查性能问题的第一手信息,可通过 _cat API 或 Kibana Stack Monitoring 观察。

13. 学习路径建议

  1. 入门(1~2 周):熟练使用 CRUD、bool 查询、基础聚合,理解 text/keyword 区别,能独立设计一个简单业务的 Mapping。
  2. 进阶(掌握架构):理解分片/副本、写入的 refresh/flush/merge 流程、Query Then Fetch 读取模型,能对一个真实业务做分片数与副本数的规划。
  3. 深入(原理层):理解倒排索引、FST、Doc Values、BKD 树、Roaring Bitmap 的各自适用场景,能解释"为什么 filter 比 must 快""为什么深分页慢"这类问题背后的数据结构原因。
  4. 相关性调优:吃透 BM25 的 k1/b,掌握 function_scorerank_featurerescore 的适用场景与性能差异。
  5. 向量与混合搜索:动手搭一个"BM25 + kNN + RRF + 重排"的完整检索管线,用真实数据评估 recall 与延迟,理解量化方案的取舍。
  6. 生产运维:学习 ILM、集群监控、快照备份、滚动升级,具备独立支撑一个生产集群的能力。

建议始终以 Elastic 官方文档(elastic.co/docs)和官方 Search Labs 博客作为权威信息源来源,官方文档对每个版本的 Breaking Changes、API 变更记录非常详尽,能避免踩到过时或不准确的二手教程导致的坑。


文档完

此博客中的热门博文

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...

事务的ACID是什么

 事务的 ACID 是数据库事务必须满足的四个基本性质,用来保证在并发和故障情况下数据的正确性与可靠性: A(Atomicity,原子性) 一个事务中的操作要么 全部成功 ,要么 全部失败回滚 ,不存在“只做了一半”的中间状态。 C(Consistency,一致性) 事务执行前后,数据库都必须处于 一致的合法状态 ,满足约束(如主键、外键、唯一性、业务规则等)。 I(Isolation,隔离性) 并发执行的多个事务之间 相互隔离 ,一个事务未提交的中间结果对其他事务不可见(具体强弱由隔离级别决定)。 D(Durability,持久性) 一旦事务提交成功,其结果会被 永久保存 ,即使系统崩溃也不会丢失(通常依赖 WAL/redo log 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。