Dynamic mapping 是 Elasticsearch 中的特性,指的是当 Elasticsearch 遇到文档中以前未遇到的字段,它用什么数据类型来进行映射。这看似是一个非常好的功能,因为有了 Dynamic Mapping 的机制,使得我们无需手动定义Mappings,ES 会自动推算出字段的类型。但是在某些场景下是有问题的,如果 Elasticsearch 是作为重要的数据存储,如果遇到前所未有的字段,我们可能希望 Elasticsearch 直接抛出异常而不是直接索引,这样可以及时发现问题。 例如:
PUT es_books/_doc/1
{
"title":" how to master elasticsearch"
}
查看 index mapping
GET es_books/_mapping
输出如下
{
"es_books" : {
"mappings" : {
"properties" : {
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
当我们索引一个新的字段后,会发现索引的mapping 已经发生变化:
PUT es_books/_doc/2
{
"title":"ElasticSearch 7.0",
"public_date":"2017"
}
查看 index mapping
```json
{
"es_books" : {
"mappings" : {
"properties" : {
"public_date" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
我们前面提到,默认情况下,当在文档中找到以前未见过的字段时,Elasticsearch 会将新字段添加到类型映射中。 如果我们将 dynamic 参数设置为 false(忽略新字段)或 strict(如果遇到未知字段则抛出异常),可以在文档和对象级别禁用此行为。
1. dynamic = strict
将 dynamic mapping 设置为 strict:发现文档将不会被索引:
PUT es_books/_mapping
{
"dynamic": "strict"
}
写入数据:
PUT es_books/_doc/4
{
"title":"ElasticSearch 7.0",
"public_date":"2017",
"first_author" : "rudonx"
}
执行后反馈如下:
{
"error" : {
"root_cause" : [
{
"type" : "strict_dynamic_mapping_exception",
"reason" : "mapping set to strict, dynamic introduction of [first_author] within [_doc] is not allowed"
}
],
"type" : "strict_dynamic_mapping_exception",
"reason" : "mapping set to strict, dynamic introduction of [first_author] within [_doc] is not allowed"
},
"status" : 400
}
2. dynamic = false
PUT dynamic_mapping_test/_mapping
{
"dynamic": false
}
PUT dynamic_mapping_test/_doc/2
{
"newField":"field2"
}
我们发现文档看起来可以被索引,返回如下:
{
"_index" : "dynamic_mapping_test",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
但是新的字段没有被索引, 该字段不可以被搜索:
POST dynamic_mapping_test/_search
{
"query":{
"match":{
"newField":"field2"
}
}
}
返回如下:
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
使用 strict 是一种比较严格的方式,如果实际的业务上不可避免的有新字段的加入,我们只能使用 dynamic = true 的设置,在索引中定义太多字段可能会导致索引膨胀,对性能造成损失,我们建议使用一些参数来控制 mapping 的行为。
1. index.mapping.total_fields.limit
索引中的最大字段数。 字段和对象映射以及字段别名都计入此限制。 默认值为1000
2, index.mapping.nested_fields.limit
索引中不同 nested 映射的最大数量,默认为50。
3. index.mapping.depth.limit
字段的最大深度,以内部对象的数量来衡量。 例如,如果所有字段都在根对象级别定义,则深度为1.如果有一个对象映射,则深度为2。默认值为20。 设置的方式如下:
PUT /dynamic_mapping_test/_settings
{
"index.mapping.total_fields.limit": 200,
"index.mapping.depth.limit" : "50"
}
查看 index setting
GET dynamic_mapping_test/_settings
输出如下:
{
"dynamic_mapping_test" : {
"settings" : {
"index" : {
"mapping" : {
"total_fields" : {
"limit" : "200"
},
"depth" : {
"limit" : "50"
}
},
"number_of_shards" : "1",
"provided_name" : "dynamic_mapping_test",
"creation_date" : "1644810265240",
"number_of_replicas" : "1",
"uuid" : "ECDPqm-RQ22y8-eRcWQhjQ",
"version" : {
"created" : "7100299"
}
}
}
}
}
总结下控制dynamic mapping 的参数 dynamic 的三种取值:
- 在 dynamic 为 true 时,这个文档将被建立索引,因此新增加的字段将是可以被搜索的,mapping 也会自动更新
- 在 dynamic 为 false 时,这个文档将被建立索引,但是新的字段将不被建立索引,mapping 将不被更新。
- 在 dynamic 为 strict 时,这个文档将不被建立索引,返回错误信息。
取值 | doc indexed ? | fields indexed ? | mapping updated? |
---|---|---|---|
true | 是 | 是 | 是 |
false | 是 | 否 | 否 |
strict | 否 |
综上所述,如果您的业务对数据要求非常严格,建议您使用 strict。 更多功能,您可以参考文末的官方文档。
[1 https://www.elastic.co/guide/en/elasticsearch/reference/7.17/dynamic.html#dynamic-parameters [2] https://www.elastic.co/guide/en/elasticsearch/reference/7.17/mapping-settings-limit.html#mapping-settings-limit 如果您有其他问题,欢迎您联系火山引擎技术支持服务