Elasticsearch 映射(Mapping)全解析
一、映射核心定义
官方定义翻译与优化
映射(Mapping) 是定义文档及其包含的字段如何被存储、索引和检索的过程,相当于关系型数据库中的「表结构定义」,但具备更强的灵活性和动态扩展能力。
每一篇ES文档都是字段的集合,每个字段都有对应的数据类型。映射的核心载体是映射定义(mapping definition),它包含两部分核心内容:
-
文档相关的字段列表,以及每个字段的数据类型、索引规则、分词方式等配置;
-
元数据字段(metadata fields),比如
_source字段,用于自定义文档关联元数据的处理规则。
1. 映射的核心本质
映射的核心作用,是告诉ES「如何理解你的数据」:
-
决定一个字符串字段是用于全文检索(text类型) 还是精确匹配/聚合排序(keyword类型);
-
决定数字、日期、地理坐标等字段用什么格式存储和计算;
-
决定哪些字段可以被索引、哪些字段可以被排序、哪些字段会被忽略;
-
控制文本的分词规则、语义向量生成、嵌套对象的解析方式等高级行为。
2. 核心元数据字段全解析
元数据字段是ES为每篇文档自动维护的内置字段,无需用户手动定义,映射中可自定义其行为,9.3.0版本核心元数据字段如下:
| 元数据字段 | 核心作用 | 常用配置说明 |
|---|---|---|
_source |
存储文档写入时的原始JSON内容 | 默认开启;关闭可节省50%左右磁盘空间,但无法做文档更新、重索引、字段重建,生产环境非极端场景不建议关闭 |
_id |
文档的唯一标识 | 可手动指定,也可由ES自动生成;无法修改,只能通过重写文档替换 |
_index |
文档所属的索引名称 | 只读,由ES自动维护,用于跨索引查询时筛选 |
_routing |
文档路由到分片的路由值 | 默认用_id计算;自定义路由可将相关文档放到同一个分片,提升联合查询性能 |
_version |
文档的版本号 | 每次写入/更新自动+1,用于乐观锁控制并发写入 |
_seq_no |
文档的序列号 | ES集群级别的单调递增编号,用于并发控制和数据同步 |
_primary_term |
主分片任期号 | 配合_seq_no实现乐观锁,避免脑裂导致的脏数据 |
3. 映射的两种核心设计思路
根据数据的确定性,ES提供两种映射定义方式,可组合使用:
-
动态映射(Dynamic Mapping):ES自动检测文档字段的数据类型,自动创建映射,适合快速上手、字段不确定的场景;
-
显式映射(Explicit Mapping):用户手动为每个字段指定数据类型和配置,生产环境强推荐,完全掌控数据的索引行为。
原文补充说明:7.0.0版本之前,映射定义包含「类型名称(type name)」,一个索引可包含多个文档类型;7.0.0及之后版本彻底移除了映射类型,不再接受默认映射,一个索引仅支持一个固定的_doc类型。
【深度分析】移除的核心原因:ES底层存储中,同一个索引里不同类型的同名字段,必须使用完全相同的映射,否则会出现倒排索引冲突,「多类型」设计无实际意义,还会增加用户理解成本,因此被彻底废弃。9.3.0版本完全不支持映射类型,所有操作无需指定type名称。
二、动态映射(Dynamic Mapping)
当你使用动态映射时,ES会自动检测文档中字段的数据类型,并为你自动创建对应的映射。如果后续写入的新文档包含之前没有的字段,ES会自动将这些字段添加到映射中;你可以给顶层映射、内部对象字段、嵌套字段新增动态字段。
动态映射能帮你快速上手ES,但由于其自动类型推断的特性,可能会出现不符合业务预期的映射结果,导致特定场景下的性能和功能问题。
你可以使用动态模板(Dynamic Templates),为动态添加的字段,根据匹配规则自定义映射规则。
1. 动态映射的核心类型推断规则(9.3.0版本,踩坑高频点)
ES不会凭空推断类型,有严格的内置推断规则,默认规则如下(优先级从高到低):
| 写入的字段值 | 默认推断的字段类型 | 关键补充说明 |
|---|---|---|
true / false |
boolean |
仅严格的布尔值会被推断,字符串"true"/"false"不会 |
| 带小数点的数字(如123.45) | float |
不会推断为double,除非显式指定 |
| 整数(如123) | long |
不会推断为integer/short,避免数值溢出 |
| 符合日期格式的字符串 | date |
默认匹配格式:`strict_date_optional_time |
| 普通字符串(无法转成日期/数字) | text + keyword 多字段 |
这是ES最常用的默认规则:主字段为text类型(用于全文检索),同时自动创建一个.keyword子字段(keyword类型,用于聚合、排序、精确匹配) |
嵌套JSON对象({}) |
object |
自动递归解析嵌套对象里的字段,支持无限层级(受嵌套深度限制) |
数组([]) |
按数组内第一个非空元素的类型推断 | ES数组不要求元素类型完全一致,但会按第一个元素推断类型,不匹配的会写入失败(除非开启ignore_malformed) |
| 地理坐标/地理形状 | 无自动推断 | 必须显式指定geo_point/geo_shape类型,动态映射不会自动识别 |
| 向量数据 | 无自动推断 | 必须显式指定dense_vector类型,用于语义搜索 |
2. 动态映射的核心开关
动态映射的行为可通过dynamic参数控制,可配置在索引级别、单个object字段级别,可选值如下:
dynamic参数值 |
行为说明 | 适用场景 |
|---|---|---|
true |
默认值,开启动态映射,自动添加新字段到映射 | 测试环境、字段完全不确定的场景 |
false |
关闭动态映射,新字段不会被添加到映射,但会被存储到 _source 中 |
不需要检索新字段,仅需要存储原始数据的场景 |
strict |
严格模式,遇到未知字段直接拒绝文档写入,抛出异常 | 生产环境强推荐,严格控制字段结构,避免映射爆炸 |
3. 动态模板(Dynamic Templates)全解析
动态模板是动态映射的进阶能力,可自定义「动态添加的字段」的映射规则,解决默认动态映射不符合业务预期的问题。
核心匹配规则
每个动态模板包含2个核心部分:
-
匹配规则 :支持按字段名(
match/unmatch)、字段路径(path_match/path_unmatch)、动态推断的类型(match_mapping_type)匹配; -
映射配置:匹配成功后,应用到该字段的映射规则。
实操示例(9.3.0可直接复制)
HTTP
# 创建索引,配置动态模板
PUT /my-dynamic-index
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keyword": {
"match_mapping_type": "string",
"match": "*_id",
"mapping": {
"type": "keyword",
"ignore_above": 256
}
}
},
{
"full_text_fields": {
"match_mapping_type": "string",
"unmatch": "*_id",
"mapping": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 1024
}
}
}
}
},
{
"long_to_integer": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
}
]
}
}
模板说明
-
第一个模板:所有以
_id结尾的字符串字段,直接映射为keyword类型,不创建全文索引; -
第二个模板:其他所有字符串字段,映射为
text类型(使用ik分词器),同时带keyword子字段; -
第三个模板:所有动态推断为
long的整数字段,自动映射为integer类型,节省存储空间。
三、显式映射(Explicit Mapping)
官方内容翻译与优化
显式映射,是指你手动为每个字段指定数据类型和映射配置的方式,是生产环境的推荐方案。通过显式映射,你可以完全掌控数据的索引方式,完全贴合业务的使用场景。
自定义显式映射可以实现以下能力:
-
定义哪些字符串字段应该作为全文本字段处理;
-
定义哪些文本字段可以通过语义搜索机器学习模型自动向量化;
-
定义哪些字段是数字、日期、地理坐标类型;
-
使用动态映射无法自动识别的数据类型(如
geo_point、geo_shape、dense_vector); -
自定义日期格式,包括特殊的自定义日期格式;
-
创建自定义规则,控制动态添加字段的映射;
-
为字段优化前缀匹配、模糊匹配能力;
-
配置针对特定语言的文本分词器。
实用技巧:为了适配不同的使用目的,通常会把同一个字段用不同的方式索引(多字段multi-fields)。比如,一个字符串字段可以同时映射为text类型(用于全文检索)和keyword类型(用于排序和聚合);也可以为用户输入的字符串字段,配置多个不同语言的分词器。
1. 显式映射的核心优势(生产环境为什么必须用)
-
避免类型推断错误:比如日期字符串被推断为普通字符串、数字字符串被推断为文本,导致无法按时间/数字范围查询;
-
控制存储成本:关闭不需要的索引、doc_values,避免不必要的多字段创建,大幅节省磁盘空间;
-
优化查询性能:为字段选择最合适的数据类型、分词器、索引规则,查询性能比动态映射高2-5倍;
-
规避功能限制:地理类型、向量类型、嵌套类型等高级特性,必须通过显式映射定义,动态映射无法支持;
-
严格管控数据结构 :配合
dynamic: strict,避免非法字段写入,保障数据一致性。
2. 多字段(Multi-fields)全解析
多字段是ES映射的核心设计,允许为同一个原始字段值,创建多个不同类型、不同配置的子字段,适配不同的查询场景,无需冗余存储原始数据。
核心适用场景
-
字符串字段同时支持全文检索+聚合排序:主字段
text用于全文搜索,子字段keyword用于聚合、排序、精确匹配(ES默认动态映射就是这个规则); -
同一个文本字段使用多个分词器:比如一个中文文本,同时用ik分词器做中文检索、用拼音分词器做拼音检索、用英文分词器做英文检索;
-
同一个数字字段同时支持范围查询+聚合:主字段
integer用于范围查询,子字段keyword用于精确匹配和terms聚合。
实操示例
HTTP
PUT /my-explicit-index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 1024
},
"pinyin": {
"type": "text",
"analyzer": "pinyin_analyzer"
}
}
},
"price": {
"type": "float",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
3. 显式映射的核心数据类型选型指南
| 数据类型大类 | 常用子类型 | 适用场景 | 选型避坑 |
|---|---|---|---|
| 字符串类型 | text |
全文检索场景,如文章标题、内容、日志详情 | 不要给不需要全文检索的字段用text,会浪费大量存储空间和索引性能 |
keyword |
精确匹配、聚合、排序场景,如ID、状态码、分类、标签 | 合理设置ignore_above,避免超长字符串占用资源,默认256 |
|
| 数字类型 | integer/long |
整数场景,如数量、状态码、ID | 优先用最小范围的类型,比如能用short就不用integer,能用integer就不用long,节省存储 |
float/double/scaled_float |
小数场景,如价格、金额、指标 | 金额场景优先用scaled_float,避免浮点精度丢失 |
|
| 日期类型 | date |
时间戳、日期场景,支持毫秒/秒级时间戳、格式化日期 | 显式指定日期格式,避免动态映射推断错误,不要用字符串存储日期 |
date_nanos |
纳秒级精度的日期场景,如链路追踪日志 | 非必要不使用,存储占用更高,查询性能略低 | |
| 布尔类型 | boolean |
二值场景,如是否删除、是否生效 | 不要用字符串/数字存储布尔值,浪费空间且影响查询性能 |
| 地理类型 | geo_point |
经纬度坐标场景,如位置检索、范围查询、距离排序 | 必须显式指定,动态映射无法识别,不要用两个数字字段存储经纬度 |
geo_shape |
复杂地理形状场景,如多边形、圆形、线路 | 仅用于复杂地理空间查询,普通经纬度用geo_point即可 |
|
| 嵌套类型 | object |
普通嵌套JSON对象,无关联查询需求 | 无法对嵌套对象的多个字段做联合查询,适合简单嵌套场景 |
nested |
嵌套对象需要做联合查询的场景,如订单商品列表 | 严格控制数量,默认单索引最多50个nested字段,避免性能问题 |
|
| 向量类型 | dense_vector |
语义搜索、向量检索场景,存储文本/图片的向量 embedding | 9.3.0版本支持多种向量相似度算法,必须显式指定维度和相似度类型 |
| 扁平类型 | flattened |
动态JSON对象,避免映射爆炸,如日志的扩展字段 | 把整个JSON对象映射成单个字段,仅支持精确匹配,不支持全文检索,完美解决动态字段的映射爆炸问题 |
四、运行时字段(Runtime Fields)
使用运行时字段,可以无需重索引即可修改数据结构。你可以将运行时字段与索引字段结合使用,平衡资源占用和查询性能:使用运行时字段可以让你的索引体积更小,但会带来一定的查询性能损耗。
你可以在搜索请求中直接定义运行时字段,用于:
-
试验不同的映射方案,无需修改索引结构;
-
修复索引映射中的错误,在查询时覆盖映射中的字段值。
1. 运行时字段核心原理
运行时字段是查询时动态计算的字段,不会写入索引、不占用磁盘空间,ES在执行查询时,通过你定义的Painless脚本实时计算字段值,再执行过滤、聚合、排序等操作,相当于SQL中的「计算列」。
2. 核心适用场景
-
临时修复映射错误:比如动态映射把日期字段推断成了字符串,无需重索引全量数据,通过运行时字段把字符串转回日期类型,正常做时间范围查询;
-
临时新增分析字段:业务临时需要对数据做统计分析,新增一个计算字段,无需修改索引映射、无需重索引;
-
低频查询字段优化:极少查询的字段,无需写入索引,用运行时字段按需计算,节省90%以上的存储成本;
-
敏感字段脱敏 :查询时对敏感字段做脱敏处理,比如手机号、身份证号,原始数据存储在
_source中,运行时字段返回脱敏后的值,避免原始数据泄露。
3. 性能权衡(必须重点关注)
| 维度 | 索引字段 | 运行时字段 |
|---|---|---|
| 存储占用 | 占用磁盘空间,取决于字段类型和索引配置 | 完全不占用磁盘空间,零存储成本 |
| 写入性能 | 写入时需要构建索引、doc values,有一定性能损耗 | 写入时无任何额外开销,写入性能拉满 |
| 查询性能 | 提前构建索引,查询性能极高,支持全量查询能力 | 查询时需要全量扫描文档,执行脚本计算,性能比索引字段低2-10倍,取决于脚本复杂度和数据量 |
| 功能限制 | 支持全文检索、聚合、排序、过滤所有能力 | 不支持全文检索,仅支持keyword、数字、日期等简单类型的过滤、聚合、排序 |
| 核心原则:高频查询、大结果集的字段,必须用索引字段;低频查询、临时分析、小数据量的场景,适合用运行时字段。 |
4. 两种定义方式(实操示例)
方式1:在索引映射中定义运行时字段(永久生效)
HTTP
# 给索引添加运行时字段,无需重索引
PUT /my-index/_mapping
{
"runtime": {
"order_amount": {
"type": "double",
"script": {
"source": "emit(doc['price'].value * doc['quantity'].value)"
}
},
"mobile_desensitized": {
"type": "keyword",
"script": {
"source": "if(doc['mobile'].size()!=0){emit(doc['mobile'].value.substring(0,3)+'****'+doc['mobile'].value.substring(7,11))}"
}
}
}
}
# 直接查询运行时字段,和普通字段用法完全一致
GET /my-index/_search
{
"query": {
"range": {
"order_amount": {
"gte": 100
}
}
},
"fields": ["order_amount", "mobile_desensitized"]
}
方式2:在查询请求中临时定义(单次查询生效)
HTTP
GET /my-index/_search
{
"runtime_mappings": {
"day_of_week": {
"type": "keyword",
"script": {
"source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.CHINA))"
}
}
},
"aggs": {
"week_stats": {
"terms": {
"field": "day_of_week"
}
}
}
}
五、映射的管理与更新规则
显式映射应该在索引创建时,为你提前已知的字段完成定义;随着业务数据的变化,也可以随时给映射添加新的字段。
使用更新映射API(Update Mapping API) 可以更新已有的映射。
核心规则:绝大多数情况下,已经完成映射的字段,无法修改其映射配置,这类修改需要重索引才能生效。
仅以下场景可以更新映射,无需重索引:
-
随时可以给已有映射新增字段(动态/显式均可);
-
可以给已有字段新增多字段;
补充说明:映射更新前写入的文档,不会自动生成新多字段的值,只有更新/重索引后才会生效;映射更新后写入的新文档,会自动生成新多字段的值。
- 部分特定数据类型的字段,可更新部分映射参数。
可更新vs不可更新的完整边界
1. 无需重索引,可直接更新的映射操作
| 可更新操作 | 详细说明 | 注意事项 |
|---|---|---|
| 新增字段 | 给顶层、嵌套对象、nested字段新增显式字段 | 新增字段对已有文档无影响,已有文档的该字段值为null,新文档会按新映射写入 |
| 给已有字段新增多字段 | 给已存在的字段添加新的子字段 | 已有文档不会自动生成子字段的值,必须更新文档/重索引才会生效 |
更新ignore_above参数 |
修改keyword字段的最大长度限制 | 新配置仅对新写入的文档生效,已有文档的超出长度的字段,不会被重新索引 |
更新ignore_malformed参数 |
修改是否忽略格式错误的字段 | 新配置仅对新写入的文档生效,已有文档的格式错误字段不会被处理 |
更新dynamic参数 |
修改动态映射的开关(true/false/strict) | 立即生效,对所有新写入的文档生效 |
| 新增/修改运行时字段 | 新增、修改、删除运行时字段 | 立即生效,无需重索引,所有查询都会使用最新的脚本逻辑 |
更新meta参数 |
修改映射的元数据备注信息 | 无业务影响,仅用于备注说明 |
2. 必须重索引,才能生效的映射修改(绝对不能直接改)
| 不可直接更新的操作 | 核心原因 | 正确处理方式 |
|---|---|---|
| 修改已有字段的数据类型 | ES的倒排索引、doc values都是写入时按字段类型构建的,修改类型后,已有数据的索引结构完全不匹配,会导致查询错误、数据丢失 | 新建索引,定义正确的映射,用reindex API把旧索引数据全量重索引到新索引 |
| 修改已有字段的分词器(analyzer) | 文本的倒排索引是写入时按分词器生成的,修改分词器后,已有数据的分词结果不会更新,全文检索会出现匹配错误 | 重索引,或给字段新增多字段,配置新的分词器 |
修改已有字段的doc_values/fielddata/store参数 |
这些参数控制字段的存储结构,写入时就已确定,修改后已有数据不会重新构建 | 重索引 |
给已有字段新增/修改copy_to参数 |
copy_to是写入时把字段值复制到目标字段,已有文档不会自动复制 |
重索引 |
修改已有字段的index参数(开启/关闭索引) |
控制字段是否被索引,写入时已确定,修改后已有数据不会变更 | 重索引 |
修改日期字段的format参数 |
日期格式是写入时解析的,修改后已有日期数据不会重新解析 | 重索引,或用运行时字段临时兼容 |
3. 更新映射的实操示例(9.3.0可直接执行)
HTTP
# 1. 给已有索引新增字段
PUT /my-index/_mapping
{
"properties": {
"new_field": {
"type": "keyword",
"ignore_above": 256
},
"new_nested_field": {
"type": "nested",
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "keyword"
}
}
}
}
}
# 2. 给已有字段新增多字段
PUT /my-index/_mapping
{
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"pinyin": {
"type": "text",
"analyzer": "pinyin_analyzer"
}
}
}
}
}
# 3. 修改dynamic参数为严格模式
PUT /my-index/_mapping
{
"dynamic": "strict"
}
六、映射爆炸(Mapping Explosion)防控
在一个索引中定义过多的字段,会导致映射爆炸,进而引发OOM内存溢出、集群不可用等严重问题,且故障恢复难度极大。
最常见的场景是:开启动态映射后,每一篇新写入的文档都引入新的字段(比如日志中的动态JSON字段),每一个新字段都会被添加到索引映射中,随着字段数量增长,最终引发映射爆炸。
你可以使用映射限制配置(mapping limit settings),限制手动/动态创建的字段映射数量,避免文档引发映射爆炸。
完整防控方案
1. 映射爆炸的根因
ES集群的所有索引的映射元数据,都会加载到集群所有节点的JVM堆内存中,字段数量越多,元数据体积越大,会导致:
-
JVM堆内存占用持续升高,最终触发OOM,节点宕机;
-
集群元数据同步压力剧增,集群状态变为黄色/红色,元数据更新超时;
-
索引、写入、查询性能急剧下降,甚至完全不可用。
最常见的触发场景:
-
日志场景中,把动态JSON格式的日志内容直接展开,每个key都变成一个字段,很快就会突破上万字段;
-
开启动态映射,业务代码不规范,每次写入都带新的字段名;
-
多租户场景,每个租户的自定义字段都直接写到索引的顶层,字段数量指数级增长。
2. 9.3.0版本核心防控配置项
| 配置项 | 默认值 | 核心作用 |
|---|---|---|
index.mapping.total_fields.limit |
1000 | 单个索引允许的最大字段数量,最核心的防控阈值 |
index.mapping.depth.limit |
20 | 字段的最大嵌套深度,比如a.b.c.d的深度为4 |
index.mapping.nested_fields.limit |
50 | 单个索引允许的最大nested字段数量 |
index.mapping.nested_objects.limit |
10000 | 单个文档中允许的最大nested对象数量 |
index.mapping.field_name_length.limit |
256 | 单个字段名的最大长度 |
index.mapping.total_fields.ignore_dynamic_beyond_limit |
false | 字段数量超过阈值后,是否忽略新增的动态字段,而不是拒绝写入(logsdb模式默认开启) |
避坑提醒:绝对不要盲目调大index.mapping.total_fields.limit的默认值1000,调大只会延迟故障发生的时间,不会解决根本问题,生产环境单索引字段数建议控制在500以内。 |
3. 生产环境终极防控方案
-
严格关闭动态映射 :索引级别设置
dynamic: strict,只允许提前显式定义的字段写入,从根源上杜绝动态字段增长; -
使用
flattened类型处理动态JSON :对于不确定的动态字段,用flattened类型映射,把整个JSON对象当成一个字段,不会展开成多个独立字段,完美解决映射爆炸; -
合理使用动态模板:对于必须开启的动态映射,用动态模板严格控制字段类型,避免不必要的多字段创建;
-
分索引/分租户隔离:多租户场景,按租户分索引,不要把所有租户的字段都放到同一个索引里;
-
定期监控字段数量:通过ES集群监控,持续跟踪索引的字段数量,提前预警增长异常。
4. flattened类型防控映射爆炸实操示例
HTTP
# 创建索引,用flattened类型处理动态扩展字段
PUT /my-safe-index
{
"mappings": {
"dynamic": "strict",
"properties": {
"@timestamp": {
"type": "date"
},
"host.name": {
"type": "keyword"
},
"log.message": {
"type": "text"
},
# 动态扩展字段,全部映射为flattened类型,不会展开
"ext_fields": {
"type": "flattened"
}
}
}
}
# 写入数据,ext_fields里的任意JSON内容都可以写入,不会新增字段
POST /my-safe-index/_doc
{
"@timestamp": "2026-02-14 18:00:00",
"host.name": "web-server-01",
"log.message": "user login success",
"ext_fields": {
"username": "test",
"login_ip": "192.168.1.100",
"user_agent": "chrome",
"custom_field_1": "value1",
"custom_field_2": "value2"
}
}
# 可正常查询ext_fields里的字段
GET /my-safe-index/_search
{
"query": {
"term": {
"ext_fields.username": "test"
}
}
}
七、9.3.0版本完整映射实操示例
HTTP
# 1. 创建索引,定义完整的显式映射,包含动态模板、防控映射爆炸配置
PUT /my-production-index
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
# 映射爆炸防控配置
"index.mapping.total_fields.limit": 500,
"index.mapping.depth.limit": 10
},
"mappings": {
# 严格模式,禁止未知字段写入
"dynamic": "strict",
# 动态模板,仅允许指定规则的动态字段
"dynamic_templates": [
{
"id_fields_as_keyword": {
"match": "*_id",
"mapping": {
"type": "keyword",
"ignore_above": 256
}
}
}
],
# 运行时字段,预定义计算字段
"runtime": {
"order_total_amount": {
"type": "double",
"script": {
"source": "emit(doc['goods_price'].value * doc['goods_quantity'].value)"
}
}
},
# 显式字段定义
"properties": {
"@timestamp": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"order_id": {
"type": "keyword"
},
"user_id": {
"type": "keyword"
},
"goods_name": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 1024
}
}
},
"goods_price": {
"type": "scaled_float",
"scaling_factor": 100
},
"goods_quantity": {
"type": "integer"
},
"order_status": {
"type": "integer"
},
"pay_time": {
"type": "date"
},
"user_address": {
"type": "geo_point"
},
"goods_ext": {
"type": "flattened"
}
}
}
}
# 2. 验证映射创建成功
GET /my-production-index/_mapping
# 3. 写入测试数据
POST /my-production-index/_doc
{
"@timestamp": "2026-02-14 18:30:00",
"order_id": "ORD202602140001",
"user_id": "U123456",
"goods_name": "华为Mate 60 Pro 手机",
"goods_price": 6999.00,
"goods_quantity": 1,
"order_status": 1,
"pay_time": "2026-02-14 18:31:00",
"user_address": {
"lat": 30.5728,
"lon": 104.0668
},
"goods_ext": {
"color": "雅丹黑",
"storage": "12GB+512GB",
"supplier": "华为官方旗舰店"
}
}
# 4. 查询数据,包含运行时字段
GET /my-production-index/_search
{
"fields": ["*", "order_total_amount"]
}
八、生产环境映射设计最佳实践(9.3.0版本适配)
-
生产环境必须使用显式映射 ,配合
dynamic: strict严格模式,从根源上避免类型错误和映射爆炸; -
最小字段原则 :只给需要检索、聚合的字段创建索引,不需要检索的字段关闭
index,不需要排序聚合的字段关闭doc_values,最大化节省存储; -
字符串字段合理选型 :不需要全文检索的字段,一律用
keyword类型,不要用text;需要全文检索的字段,合理配置分词器,避免不必要的多字段; -
数字类型最小化原则 :能用
short不用integer,能用integer不用long,金额场景优先用scaled_float避免精度丢失; -
动态字段用
flattened类型 :不确定的动态JSON字段,一律用flattened类型,绝对不要直接展开成顶层字段; -
高频查询用索引字段,低频临时分析用运行时字段,平衡存储和性能,不要滥用运行时字段;
-
严格控制字段数量:单索引字段数建议控制在500以内,绝对不要盲目调大字段数量阈值;
-
索引创建时就定义好完整映射,避免后续修改需要重索引,重索引是生产环境的高风险操作;
-
特殊类型必须显式指定:日期、地理、向量、嵌套类型,必须显式定义,不要依赖动态映射;
-
映射变更必须先测试:生产环境修改映射前,必须在测试环境验证,避免映射错误导致写入失败。