项目实战 # elasticsearch已发布

Elasticsearch Mapping 字段类型详解与性能优化实践

Elasticsearch(简称 ES)是基于 Apache Lucene 构建的分布式搜索与分析引擎,广泛应用于日志检索、业务搜索、数据统计和监控分析。在 ES 中,数据通常以 JSON 文档的形式写入索引,但 JSON 本身只有字符串、数字、布尔值、数组和对象等基本结构,并不能直接表达字段应该如何参与搜索、排序、聚合或地理位置计算...

2025.11.0222 分钟阅读
Elasticsearch Mapping 字段类型详解与性能优化实践
Elasticsearch Mapping 字段类型详解与性能优化实践 · Cover

Elasticsearch(简称 ES)是基于 Apache Lucene 构建的分布式搜索与分析引擎,广泛应用于日志检索、业务搜索、数据统计和监控分析。

在 ES 中,数据通常以 JSON 文档的形式写入索引,但 JSON 本身只有字符串、数字、布尔值、数组和对象等基本结构,并不能直接表达字段应该如何参与搜索、排序、聚合或地理位置计算。

在我之前的一个项目中,因为字段类型设置的不合理,字段安排的不正确,导致了 mapping 爆炸的问题,使得性能下降,后续花费了很大功夫去优化这方面的内容。

Mapping 就是解决这个问题的机制。它定义字段的数据类型、分析方式以及相关索引行为。一个字段选择 text 还是 keyword,一个对象选择 object、nested 还是 flattened,不仅会影响查询结果,还会影响索引大小、写入吞吐量、内存占用和查询性能。

因此,理解 Mapping 的关键不只是认识字段类型,而是知道每种类型适合什么场景、有哪些限制,以及应该如何根据业务需求选择。

一、Mapping 的基本结构

创建一个简单的日志索引:

PUT api-logs
{
  "mappings": {
    "properties": {
      "requestId": {
        "type": "keyword"
      },
      "status": {
        "type": "integer"
      },
      "duration": {
        "type": "long"
      },
      "createdAt": {
        "type": "date"
      },
      "message": {
        "type": "text"
      }
    }
  }
}

这里的 properties 用来定义文档中的字段及其类型。

写入文档:

POST api-logs/_doc/1
{
  "requestId": "req-001",
  "status": 500,
  "duration": 126,
  "createdAt": "2026-10-09T08:30:00+08:00",
  "message": "User authentication failed"
}

ES 会根据 Mapping 对字段进行处理,并建立相应的索引结构。

常见操作包括:

GET api-logs/_mapping

查看 Mapping。

GET api-logs/_field_caps?fields=*

查看字段在目标索引中的类型及相关能力,尤其适合排查跨索引查询时字段类型不一致的问题。

需要注意,Mapping 不是关系型数据库中要求每一列都存在的表结构。文档可以缺少某些字段,一个字段也可以包含多个值。不过,普通数组中的值应与字段定义的类型兼容,不能把 Mapping 当成可以任意混装不同类型数据的容器。

二、字符串类型:text、keyword 与其他类型

字符串是最容易出现 Mapping 设计问题的一类数据。

1. text:全文检索

text 用于需要分词的文本,例如文章内容、错误消息、商品描述和用户评论。

{
  "message": {
    "type": "text"
  }
}

写入:

{
  "message": "User authentication failed"
}

经过分析器处理后,文本会被拆分成词项,并建立倒排索引。查询时,ES 可以通过词项查找包含相关内容的文档。

GET api-logs/_search
{
  "query": {
    "match": {
      "message": "authentication failed"
    }
  }
}

text 的主要用途是全文检索。对于普通的精确匹配、排序和聚合,通常不应该直接使用 text 字段。

如果需要中文全文检索,还要考虑分词器是否适合实际语料。默认分析器并不一定符合中文搜索需求。

2. keyword:精确匹配

