# ES 基础


## 1. 倒排索引

### 1.1 什么是倒排索引

倒排索引是一种 **从内容到文档的映射** 数据结构，它和“正向索引”相反。

* **正向索引**：文档 → 内容
* **倒排索引**：内容（词语） → 出现该词语的文档列表

它的核心作用是 **快速找到包含某个词的文档**，是搜索引擎（比如 ElasticSearch、Lucene）最核心的数据结构。

---

### 1.2 举例说明

假设我们有 3 篇文档：

| 文档ID | 内容        |
| ---- | --------- |
| D1   | 我喜欢苹果和香蕉  |
| D2   | 苹果很好吃     |
| D3   | 香蕉和橘子都是水果 |

#### Step 1: 分词（简单按空格或词语切分）

* D1 → 我, 喜欢, 苹果, 和, 香蕉
* D2 → 苹果, 很好吃
* D3 → 香蕉, 和, 橘子, 都是, 水果

#### Step 2: 构建倒排索引

把每个词对应出现的文档列出来：

| 词语  | 文档列表   |
| --- | ------ |
| 我   | D1     |
| 喜欢  | D1     |
| 苹果  | D1, D2 |
| 和   | D1, D3 |
| 香蕉  | D1, D3 |
| 很好吃 | D2     |
| 橘子  | D3     |
| 都是  | D3     |
| 水果  | D3     |

---


### 1.3 倒排索引的结构

![倒排索引](/images/es/post_index.png)

如上图所示，倒排索引有如下核心结构:

#### 1️⃣ 词条（Term）

* 文档经过 **分析器（Analyzer）** 处理后得到的最小索引单位。
* 可以是单词、符号、或者中文分词后的词。

英文分词比较简单，常用分词器包括：

| 分词器            | 说明                                  |
| -------------- | ----------------------------------- |
| **standard**   | 默认分词器，按照空格、标点符号切分，并做小写处理。           |
| **simple**     | 按非字母字符切分，忽略大小写，不做停用词过滤。             |
| **whitespace** | 仅按空格分词，不做其他处理。                      |
| **keyword**    | 不拆分整个字段，作为一个整体索引，常用于精确匹配。           |
| **stop**       | 类似 standard，但会去掉停用词（如 “the”, “is”）。 |


中文没有天然的空格分词，需要专门的中文分词器。ElasticSearch 官方没有默认中文分词器，但常用插件有：

| 分词器               | 来源/说明                                          |
| ----------------- | ---------------------------------------------- |
| **ik_max_word** | IK 分词器插件，尽可能切分出所有可能的词（最大化匹配），适合搜索场景。           |
| **ik_smart**     | IK 分词器插件，智能切分，倾向于最自然的分词，适合精确匹配或分析场景。           |
| **jieba**         | 基于 Python 的中文分词库，ElasticSearch 可通过插件或自定义分析器集成。 |

es 分词器配置:

```json
PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_chinese_analyzer": {
          "type": "ik_max_word"
        },
        "my_english_analyzer": {
          "type": "standard"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "my_chinese_analyzer"
      },
      "description": {
        "type": "text",
        "analyzer": "my_english_analyzer"
      }
    }
  }
}

```
---

#### 2️⃣ 词典（Dictionary / Term Dictionary）

* 保存了索引中 **所有唯一词条的集合**，通常按字典序排序。
* 作用是快速查找某个词条是否存在，并定位它在倒排表中的位置。

---

#### 3️⃣ 倒排表（Posting List）

* 是倒排索引的核心数据结构，用于快速定位匹配文档。
* 倒排表通常包含以下几个部分：

| 字段               | 说明                  |
| ---------------- | ------------------- |
| **词项（Term）**     | 被索引的词，比如 “苹果”       |
| **文档ID（DocID）**  | 出现该词的文档标识           |
| **词频（TF）**       | 词在文档中出现的次数，可选       |
| **位置（Position）** | 词在文档中的顺序，用于短语搜索     |
| **偏移量（Offset）**  | 词在原文中的起止字符位置，用于高亮显示 |

---

#### 4️⃣ 倒排文件（Inverted File）

* 倒排索引在磁盘上的物理存储文件，通常包含 **词典 + 倒排表**。
* Lucene/ElasticSearch 会将倒排表和词典压缩存储，提高查询效率。

