Elasticsearch 映射(mapping)

概念

在 Elasticsearch 中,映射(Mapping)定义了索引中字段的类型和属性。它是索引数据结构的基础,类似于传统数据库中的表结构定义。映射不仅定义了字段的类型(如 ​text​、​keyword​、​integer​ 等),还定义了字段的分析器、是否存储、是否索引等属性。映射是 Elasticsearch 中定义索引字段类型和属性的重要概念。通过显式定义映射,你可以控制字段的类型、分析器和其他属性,从而更好地管理和优化索引数据。动态映射则提供了灵活性,使得在没有显式映射的情况下也能插入数据。

映射的基本概念
  1. 字段类型:Elasticsearch 支持多种字段类型,包括文本(​text​)、关键字(​keyword​)、整数(​integer​)、浮点数(​float​)、日期(​date​)、布尔(​boolean​)等。

  2. 分析器:对于文本字段,可以指定分析器(Analyzer),用于在索引和搜索时对文本进行分词和处理。

  3. 多字段:一个字段可以有多个子字段(Multi-fields),每个子字段可以有不同的类型和分析器。

  4. 动态映射:如果没有显式定义映射,Elasticsearch 可以根据插入的数据自动推断字段类型,这称为动态映射(Dynamic Mapping)。

动态映射规则

动态映射(Dynamic Mapping)是 Elasticsearch 中一个非常强大的功能,它允许 Elasticsearch 根据插入的数据自动推断字段类型,从而简化索引的创建和管理。Elasticsearch 使用一组预定义的规则来推断字段类型,这些规则称为动态映射规则。

以下是 Elasticsearch 中一些常见的动态映射规则:

1. 字符串(String)
   - 如果字符串包含日期格式,Elasticsearch 会将其推断为 `date` 类型。
   - 如果字符串包含数字格式,Elasticsearch 会将其推断为 `float` 或 `long` 类型。
   - 否则,Elasticsearch 会将其推断为 `text` 类型,并为其创建一个子字段 `keyword` 类型。

2. 数字(Number)
   - 如果字段值是整数,Elasticsearch 会将其推断为 `long` 类型。
   - 如果字段值是浮点数,Elasticsearch 会将其推断为 `float` 类型。

3. 布尔(Boolean)
   - 如果字段值是 `true` 或 `false`,Elasticsearch 会将其推断为 `boolean` 类型。

4. 对象(Object)
   - 如果字段值是一个 JSON 对象,Elasticsearch 会将其推断为 `object` 类型。

5. 数组(Array)
   - 如果字段值是一个数组,Elasticsearch 会根据数组中的第一个元素类型来推断字段类型。

6. 日期(Date)
   - 如果字段值是一个日期字符串,Elasticsearch 会将其推断为 `date` 类型。

示例

假设你插入以下文档到 Elasticsearch:

PUT /my_index/_doc/1
{
  "name": "John Doe",
  "age": 30,
  "is_active": true,
  "created_at": "2023-10-01T12:00:00Z",
  "tags": ["elasticsearch", "mapping"]
}

Elasticsearch 会根据动态映射规则推断以下字段类型:

  • `name`:`text` 类型,并为其创建一个子字段 `keyword` 类型。
  • `age`:`long` 类型。
  • `is_active`:`boolean` 类型。
  • `created_at`:`date` 类型。
  • `tags`:`keyword` 类型(因为数组中的元素是字符串)。
控制动态映射

你可以通过设置索引的动态映射策略来控制动态映射的行为。以下是一些常见的动态映射策略:

1. **true**:启用动态映射(默认)。
2. **false**:禁用动态映射,忽略新字段。
3. **strict**:如果遇到新字段,抛出异常。

例如,禁用动态映射:

PUT /my_index
{
  "mappings": {
    "dynamic": "false"
  }
}

动态映射规则是 Elasticsearch 自动推断字段类型的一组预定义规则。通过了解这些规则,你可以更好地理解和控制 Elasticsearch 如何处理新字段。动态映射提供了灵活性,使得在没有显式映射的情况下也能插入数据,但有时也需要通过设置动态映射策略来控制其行为。在生产环境中,为了确保数据的结构和类型一致性,以及避免潜在的性能问题和数据不一致,强烈建议显式定义索引的映射。