keyword 适用于不需要全文分词的字符串,例如:

  • 用户 ID、应用 ID、订单号。
  • 请求 ID、Trace ID。
  • 状态、类别、标签。
  • 邮箱、域名和枚举值。
{
  "userId": {
    "type": "keyword"
  },
  "appId": {
    "type": "keyword"
  }
}

查询:

GET api-logs/_search
{
  "query": {
    "term": {
      "userId": "10086"
    }
  }
}

keyword 通常还适用于排序、聚合和去重统计。

一个容易忽略的细节是:数字形式的 ID 不一定应该使用数字类型。如果它只是标识符,不需要数值范围查询或数学计算,使用 keyword 往往更合理。

3. text 和 keyword 同时使用

如果同一个字段既需要全文搜索,又需要精确匹配和聚合,可以使用多字段(Multi-fields)。

PUT applications
{
  "mappings": {
    "properties": {
      "appName": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}

这样就有两种访问方式:

appName           全文检索
appName.keyword   精确匹配、排序、聚合

但多字段也有成本。每增加一种字段映射,就可能增加索引和写入开销,因此只应该为实际需要的查询方式增加子字段。

ignore_above 用于限制 keyword 子字段索引的字符串长度。超过限制的值仍可能保留在 _source 中,但无法通过这个子字段正常匹配或排序。

4. constant_keyword:固定值字段

constant_keyword 适用于某个索引中的所有文档都具有同一个固定值的字段。

例如,一个专门保存生产环境日志的索引:

{
  "environment": {
    "type": "constant_keyword",
    "value": "production"
  }
}

如果索引中的每条文档都属于生产环境,就不必为每条文档重复存储和索引不同的环境标识。

它适合数据集具有明确固定属性的场景,但不适合一个索引中存在多种环境值的情况。

5. wildcard:适合特定的通配符检索

wildcard 是针对特定字符串检索场景设计的类型,尤其适用于较长字符串上的通配符查询,例如日志内容或路径模式匹配。

{
  "logContent": {
    "type": "wildcard"
  }
}

它并不是所有字符串字段的通用替代品。普通精确匹配和聚合通常仍然适合使用 keyword,全文搜索则更适合 text。

如果大量使用 *error* 这样的包含式通配符查询,即使字段类型合适,也仍然可能产生较高的查询成本。

6. match_only_text:降低部分全文字段的存储成本

match_only_text 适用于主要需要全文匹配,但不需要完整相关性评分能力的文本字段。

{
  "message": {
    "type": "match_only_text"
  }
}

它适合某些日志消息和低成本文本检索场景。

但如果业务需要精细的相关性评分、复杂排序或其他完整 text 能力,就不应该直接用它替换 text。

7. search_as_you_type:输入即搜索

如果业务需要用户输入几个字符后就能逐步显示搜索结果,可以考虑 search_as_you_type。

{
  "title": {
    "type": "search_as_you_type"
  }
}

它针对逐步输入的搜索场景提供相应的索引结构,适用于搜索框、标题联想和部分前缀匹配场景。

它并不等同于所有场景下的自动补全。对于预先维护候选词、名称或热门搜索词的场景,还可以评估 completion。

8. completion:搜索建议

completion 用于专门的自动补全场景。

PUT search-suggestions
{
  "mappings": {
    "properties": {
      "suggest": {
        "type": "completion"
      }
    }
  }
}

写入:

POST search-suggestions/_doc/1
{
  "suggest": [
    "Elasticsearch Mapping",
    "Elasticsearch Query"
  ]
}

查询:

POST search-suggestions/_search
{
  "suggest": {
    "title-suggest": {
      "prefix": "Elas",
      "completion": {
        "field": "suggest"
      }
    }
  }
}

completion 和 search_as_you_type 都能用于搜索建议,但实现方式和适用场景不同。选择时需要结合候选词规模、匹配方式、权重和查询延迟进行测试。

9. 其他文本相关类型

类型用途
annotated-text带有特殊标记的文本,常用于实体标注相关场景
pattern_text在支持该类型的版本中,用于特定模式驱动的文本处理场景
token_count保存文本经分析器处理后的词项数量
semantic_text通过推理模型实现文本语义检索的字段类型
semantic支持相应推理能力的多模态语义检索字段

这些类型并不是所有 ES 版本都支持。尤其 semantic_text 和 semantic 涉及推理模型、向量索引和版本或许可要求,使用前必须确认目标集群的实际能力。

三、数值类型

数值类型主要用于范围查询、数值排序、统计和计算。

类型适用场景
byte较小范围的整数
short较小范围的整数
integer状态码、数量、普通整数
long大整数、较大计数值
unsigned_long非负 64 位整数范围内的值
half_float对精度要求较低的浮点数
float常见浮点数
double需要更大数值范围或更高浮点精度的场景
scaled_float通过缩放因子存储的数值

例如:

{
  "status": {
    "type": "integer"
  },
  "duration": {
    "type": "long"
  },
  "price": {
    "type": "scaled_float",
    "scaling_factor": 100
  }
}

scaled_float 可以用缩放因子处理固定小数位的数据,例如价格。

不过,不能只因为字段的实际数值较小,就随意把类型从 long 改成 byte。还需要考虑未来数据范围、聚合、精度以及应用程序的写入行为。

数值字段的类型设计应当由业务语义和数据范围决定,而不是只追求最小的类型。

四、日期和时间类型

1. date

date 是常规日期时间字段,适合日志时间、创建时间、更新时间等。

{
  "createdAt": {
    "type": "date",
    "format": "strict_date_optional_time||epoch_millis"
  }
}

查询某一天的数据:

GET api-logs/_search
{
  "query": {
    "range": {
      "createdAt": {
        "gte": "2026-10-09T00:00:00+08:00",
        "lt": "2026-10-10T00:00:00+08:00"
      }
    }
  }
}

这里使用左闭右开的时间范围,方便连续时间段的拼接。

2. date_nanos

date_nanos 支持纳秒精度的日期时间。

{
  "eventTime": {
    "type": "date_nanos"
  }
}

它适合确实需要更高时间精度的事件数据,例如高频事件和精细排序场景。

如果业务只精确到毫秒,通常没有必要为了“更精确”而全部使用 date_nanos。还需要考虑目标版本、查询能力和数据存储成本。

五、布尔、二进制与专用字符串类型

1. boolean

用于布尔值:

{
  "enabled": {
    "type": "boolean"
  }
}

适合启用状态、是否成功、是否删除等字段。

2. binary

用于 Base64 编码的二进制数据:

{
  "payload": {
    "type": "binary"
  }
}

binary 并不是用来替代普通文件存储或对象存储的通用方案。它适合特定的二进制数据需求,通常不具备普通字符串字段那样的检索能力。

3. ip

用于 IPv4 和 IPv6 地址:

{
  "clientIp": {
    "type": "ip"
  }
}

相比将 IP 存成普通字符串,ip 更适合 IP 地址相关的查询和范围筛选。

4. version

在支持该类型的版本中,version 用于软件版本号:

{
  "softwareVersion": {
    "type": "version"
  }
}

它适合需要按照软件版本语义进行比较的场景。不要把版本号简单当作浮点数处理,例如 1.10 与 1.9 的比较并不符合通常的软件版本语义。

5. murmur3

murmur3 用于计算并保存值的 MurmurHash3 哈希,通常与特定的哈希比较或去重类场景有关。

它不是普通业务字段的默认选择,也不应仅为了让查询更快就随意添加。需要先确认目标版本支持情况及实际收益。

六、对象类型:object、nested、flattened

这是 Mapping 中最容易影响查询正确性和字段数量的部分。

假设一个用户拥有多个应用角色:

{
  "userId": "10086",
  "roles": [
    {
      "name": "admin",
      "level": 1
    },
    {
      "name": "viewer",
      "level": 5
    }
  ]
}

如果需要按照 name 和 level 的组合进行查询,就必须考虑对象数组的语义。

1. object:普通 JSON 对象

普通对象通常是默认的对象映射方式。

{
  "profile": {
    "type": "object",
    "properties": {
      "name": {
        "type": "keyword"
      },
      "age": {
        "type": "integer"
      }
    }
  }
}

查询时可以使用点号路径:

{
  "query": {
    "term": {
      "profile.name": "Alice"
    }
  }
}

但普通 object 有一个重要限制:当字段是对象数组时,数组中每个对象的字段关系不会被独立保留用于查询。

例如:

{
  "roles": [
    {
      "name": "admin",
      "level": 1
    },
    {
      "name": "viewer",
      "level": 5
    }
  ]
}

如果 roles 是普通 object,查询 roles.name = admin 且 roles.level = 5 时,可能匹配到这条文档,因为查询条件可以分别命中不同数组元素中的值。

如果业务要求两个条件必须来自同一个角色对象,就不应该直接使用普通 object。

2. nested:保留对象数组的关联关系

nested 用于需要独立保留对象数组内部字段关系的场景。

PUT users
{
  "mappings": {
    "properties": {
      "userId": {
        "type": "keyword"
      },
      "roles": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "keyword"
          },
          "level": {
            "type": "integer"
          }
        }
      }
    }
  }
}