---

## 2. 主节点选举

| 方面        | Zen1                              | Zen2 优势                                            |
| --------- | --------------------------------- | -------------------------------------------------- |
| **脑裂防护**  | 依赖 `minimum_master_nodes` 配置，容易出错 | 使用 **quorum（法定票数）+任期（term）** 自动保证单主节点，几乎杜绝脑裂       |
| **选举算法**  | 基于 cluster state 版本和节点 ID         | 基于 **Raft 共识变种**，有明确的 term、投票和 Candidate 状态，更严格和可靠 |
| **状态同步**  | 广播集群状态，可能有延迟和冲突                   | **Leader-Follower 模型**，新主节点选出后，自动与大多数节点对齐最新状态      |
| **选举稳定性** | 网络抖动时容易选出落后节点                     | **随机重试时间 + 任期控制**，选举冲突概率低，收敛快                      |
| **操作简化**  | 管理员需配置 `minimum_master_nodes`     | 无需配置，集群自动计算 quorum，提高易用性                           |
| **故障恢复**  | 网络分区后恢复慢，可能丢失元数据                  | 网络分区恢复快，新 Leader 能自动同步最新集群状态                       |


## 3. 集群元数据

## 4. Elasticsearch 写入与存储机制

### 4.1 延迟写策略（近实时搜索）

* **写入不是直接 fsync 到磁盘**，而是先写入内存（JVM 堆）和文件系统缓存（操作系统内存），提升写入性能，避免每条数据都触发磁盘 I/O。
* **刷新（Refresh）机制**：

  * 默认每秒自动刷新一次，每个分片生成一个新的 **段（Segment）**。
  * 内存中的数据刷新到文件系统缓存后即可被搜索，但尚未真正持久化到磁盘。
  * 手动刷新：`POST /_refresh` 或 `POST /index/_refresh`。
  * 可调 `refresh_interval` 优化写入性能（如日志批量写入时可延长刷新间隔）。

### 4.2 事务日志（Translog）保证数据安全

* **内存 + Translog 机制**：

  * 文档写入内存的同时，也追加写入事务日志。
  * 避免断电或异常导致的数据丢失。
  * Translog 用于重放未刷新到磁盘的写入。

### 4.3 Flush 机制（持久化）

* **触发条件**：

  * 内存数据或 Translog 达到一定大小（默认 512MB）
  * 或时间超过阈值（默认 30 分钟）

* **操作**：

  1. 内存数据写入新段（Segment）
  2. 文件系统缓存刷新到磁盘（fsync）
  3. 生成提交点（Commit Point）
  4. 清空旧 Translog，创建新的空日志

* **结果**：保证数据持久化安全，即使重启或断电，也能通过提交点和 Translog 恢复所有数据。

### 4.4 核心流程图（文字版）

```
客户端写入文档
       │
       ▼
写入内存（JVM 堆） + 追加到 Translog（防丢失）
       │
       ▼
达到 refresh_interval 或内存阈值？
       ├─否─> 新数据继续写入内存
       └─是─> Refresh -> 生成新段（Segment），可搜索
                       │
                       ▼
达到 flush 条件？
       ├─否─> 等待下一次 flush
       └─是─> Flush -> 段写入磁盘（fsync） + 提交点 + 清空 Translog
```

### 4.5 关键特点

* **近实时搜索**：刷新后文档可搜索，但未必持久化。
* **高写入性能**：延迟写减少频繁 fsync，利用内存和文件系统缓存。
* **数据安全**：Translog + Flush 机制防止断电或重启丢失数据。
* **可调性**：`refresh_interval` 和 flush 策略可根据业务需求优化写入性能或检索实时性。


## 5. term index


## 6. 参数调优

好的，我把你提供的文本内容整理成一个 **Elasticsearch 性能优化参数表格**，按参数/操作、作用、适用场景、注意事项列出：

