概念
在 Elasticsearch 中,映射(Mapping)定义了索引中字段的类型和属性。它是索引数据结构的基础,类似于传统数据库中的表结构定义。映射不仅定义了字段的类型(如 text
、keyword
、integer
等),还定义了字段的分析器、是否存储、是否索引等属性。映射是 Elasticsearch 中定义索引字段类型和属性的重要概念。通过显式定义映射,你可以控制字段的类型、分析器和其他属性,从而更好地管理和优化索引数据。动态映射则提供了灵活性,使得在没有显式映射的情况下也能插入数据。
映射的基本概念
字段类型:Elasticsearch 支持多种字段类型,包括文本(
text
)、关键字(keyword
)、整数(integer
)、浮点数(float
)、日期(date
)、布尔(boolean
)等。分析器:对于文本字段,可以指定分析器(Analyzer),用于在索引和搜索时对文本进行分词和处理。
多字段:一个字段可以有多个子字段(Multi-fields),每个子字段可以有不同的类型和分析器。
动态映射:如果没有显式定义映射,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
类型,我猜测你可能是指的复杂数据类型如 object
、array
或其他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.” 是在告诉你几个关键点:
- 默认禁用:
text
字段上的fielddata
默认是禁用的。 - 启用fielddata:如果你确实需要在
disposalstatus
字段上使用fielddata
(比如进行排序或聚合操作),你可以在字段映射中设置fielddata=true
。但是,这可能会消耗大量内存。 - 使用keyword字段:作为替代方案,你可以将
disposalstatus
字段的类型更改为keyword
。keyword
类型的字段天然支持fielddata
,因为它们通常用于存储不需要全文搜索的精确值(如标签、枚举值等)。
为什么使用 keyword
字段?
- 内存使用:
keyword
字段的fielddata
消耗的内存通常比text
字段少,因为keyword
字段不包含分词(tokenization)和索引(indexing)过程中产生的额外信息。 - 性能:对于排序、聚合和脚本等需要
fielddata
的操作,keyword
字段通常能提供更好的性能。 - 用途:
keyword
字段非常适合存储不需要全文搜索的精确字符串值。
如何更改字段类型?
如果你需要更改 disposalstatus
字段的类型从 text
到 keyword
,并且该字段尚未包含大量数据,你可以通过以下步骤进行:
更新映射:使用Elasticsearch的映射API来更新索引的映射,将
disposalstatus
字段的类型更改为keyword
。但是,请注意,直接更改已存在字段的类型通常是不允许的。因此,你可能需要:- 删除索引并重新创建它,包含新的映射。
- 或者,如果可能的话,使用别名(alias)和索引滚动更新(rolling updates)来最小化停机时间。
- 在某些情况下,如果Elasticsearch版本和配置允许,你可以使用索引模板来为新文档设置新的字段类型,但已存在的文档不会受到影响。
重新索引数据(如果需要):如果你选择了删除并重新创建索引的方法,你需要将旧索引中的数据重新索引到新索引中。
验证更改:在更改生效后,验证新索引和映射是否符合预期,并确保应用程序可以正确地与新的索引和映射交互。
请注意,在进行此类更改之前,最好先在测试环境中验证更改的影响。
建议
- 在生产环境中,为了确保数据的结构和类型一致性,以及避免潜在的性能问题和数据不一致,强烈建议显式定义索引的映射,设置索引的动态映射策略为禁用动态映射,忽略新字段。
- 针对出现的字段映射不一致的问题,采用重建索引(reindex)的方式进行修正,避免出现存储查询排序聚合等错误。