查询某个角色同时满足两个条件:

GET users/_search
{
  "query": {
    "nested": {
      "path": "roles",
      "query": {
        "bool": {
          "filter": [
            {
              "term": {
                "roles.name": "admin"
              }
            },
            {
              "term": {
                "roles.level": 5
              }
            }
          ]
        }
      }
    }
  }
}

由于原始数据中 admin 对应 level = 1,这条文档不会因为另一个角色的 level = 5 而满足查询。

nested 的优势是保留关联关系,代价则是索引和查询处理更复杂。每个嵌套对象会以独立的隐藏文档参与 Lucene 索引,因此大量嵌套对象会增加索引规模和查询成本。

如果对象数组只是用于简单筛选,不需要保证字段必须属于同一个数组元素,那么 nested 可能是不必要的复杂度。

3. flattened:解决动态对象字段膨胀

假设业务中存在一个不固定的扩展属性对象:

{
  "userId": "10086",
  "attributes": {
    "department": "engineering",
    "region": "beijing",
    "customFieldA": "valueA",
    "customFieldB": "valueB"
  }
}

如果每个扩展属性都被映射为独立字段,字段数量可能随着业务发展持续增长,最终造成 Mapping 膨胀。

flattened 可以把整个对象作为一个字段处理:

PUT users
{
  "mappings": {
    "properties": {
      "userId": {
        "type": "keyword"
      },
      "attributes": {
        "type": "flattened"
      }
    }
  }
}