| 参数 / 操作                 | 作用                                | 适用场景             | 注意事项                      |
| ----------------------- | --------------------------------- | ---------------- | ------------------------- |
| 文档 ID 设计                | 使用有序、可压缩的序列模式 ID                  | 批量写入、Lucene 压缩优化 | 避免随机 UUID-4，会降低压缩比、拖慢索引性能 |
| Doc Values              | 禁用不需要聚合/排序的字段的 Doc Values         | 非聚合/排序字段         | 可以减少磁盘和内存开销，但无法用于聚合/排序    |
| 字段类型选择                  | 不需要模糊检索的字段使用 Keyword 类型代替 Text 类型 | 精确匹配字段           | Keyword 字段不分词，索引更轻量       |
| refresh\_interval       | 调整索引刷新间隔                          | 搜索结果不要求近实时       | 默认 1s，可改为 30s 提高写入性能      |
| refresh\_interval（批量导入） | 导入期间设置为 -1 关闭刷新                   | 大批量数据导入          | 导入完成后需恢复刷新，数据在导入期间不可搜索    |
| number\_of\_replicas    | 导入期间设置为 0 关闭副本                    | 大批量数据导入          | 导入完成后需恢复副本，否则节点宕机会丢数据     |
| 分页查询优化                  | 使用 Scroll 查询代替深度分页                | 深度分页（from 很大）查询  | 避免普通分页造成的优先队列排序和 CPU 消耗   |
| 减少映射字段                  | 只保留需要检索、聚合、排序的字段                  | 高性能索引/查询         | 其他字段可存储在外部存储（如 HBase）     |
| Routing                 | 创建索引和查询时指定路由值                     | 精确查询、热点数据分片      | 路由值需保证数据均衡，避免单个分片压力过大     |


## 7. 向量数据库
在 **7.3 版本**开始，Elasticsearch 引入了 **dense_vector** 字段类型，用于存储和检索向量，逐步支持语义搜索、推荐系统、RAG 等场景。

所以现在 ES 既可以作为 **全文搜索引擎**，也能作为 **向量数据库** 使用。

---

### 7.1 向量存储方式

ES 提供两种主要的向量存储方式：

#### `dense_vector`

* 存储 **浮点向量**（dense vector）
* 典型用于存储 **embedding 向量**
* 可以指定维度（如 `dims: 768`）

例子：

```json
PUT my_index
{
  "mappings": {
    "properties": {
      "text_vector": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,   
        "similarity": "cosine"
      }
    }
  }
}
```

#### `sparse_vector`

* 存储稀疏向量，通常用于 **BM25** 或传统稀疏表示
* 使用频率低于 dense\_vector

---

### 7.2 相似度搜索

Elasticsearch 在向量检索上支持 **ANN（Approximate Nearest Neighbor）索引**，底层使用 **HNSW (Hierarchical Navigable Small World)** 图结构。

常见的相似度度量方式：

* **cosine**（余弦相似度，常用于语义搜索）
* **l2_norm**（欧几里得距离）
* **dot_product**（点积）

查询示例：

```json
POST my_index/_search
{
  "knn": {
    "field": "text_vector",
    "query_vector": [0.12, -0.34, 0.98, ...],
    "k": 10,
    "num_candidates": 100
  }
}
```

* `k`: 返回的最近邻数量
* `num_candidates`: 候选数量，越大越准，但查询速度变慢

---

### 7.3 混合搜索 (Hybrid Search)

ES 向量检索的一大优势是可以直接和 **全文检索 (BM25)** 结合，实现 **Hybrid Search**。

常见模式：

1. **向量搜索**：语义相关性
2. **关键字搜索**：精确匹配
3. **加权融合**：比如 RRF（Reciprocal Rank Fusion）

例子：

```json
POST my_index/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "knn": {
            "field": "text_vector",
            "query_vector": [...],
            "k": 10,
            "num_candidates": 100
          }
        },
        {
          "match": {
            "content": "向量数据库"
          }
        }
      ]
    }
  }
}
```

---

### 7.4 向量数据库 vs 专门的向量引擎

Elasticsearch 的向量搜索和 **Milvus / Weaviate / Pinecone / Qdrant** 这些纯向量数据库相比，有以下特点：

### 优势

* 生态成熟：全文检索 + 向量检索一体
* 企业级安全、权限、集群、索引机制健全
* **混合搜索能力强**，适合 RAG（LLM + ES）
* 内置 Kibana 可视化

### 劣势

* 索引和存储开销大（HNSW 内存占用高）
* 查询延迟比不上专门的向量库（但一般能接受）
* 向量写入更新性能略弱（HNSW 索引需要重构）

