跳至主要内容

Elasticsearch搜索条件分词机制详解

**核心原则:Elasticsearch是否会分词取决于使用的查询类型和字段映射配置**。这与传统数据库完全不同,是理解ES搜索行为的关键。


## 一、会分词的查询类型


### 1. `match` 查询(最常用)

```json

{

  "query": {

    "match": {

      "content": "中文分词测试"

    }

  }

}

```

- **会分词**:使用字段映射中定义的`search_analyzer`(或默认`analyzer`)

- 中文示例:`"content": "苹果手机"` 可能被分成 `["苹果", "手机"]`

- 默认使用`OR`操作符,匹配任一分词即可

- 可设置`"operator": "AND"`要求匹配所有分词


### 2. `match_phrase` 查询

```json

{

  "query": {

    "match_phrase": {

      "content": " Elasticsearch教程 "

    }

  }

}

```

- **会分词**,但要求分词后的词语保持原始顺序

- 保留词语间的位置信息,适合短语精确匹配

- 示例:`"quick brown fox"` 会分词为 `["quick", "brown", "fox"]` 并要求按此顺序出现


### 3. `query_string` / `simple_query_string`

```json

{

  "query": {

    "query_string": {

      "query": "Java AND Python",

      "fields": ["title", "content"]

    }

  }

}

```

- **会分词**,且支持类似SQL的查询语法

- 默认使用`standard`分析器分词

- 注意:特殊字符需要转义,可能导致意外行为(新手慎用)


## 二、不会分词的查询类型


### 1. `term` / `terms` 查询(最常用)

```json

{

  "query": {

    "term": {

      "status": "published" 

    }

  }

}

```

- **不分词**:将搜索条件视为完整精确值

- 适用于:关键字字段(`.keyword`)、ID、状态码等不需要分词的字段

- **重要陷阱**:对text类型字段直接使用term查询通常得不到结果!


### 2. `range` / `exists` 等结构化查询

- 日期范围、数值范围等查询不会进行文本分词

- 基于字段的原始值进行比较


## 三、关键机制解析


### 1. 字段映射决定分词行为

```json

{

  "mappings": {

    "properties": {

      "title": {

        "type": "text",

        "analyzer": "ik_max_word",        // 索引时分词器

        "search_analyzer": "ik_smart"     // 搜索时分词器

      },

      "title.keyword": {

        "type": "keyword"

      }

    }

  }

}

```

- **text类型字段**:存储时被分词,搜索时根据查询类型决定是否再次分词

- **keyword类型字段**:始终不分词,用于精确匹配、聚合、排序

- **最佳实践**:对需要同时支持全文搜索和精确匹配的字段,使用多字段(multi-field)设计


### 2. 分词过程可视化

使用`_analyze` API查看实际分词效果:

```json

GET /_analyze

{

  "analyzer": "ik_max_word",

  "text": "中华人民共和国国歌"

}

```

返回结果:

```json

{

  "tokens": [

    {"token": "中华人民共和国", "position": 0},

    {"token": "中华人民", "position": 1},

    {"token": "中华", "position": 2},

    {"token": "华人", "position": 3},

    ...

  ]

}

```


## 四、常见误区与解决方案


### 误区1:对text字段使用term查询

```json

// 错误示例(通常返回空结果):

{

  "query": {

    "term": {

      "title": "Elasticsearch 入门教程" 

    }

  }

}

```

**正确做法**:

- 全文搜索用`match`:`"match": { "title": "Elasticsearch 入门教程" }`

- 精确匹配用`.keyword`:`"term": { "title.keyword": "完整标题文本" }`


### 误区2:忽略大小写问题

- 默认`standard`分析器会转为小写:`"Apple" → "apple"`

- 中文分词器通常保留原始大小写,需注意大小写敏感性


### 误区3:未配置中文分词器

- 默认英文分词器对中文按单字切分:`"中文测试" → ["中","文","测","试"]`

- **解决方案**:安装IK、jieba等中文分词插件


## 五、最佳实践建议


1. **明确字段用途**:

   - 全文搜索 → `text` + 合适的分词器

   - 精确值/聚合 → `keyword`

   - 邮箱/URL等 → 使用`normalizer`进行大小写标准化


2. **查询类型选择原则**:

   - 用户输入的自然语言 → `match` 或 `multi_match`

   - 精确值过滤(状态、类型)→ `term`

   - 标签/分类等多值匹配 → `terms`


3. **生产环境必备**:

   ```json

   // 标准字段设计示例

   "product_name": {

     "type": "text",

     "analyzer": "ik_max_word",

     "search_analyzer": "ik_smart",

     "fields": {

       "keyword": {

         "type": "keyword",

         "ignore_above": 256

       }

     }

   }

   ```


4. **调试技巧**:

   - 使用`"explain": true`查看评分细节

   - 用`_validate` API检查查询是否被正确解析

   - 通过Kibana的"Dev Tools"实时测试分词效果


> **关键总结**:Elasticsearch不是"输入什么搜什么"的简单系统。理解**索引时分析**和**查询时分析**的双重过程,是掌握ES搜索行为的核心。永远先检查字段mapping,再选择合适的查询类型。

此博客中的热门博文

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 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。