查询:

GET users/_search
{
  "query": {
    "term": {
      "attributes.department": "engineering"
    }
  }
}

flattened 适合键名和结构不固定、只需要基本键值检索的对象。

但是它的子字段通常按照字符串值处理,不具备普通数值字段的完整语义。例如,对数值进行范围查询时,不应假设它会像 integer 一样执行数值比较;排序也可能按字符串字典序进行。

因此,flattened 不是所有 JSON 对象的替代品,而是对动态键值对象的一种折中方案。

4. 三者如何选择

对比项objectnestedflattened
主要用途普通对象对象数组及关联查询动态键值对象
子字段独立 Mapping是是默认整体映射
保留对象数组元素关联否是不提供 nested 语义
数值范围查询由子字段类型决定由子字段类型决定默认按字符串处理
字段膨胀风险动态子字段多时较高动态子字段多时较高通常更低
查询复杂度通常较低通常更高适合基本键值检索

还有一个与 nested 不同的关系类型:join。它用于在同一个索引中建立父子文档关系,例如一条产品文档关联多条评论文档。它并不等价于普通对象数组,且父子查询有额外成本,只有在确实需要独立更新或查询父子实体时才值得考虑。

七、范围类型

如果业务存储的不是一个单独数值,而是一个区间,可以考虑范围类型。

