Mapping 决定数据如何索引

Mapping 类似搜索索引的数据结构定义,它决定字段类型、是否分词、能否聚合以及采用什么分析器。生产环境建议显式维护 Mapping,避免动态映射把日期、数字或标识符识别成错误类型。

text 与 keyword

text 字段会经过分析器分词,适合标题、正文等全文检索。keyword 保留完整值,适合状态、标签、订单号、排序和聚合。

{
  "mappings": {
    "properties": {
      "title": { "type": "text", "analyzer": "standard" },
      "status": { "type": "keyword" },
      "created_at": { "type": "date" }
    }
  }
}

同一个字符串可以使用 multi-fields 同时建立 textkeyword 子字段,分别服务搜索与聚合,但会增加索引体积。

分析器链路

分析器通常包含字符过滤器、分词器和词元过滤器。写入时的分析方式应与查询意图匹配。中文检索通常需要选择合适的中文分词方案,并维护词典、同义词和版本发布流程。

使用 _analyze API 可以查看文本最终生成哪些词项,这是排查“明明有数据却搜不到”的第一步。

match 与 term

match 会分析查询文本,适合全文字段;term 不分析输入,要求精确匹配索引中的词项,适合 keyword、数字、布尔等字段。

text 字段直接使用 term 查询原始句子通常得不到预期结果,因为原文已经被拆成多个词项。反过来,对订单号使用 match 也可能产生意外分词。

bool 查询

  • must:必须匹配并参与相关性评分。
  • filter:必须匹配但不参与评分,适合状态和时间范围。
  • should:提高匹配文档得分,可配置最少匹配数量。
  • must_not:排除文档。

不需要相关性评分的条件应放入 filter 上下文,语义更准确,也更有利于缓存和执行优化。

Mapping 变更策略

许多字段类型和分析器不能在已有索引上直接修改。标准流程是创建带新 Mapping 的版本化索引,使用 _reindex 或业务双写迁移数据,验证后原子切换别名,最后再清理旧索引。

常见问题

  1. 字段爆炸:动态键生成海量字段,应限制动态映射或改用扁平结构。
  2. 深分页:from + size 越深成本越高,大结果遍历使用 search_after 配合稳定排序。
  3. 聚合内存高:避免直接对超高基数字段做巨型聚合。
  4. Mapping 不一致:索引模板应纳入版本管理,并在写入前创建索引。

核心考点清单

  • Mapping 决定字段如何索引,生产环境应显式管理并纳入版本控制。
  • text 分词用于全文搜索,keyword 保留完整值用于过滤、排序和聚合。
  • match 会分析查询文本,term 直接匹配词项。
  • 不可兼容 Mapping 变更通常通过新索引、Reindex 和别名切换完成。

高频追问与参考回答

追问:为什么不能直接修改字段类型?

已有倒排和列式数据按旧类型编码,直接改变解释会破坏索引。应创建新版本索引并迁移,验证后原子切换别名。

追问:深分页为什么慢?

每个分片都要取出并排序 from + size 条候选,再由协调节点合并。持续遍历应使用带稳定排序的 search_after

机制全景图

下面把「Elasticsearch Mapping、分词与查询选择」从输入到结果压缩成一条可复述的主链路。面试时先用图建立全局坐标,再进入局部实现,能避免只背零散结论。

flowchart LR
    A["识别字段业务语义"]
    A --> B["定义 Mapping 与 Analyzer"]
    B --> C["写入并建立索引结构"]
    C --> D["选择 Query/Filter 上下文"]
    D --> E["聚合排序并返回"]

完整链路:从输入到结果

沿着「识别字段业务语义 → 定义 Mapping 与 Analyzer → 写入并建立索引结构 → 选择 Query/Filter 上下文 → 聚合排序并返回」观察输入、状态与输出,下面每个阶段都对应一个可以在源码、日志或系统表中验证的位置。

1. 识别字段业务语义

字段类型决定可用查询、存储格式和内存结构,动态映射方便但可能把 ID、日期或数值识别错误。

2. 定义 Mapping 与 Analyzer

text/keyword、多字段、normalizer 和 nested 等设计应来自查询模式而不是样例 JSON。

3. 写入并建立索引结构

写入后字段值分别进入倒排、doc values 和 _source;关闭某能力会影响搜索、聚合或回显。

4. 选择 Query/Filter 上下文

Query Context 计算相关性,Filter Context 只判断匹配并更易缓存,精确条件不应无谓评分。

5. 聚合排序并返回

聚合与排序通常依赖 doc values,高基数字段和大 size 会显著增加内存与跨分片合并。