问题

版本开发中,业务场景需求,增加字段disposalstatus ,升级包中增加了该字段数据模型的SQL语句,由于在升级版本的过程中,升级的时候未先更新数据模型,未先新增字段映射,导致接入数据时,ES采用了动态映射的方式进行了数据的写入,然后再升级数据模型和ES索引mapping更新时,索引中字段已新增,并且字段类型不一致,触发了报错

[2024-08-02T17:37:59,956][DEBUG][o.e.a.a.i.m.p.TransportPutMappingAction] [HOxfa0m] failed to put mappings on indices [[[alert_trace_v0/htU57CoYTQeLgs9Cxo2Mvg]]], type [alert_trace]
java.lang.IllegalArgumentException: mapper [disposalstatus] of different type, current_type [text], merged_type [keyword]
        at org.elasticsearch.index.mapper.FieldMapper.doMerge(FieldMapper.java:354) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.TextFieldMapper.doMerge(TextFieldMapper.java:876) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.FieldMapper.merge(FieldMapper.java:340) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.FieldMapper.merge(FieldMapper.java:52) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.ObjectMapper.doMerge(ObjectMapper.java:487) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.RootObjectMapper.doMerge(RootObjectMapper.java:278) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.ObjectMapper.merge(ObjectMapper.java:457) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.RootObjectMapper.merge(RootObjectMapper.java:273) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.Mapping.merge(Mapping.java:91) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.index.mapper.DocumentMapper.merge(DocumentMapper.java:339) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.metadata.MetaDataMappingService$PutMappingExecutor.applyRequest(MetaDataMappingService.java:273) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.metadata.MetaDataMappingService$PutMappingExecutor.execute(MetaDataMappingService.java:231) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.service.MasterService.executeTasks(MasterService.java:643) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.service.MasterService.calculateTaskOutputs(MasterService.java:270) ~[elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.service.MasterService.runTasks(MasterService.java:200) [elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.service.MasterService$Batcher.run(MasterService.java:135) [elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.service.TaskBatcher.runIfNotProcessed(TaskBatcher.java:150) [elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.cluster.service.TaskBatcher$BatchedTask.run(TaskBatcher.java:188) [elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:708) [elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.runAndClean(PrioritizedEsThreadPoolExecutor.java:252) [elasticsearch-6.8.23.jar:6.8.23]
        at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.run(PrioritizedEsThreadPoolExecutor.java:215) [elasticsearch-6.8.23.jar:6.8.23]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_332]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_332]
        at java.lang.Thread.run(Thread.java:750) [?:1.8.0_332]

问题一,出现这个错误会影响数据的写入吗

在Elasticsearch中,当你遇到类似 [DEBUG][o.e.a.a.i.m.p.TransportPutMappingAction] ... failed to put mappings ... mapper [disposalstatus] of different type, current_type [text], merged_type [keyword] 的错误时,这意呀着你尝试更改一个已存在字段(在这个例子中是 disposalstatus)的类型,但是Elasticsearch不允许直接更改已索引字段的类型。

关于为什么这个错误“不会影响 disposalstatus 字段的写入”,这主要是因为Elasticsearch在写入数据时不会重新评估或更改字段的映射。一旦数据被索引,它就被存储为与当时字段映射相匹配的形式。因此,即使你尝试更改映射并失败了,已经存在的数据(包括 disposalstatus 字段)仍然保持原样,并且新的写入操作(如果它们遵循现有的映射)仍然可以成功进行。

然而,这并不意味着你可以随意写入与当前映射不匹配的数据类型到 disposalstatus 字段。如果字段的当前类型是 text,并且你尝试写入一个 data 类型(注意:Elasticsearch中没有内置的 data 类型,我猜测你可能是指的复杂数据类型如 objectarray 或其他JSON兼容类型)或 boolean 类型,Elasticsearch将尝试将这些数据转换为与字段映射兼容的形式。

  • 对于 boolean 类型,如果 disposalstatus 字段是 text 类型,并且你尝试写入一个布尔值(如 true 或 false),Elasticsearch将把这些值作为字符串处理,并存储在索引中。虽然查询时可能需要特别注意类型转换,但写入操作本身通常是成功的。

  • 对于复杂数据类型(如 object 或 array),如果 disposalstatus 字段是 text 类型,并且你尝试写入一个对象或数组,Elasticsearch的行为将取决于你的Elasticsearch版本和配置。在某些情况下,它可能会尝试将这些对象或数组转换为字符串(例如,通过JSON序列化),但这通常不是最佳实践,因为这会丢失结构信息并使查询变得复杂。在较新的Elasticsearch版本中,尝试写入不兼容类型的数据可能会导致错误。

总之,错误 mapper [disposalstatus] of different type 不会影响已经存在的 disposalstatus 字段的写入(只要写入的数据类型与当前映射兼容),但它会阻止你更改字段的类型。如果你需要存储不同类型的数据,你应该考虑创建一个新字段,并在写入新数据时同时更新这两个字段。此外,对于复杂数据类型和布尔值,你应该确保字段的映射与你要存储的数据类型相匹配。

问题二:数据可以正常写入,但是能正常的查询,排序,聚合等操作吗
Fielddata is disabled on text fields by default. Set fielddata=true on [disposalstatus] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.

在Elasticsearch中,fielddata 默认在 text 类型的字段上是禁用的。fielddata 是一种将数据从磁盘上的倒排索引(inverted index)加载到内存中以便进行排序、聚合、脚本执行等操作的功能。但是,由于 text 字段通常包含大量文本,如果启用 fielddata,可能会消耗大量内存,影响Elasticsearch集群的性能和稳定性。

错误信息中提到“Fielddata is disabled on text fields by default. Set fielddata=true on [disposalstatus] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.” 是在告诉你几个关键点:

  1. 默认禁用text 字段上的 fielddata 默认是禁用的。
  2. 启用fielddata:如果你确实需要在 disposalstatus 字段上使用 fielddata(比如进行排序或聚合操作),你可以在字段映射中设置 fielddata=true。但是,这可能会消耗大量内存。
  3. 使用keyword字段:作为替代方案,你可以将 disposalstatus 字段的类型更改为 keywordkeyword 类型的字段天然支持 fielddata,因为它们通常用于存储不需要全文搜索的精确值(如标签、枚举值等)。
为什么使用 keyword 字段?
  • 内存使用keyword 字段的 fielddata 消耗的内存通常比 text 字段少,因为 keyword 字段不包含分词(tokenization)和索引(indexing)过程中产生的额外信息。
  • 性能:对于排序、聚合和脚本等需要 fielddata 的操作,keyword 字段通常能提供更好的性能。
  • 用途keyword 字段非常适合存储不需要全文搜索的精确字符串值。
如何更改字段类型?

如果你需要更改 disposalstatus 字段的类型从 text 到 keyword,并且该字段尚未包含大量数据,你可以通过以下步骤进行:

  1. 更新映射:使用Elasticsearch的映射API来更新索引的映射,将 disposalstatus 字段的类型更改为 keyword。但是,请注意,直接更改已存在字段的类型通常是不允许的。因此,你可能需要:

    • 删除索引并重新创建它,包含新的映射。
    • 或者,如果可能的话,使用别名(alias)和索引滚动更新(rolling updates)来最小化停机时间。
    • 在某些情况下,如果Elasticsearch版本和配置允许,你可以使用索引模板来为新文档设置新的字段类型,但已存在的文档不会受到影响。
  2. 重新索引数据(如果需要):如果你选择了删除并重新创建索引的方法,你需要将旧索引中的数据重新索引到新索引中。

  3. 验证更改:在更改生效后,验证新索引和映射是否符合预期,并确保应用程序可以正确地与新的索引和映射交互。

请注意,在进行此类更改之前,最好先在测试环境中验证更改的影响。

建议

  1. 在生产环境中,为了确保数据的结构和类型一致性,以及避免潜在的性能问题和数据不一致,强烈建议显式定义索引的映射,设置索引的动态映射策略为禁用动态映射,忽略新字段。
  2. 针对出现的字段映射不一致的问题,采用重建索引(reindex)的方式进行修正,避免出现存储查询排序聚合等错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值