常见类型包括:

类型表示的范围
integer_range整数范围
long_range长整数范围
float_range单精度浮点范围
double_range双精度浮点范围
date_range日期时间范围
ip_rangeIP 地址范围

例如,某个服务支持的有效时间区间:

{
  "validPeriod": {
    "type": "date_range"
  }
}

写入:

POST contracts/_doc/1
{
  "validPeriod": {
    "gte": "2026-01-01",
    "lt": "2027-01-01"
  }
}

范围字段可以用于表示有效期、价格区间、IP 地址段和数值区间等。

它与普通 date 或 long 字段的区别在于:普通字段通常表示一个值,而范围字段表示一个区间。查询时需要根据区间重叠、包含等业务语义选择合适的范围查询方式。

八、地理位置类型

ES 支持地理坐标和几何形状相关的类型。

1. geo_point

geo_point 表示地理坐标点:

{
  "location": {
    "type": "geo_point"
  }
}

例如:

{
  "location": {
    "lat": 40.123,
    "lon": 116.456
  }
}

适用于附近搜索、按距离排序和地理范围筛选。

2. geo_shape

geo_shape 用于表示地理形状,例如多边形、线段和复杂地理区域。

适用于判断坐标是否位于某个区域、区域相交等地理空间查询。

3. point 和 shape

这两个类型用于笛卡尔坐标空间,而不是地球经纬度。

  • point:表示平面上的点。
  • shape:表示平面中的复杂几何形状。

如果业务处理的是地图经纬度,通常优先考虑 geo_point 和 geo_shape;如果是游戏地图、平面图或者自定义坐标空间,则可以评估 point 和 shape。

九、向量与语义搜索类型

随着语义搜索和 RAG 应用越来越普遍,ES 也经常被用于向量检索。

1. dense_vector

dense_vector 用于存储稠密向量,通常是 embedding 模型生成的浮点数数组。

{
  "embedding": {
    "type": "dense_vector",
    "dims": 384
  }
}

这里的维度必须与所用模型输出一致。向量检索可以用于文档语义搜索、相似内容推荐和 RAG 检索。

不同 ES 版本支持的向量索引算法、量化类型、维度限制和查询语法有所不同,不能把较新版本的配置直接套用到 ES 7.x。

2. sparse_vector

sparse_vector 用于稀疏向量表示,适合相应的稀疏语义检索模型。

它与普通数值字段不同,也不应该被当成一个普通的数组字段随意聚合。

3. rank_feature 和 rank_features

这两个类型用于将某些数值特征用于搜索相关性排序。

例如,文章的热度、点击权重或业务评分可能影响搜索结果的排名。与直接把这些字段用于普通排序相比,Rank Feature 查询可以按照专门的评分逻辑影响相关性得分。

4. rank_vectors

在支持该类型的较新版本中,rank_vectors 用于存储每个文档的多个向量,以支持 late-interaction 等重排序场景。它不是普通向量检索的通用替代品。

5. semantic_text 与 semantic

较新版本还提供了更高层的语义字段类型:

  • semantic_text:主要面向文本语义搜索,可以自动处理部分向量生成、分块和索引细节。
  • semantic:面向支持的多模态语义检索场景,可以处理文本之外的内容类型,具体取决于推理端点能力。

这些类型依赖对应版本、推理服务和许可条件。对于传统 ES 7.x 集群,应先确认版本能力,不能因为 Mapping JSON 能写出来就认为集群可以正常执行。

十、聚合指标与直方图类型