源码与实现定位

入口 阅读重点
MapperService Mapping 合并与字段类型
_field_caps 跨索引字段能力

源码或系统表应按上表顺序追踪:先确认入口实际走到哪条路径,再用运行时数据验证,而不是仅凭类名或配置推测。

参数配置与可复现实验

PUT _index_template/orders
{"template":{"mappings":{"dynamic":"strict","properties":{"order_id":{"type":"keyword"}}}}}

写入数字、前导零、数组对象和未知字段,验证 strict 模板与 nested 语义。

验证步骤与预期结果

1. 固定输入和基线

先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「mapping 冲突」为主基线,记录值应满足「记录稳态基线」;同时保存 字段总数、mapping 冲突数,使后续变化能够回到同一时间轴比较。

2. 从实现入口确认路径

在「MapperService」确认请求确实进入「Mapping 合并与字段类型」对应的实现,再沿「_field_caps」观察「跨索引字段能力」。如果入口路径都未命中,就不应继续调整下游参数,而应先检查调用条件、版本或路由是否与假设一致。

3. 注入本文特有的失败模式

优先复现「字段爆炸触发 mapping limit」,并把单一变量逐级放大,直到「mapping 冲突」越过「超过基线2倍」。随后再分别验证「数组对象误用 object 导致跨对象匹配」和「在 filter 条件使用 script 扫描每文档」,三类故障分开执行,避免多个变量同时变化而无法归因。

4. 执行止损和根因修复

第一轮只应用「核心字段显式 Mapping」,确认它能控制影响范围;第二轮应用「限制 total_fields」,验证核心链路恢复;最后落实「类型变化新索引+alias」,消除同类问题再次出现的条件。每一步都保留变更前后数据,不用“感觉变快了”替代测量。

5. 通过退出条件

实验只有同时满足三项才算通过:「mapping 冲突」回到「记录稳态基线」、「P99」回到「小于业务预算」、「结果差异」回到「0」,并且业务结果差异为零。若性能恢复但结果不一致,仍应视为失败;若指标恢复后很快再次越线,则说明只完成了临时止损,没有消除根因。

量化基线

指标 样例基线/口径 风险线 结论
mapping 冲突 记录稳态基线 超过基线2倍 按实现入口定位
P99 小于业务预算 突破预算 停止扩量
结果差异 0 任意非零 回滚并重建

这些数值是实验口径或示例告警线,不是可复制到所有系统的固定答案;上线阈值应由本系统稳态、峰值和故障演练共同确定。

事故复盘:订单 ID 自动映射成 long 后前导零丢失

测试样本全是数字,动态映射把订单号识别为 long,后续带字母数据写入失败,精确格式也无法保留。通过索引模板显式定义 keyword 并版本化 alias 后,数据契约才稳定。

失败模式 首要证据 第一处置动作
字段爆炸触发 mapping limit 字段总数 核心字段显式 Mapping
数组对象误用 object 导致跨对象匹配 mapping 冲突数 限制 total_fields
在 filter 条件使用 script 扫描每文档 查询 cache 命中 类型变化新索引+alias

发布与回滚检查点

  • 发布前:确认「MapperService」对应实现和上述配置在目标版本仍然有效,并保存「mapping 冲突」基线。
  • 灰度中:同时观察 字段总数、mapping 冲突数、查询 cache 命中;任一指标越过表中风险线,就停止继续扩量。
  • 回滚时:先执行「核心字段显式 Mapping」控制影响,再回退代码或参数;涉及持久状态时必须额外核对结果差异。
  • 发布后:至少覆盖一个完整峰值周期,确认「字段爆炸触发 mapping limit」没有再次出现,才关闭变更观察窗口。

方案对比与选型

方案 更适合的场景 主要收益 代价与边界
显式 Mapping 生产核心索引 类型可控、错误前置 演进需模板与重建
动态模板 字段模式可预测的大量日志 减少手工定义 规则优先级错误会污染索引
runtime field 临时派生或历史兼容 无需立即重建 查询时计算成本高

选型至少带上 文档规模、分片数、字段基数、查询并发和写入速率,并用上面的量化基线验证;未知数据应明确为待测假设。

设计边界与工程取舍

Mapping 是写入前的物理设计,许多字段类型不能原地改变;重要索引应通过模板、版本索引和 alias 切换演进。

工程落地遵循:先设计 Mapping 与分片,再优化查询;任何调优都要控制扫描与内存放大。回答时直接引用「MapperService」、配置实验和事故数据,比复述固定模板更有说服力。