对于已经预先计算好的指标数据,ES 提供了一些专门的类型。

1. aggregate_metric_double

用于存储已经聚合的数值指标,例如最小值、最大值、总和以及数量等。它适合上游已经完成部分聚合、希望在 ES 中保存聚合结果的场景。

2. histogram

用于存储直方图数据,通常包括数值样本的分布信息。它适合指标系统将一批数值预聚合后再写入 ES 的场景。

3. exponential_histogram

用于保存指数直方图形式的聚合指标数据,适用于相应的可观测性和指标处理工作流。

4. tdigest

在支持该类型的版本中,用于表示 T-Digest 相关的数据结构,常用于近似分位数等统计场景。

这些类型并不是常规业务文档的默认字段类型。如果每条请求日志只保存一个耗时,通常直接使用 long 即可;只有在数据已经以特定聚合结构产生时,才应该考虑对应的聚合类型。

十一、其他特殊类型

还有一些不属于普通业务字段的类型,了解它们的用途即可。

类型用途
alias为现有字段创建字段别名,方便统一访问
passthrough在支持该功能的版本中,让指定子字段以别名形式暴露
percolator索引 Query DSL 查询,让文档可以匹配预先保存的查询条件
join建立同一索引内的父子文档关系

这些类型通常针对特定架构需求。

例如,percolator 适合“预先保存很多规则,收到新文档后判断哪些规则匹配”的场景,例如规则引擎或订阅匹配。它不是普通全文检索的替代方案。

alias 则可以帮助应用程序解耦实际字段名称与查询使用的名称,但它不能凭空创建新的数据,也不代表可以随意改变底层字段的类型。

十二、数组与多字段不是独立字段类型

有两个概念经常和 Mapping 类型混淆:数组和多字段。

1. 数组

ES 没有独立的 array 字段类型。一个字段可以包含一个值,也可以包含多个值:

{
  "tags": [
    "nodejs",
    "elasticsearch",
    "nestjs"
  ]
}

只需要将 tags 映射为适当的类型:

{
  "tags": {
    "type": "keyword"
  }
}

但如果数组元素是对象,并且需要保证多个字段必须来自同一个数组元素,就要考虑前面介绍的 nested。

2. 多字段

多字段通过 fields 将同一份源数据以多种方式索引:

{
  "name": {
    "type": "text",
    "fields": {
      "keyword": {
        "type": "keyword"
      }
    }
  }
}

多字段本身不是一种独立的 Mapping 类型,而是一种字段设计机制。

十三、Mapping 的性能优化

了解字段类型之后,下一步是根据实际访问模式优化 Mapping。

1. 减少不必要的索引

如果某个字段只用于返回展示,而不需要检索、排序或聚合,可以考虑关闭索引能力。

{
  "userAgent": {
    "type": "keyword",
    "index": false,
    "doc_values": false
  }
}

这种配置适合只需要在 _source 中保留值、不需要通过该字段检索或聚合的场景。

但不要把所有字段的 index 和 doc_values 都关闭。关闭索引会影响查询能力,关闭 doc_values 会影响排序、聚合及部分脚本访问。

2. 避免 Mapping 膨胀

对于动态字段很多的对象,可以考虑:

  • 显式定义固定字段。
  • 对未知字段使用适当的动态模板。
  • 使用 dynamic: false 保留未知字段但不自动创建 Mapping。
  • 在适合的动态键值对象上使用 flattened。
  • 控制允许写入的字段数量和嵌套深度。

如果业务需要严格限制字段结构,可以使用:

{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "requestId": {
        "type": "keyword"
      }
    }
  }
}

strict 会拒绝未知字段,因此必须提前确认数据生产端不会发送合法但未定义的字段。

3. 控制 nested 使用范围

nested 不是免费的。每个嵌套对象都会增加内部文档和索引处理成本。

只在需要保留数组对象之间的字段关联时使用它。如果普通对象查询就能满足需求,没有必要把所有对象都改成 nested。

4. 合理使用 doc_values

doc_values 主要服务于排序、聚合和字段值读取。对于需要聚合的 keyword 和数值字段,通常应保留默认行为。

不要轻易给 text 开启 fielddata 来解决聚合问题。它可能消耗大量 JVM 堆内存。通常应为需要聚合的文本增加 keyword 子字段。

5. 控制高基数聚合

对状态码、应用 ID 等字段聚合通常比较常见,但对每条文档都不同的请求 ID 进行高基数聚合,可能带来较大开销。

应只聚合真正需要统计的维度,并限制返回桶的数量。需要遍历大量唯一值时,可以评估 Composite Aggregation。

6. 优化分片规划

分片数量需要根据数据规模、查询负载、保留周期和节点资源来确定。

分片太少可能限制并行处理,分片太多则会增加集群元数据、内存、文件句柄和调度开销。

GET _cat/indices?v
GET _cat/shards?v
GET _cluster/health

先观察现有分片大小和分布,再决定是否需要调整索引生命周期或重新规划索引。

7. 使用 Bulk 并合理设置 Refresh

批量写入通常比逐条请求更高效:

POST _bulk
{"index":{"_index":"api-logs"}}
{"requestId":"req-001","status":200}
{"index":{"_index":"api-logs"}}
{"requestId":"req-002","status":500}

批次大小和并发数需要通过压测确定,不能一味增大。

如果业务允许更长的搜索可见延迟,可以适当延长 refresh_interval,减少 Refresh 频率。对于要求近实时查询的业务,则需要保留相应的延迟目标。

8. 避免深分页

from + size 的方式适合较浅的分页。对于大量数据的深分页,可以考虑 search_after,并在需要稳定快照时结合 Point in Time(PIT)。

同时应控制每次返回的文档数量,避免无意义地读取大量字段。

9. 监控集群资源

常用检查接口:

GET _nodes/stats/jvm,process,fs,indices,thread_pool
GET _cat/nodes?v
GET _cat/indices?v
GET _cat/shards?v

结合查询延迟、写入吞吐量、CPU、堆内存、磁盘 I/O、分片分布和线程池拒绝情况定位瓶颈。

不要在没有证据的情况下直接增加节点或修改 JVM 参数。先判断问题来自 Mapping、查询、分片、写入链路还是硬件资源,再进行针对性优化。

十四、如何选择字段类型

在实际项目中,可以按以下思路快速选择:

业务需求优先考虑
全文检索text
精确匹配、排序和聚合keyword
固定值字段constant_keyword
通配符模式检索wildcard
搜索框逐字输入search_as_you_type
候选词自动补全completion
数值范围和统计合适的数值类型
日期时间查询date / date_nanos
IP 地址检索ip
普通 JSON 对象object
需要保留关联关系的对象数组nested
动态键值对象flattened
父子文档关系join
地理坐标点geo_point
地理区域和复杂形状geo_shape
平面坐标点和形状point / shape
稠密向量搜索dense_vector
稀疏向量搜索sparse_vector
文本语义检索semantic_text 或适合的向量类型
预先聚合的指标对应聚合指标类型
预先保存的查询规则percolator

最后要特别强调:字段类型列表会随着 Elasticsearch 版本演进而变化。 ES 7.x、8.x 和 9.x 并不能简单地视为完全相同的 Mapping 能力集合。升级或迁移前,应根据目标版本的官方文档确认具体类型是否可用、参数是否变化,以及索引是否需要重新创建。

好的 Mapping 设计并不是把所有类型都用一遍,而是让字段类型与业务语义、查询方式和数据规模保持一致。选对类型是基础,控制字段数量、查询成本和分片规模,才是让 Elasticsearch 长期稳定运行的关键。

#elasticsearch
ZZC

ZZC

写代码,也写一些生活。