深入理解Elasticsearch的索引映射(mapping)

当我们谈论Elasticsearch时,索引映射是一个核心概念,它定义了如何存储和检索数据。在Elasticsearch 7.6及更高版本中,映射提供了一系列强大的功能,使您能够精确地控制数据的结构和行为。本文将详细介绍映射的关键属性、用途以及如何正确设置和优化它。

一、映射基础

在Elasticsearch中,映射类似于关系型数据库中的表结构定义。它描述了索引中字段的类型、如何索引这些字段以及如何处理这些字段的查询。每个索引都有一个与之关联的映射类型,尽管在Elasticsearch 7.x中,每个索引只能有一个映射类型(与之前版本中的多个映射类型不同)。

映射定义包括字段的数据类型(如text、keyword、integer、date等),这些类型决定了字段如何被索引和搜索。此外,映射还可以包含其他设置,如字段是否应存储原始值、是否应创建doc values以便于排序和聚合等。

二、关键属性与用途

1. 字段类型

选择合适的字段类型对于优化存储和查询性能至关重要。例如,对于全文搜索,应使用text类型;对于精确值搜索、排序和聚合,应使用keyword类型。
在Elasticsearch中,字段类型是映射定义的核心部分,它决定了字段如何被索引和如何在查询中被使用。以下是Elasticsearch中一些常见的字段类型及其用途的详细介绍:

1.1 text 类型
  • 用途:用于全文搜索,即当需要对字段内容进行分词、建立倒排索引,并执行相关度评分查询时,应使用text类型。
  • 特点:text类型的字段在索引前会经过分析器(analyzer)处理,将其拆分成单独的词项(term),并建立倒排索引。这使得text字段可以高效地执行全文搜索查询。
1.2 keyword 类型
  • 用途:用于精确值搜索,如标签、邮箱地址、主机名、状态码、邮政编码或任何其他需要精确匹配的字段。
  • 特点:keyword类型的字段不会被分析器处理,而是将整个字段值作为单个词项索引。因此,它们只能用于精确匹配查询,如term查询。此外,keyword字段通常用于排序、聚合和脚本计算。
1.3 数值类型:如integerlongfloatdouble等。
  • 用途:用于存储数字数据,如价格、数量、评分等。
  • 特点:数值类型的字段可以执行范围查询、排序和聚合操作。它们按原样存储,不会经过分析器处理。
1.4 date 类型
  • 用途:用于存储日期和时间数据。
  • 特点:date类型的字段可以接受多种日期格式,并可以将其转换为内部格式(UTC毫秒时间戳)进行存储。这使得它们可以执行范围查询、排序和基于时间的聚合操作。
1.5 boolean 类型
  • 用途:用于存储布尔值(true/false)。
  • 特点:boolean类型的字段可以接受truefalse或缺失值。它们通常用于过滤查询,如term查询。
1.6 geo 类型:如geo_pointgeo_shape等。
  • 用途:用于存储地理位置数据,如经纬度坐标或复杂地理形状。
  • 特点:geo类型的字段可以执行地理位置相关的查询,如距离计算、区域搜索等。它们通常与地图可视化工具结合使用。
1.7 nested 类型
  • 用途:用于存储嵌套结构的JSON对象数组。
  • 特点:nested类型的字段允许您保持数组中对象的独立性,使得可以对嵌套对象执行精确查询和聚合操作。这对于处理具有复杂结构的JSON数据非常有用。

除了上述常见类型外,Elasticsearch还支持其他更专业的字段类型,如ip类型用于存储IP地址,join类型用于父子关系建模等。正确选择字段类型对于优化存储空间和查询性能至关重要。因此,在创建索引映射时,应根据数据的特性和查询需求仔细选择合适的字段类型。

2. 索引选项

在Elasticsearch中,索引选项是映射定义中的一个重要部分,它决定了字段如何被索引以及索引的哪些属性应该被存储。这些选项可以帮助您优化存储空间和查询性能,同时提供灵活的搜索功能。

以下是Elasticsearch中一些常见的索引选项及其详细介绍:

2.1 index
  • 用途:此选项用于控制字段是否被索引。如果设置为true,则字段将被索引并可搜索。如果设置为false,则字段不会被索引,但仍然可以存储在_source字段中。
  • 默认值:通常为true,但具体取决于字段类型和其他设置。
2.2 store
  • 用途:此选项确定是否应在索引中单独存储字段的原始值。如果设置为true,则可以在不检索整个_source字段的情况下检索该字段的值。这可以提高某些查询的性能,但会增加索引的存储需求。
  • 默认值:通常为false,因为Elasticsearch默认存储整个文档的JSON源,并且可以通过_source字段检索任何字段。
2.3 doc_values
  • 用途:doc_values是一个在磁盘上以列式存储的字段值的副本,用于排序、聚合和脚本计算。对于需要频繁进行这些操作的字段,启用doc_values可以显著提高性能。
  • 默认值:大多数字段类型默认启用doc_values,但某些类型(如text)默认不启用,因为它们通常不用于排序和聚合。
2.4 fielddata
  • 用途:fielddata是用于在内存中存储字段值的数据结构,主要用于对text字段进行排序和聚合。然而,由于text字段通常包含大量数据且分词后会产生很多词项,启用fielddata可能会导致大量内存消耗。因此,默认情况下禁用text字段的fielddata
  • 默认值:对于text字段,默认禁用fielddata。如果需要对这些字段进行排序或聚合,建议使用keyword类型的多字段或重新索引数据以使用适当的字段类型。
2.5 norms
  • 用途:norms存储了字段长度的归一化因子和索引时词项的权重,用于评分计算。禁用norms可以节省磁盘空间,但会导致无法执行基于词频和文档长度的相关性评分。
  • 默认值:大多数字段类型默认启用norms,但对于不需要评分计算的字段(如用于过滤的字段),可以禁用以节省空间。
2.6 analyzersearch_analyzer
  • 用途:这些选项用于指定在索引和搜索时应用于字段的分析器。分析器负责将文本拆分为词项并转换为小写(可选),以便进行索引和搜索。analyzer用于索引和搜索,而search_analyzer仅用于搜索(如果未指定,则使用analyzer的设置)。
  • 默认值:如果未指定,则使用默认的分析器(通常是标准分析器)。但是,对于不同类型的字段和数据,选择适当的分析器非常重要。
2.7 null_value
  • 用途:此选项允许您为字段指定一个默认值,当字段在文档中为null或缺失时,Elasticsearch将使用这个默认值。这对于在索引时处理空值非常有用,可以确保查询和聚合的一致性。
  • 默认值:无默认值。您需要显式地为字段指定一个null_value
  • 注意事项:null_value必须是与字段类型相匹配的值。例如,对于keyword类型的字段,null_value可以是一个字符串;对于数值类型的字段,它必须是一个数字。

我们创建一个索引的映射,其中包含一个字段并使用null_value参数:

PUT /my_index
{
  "mappings": {
    "properties": {
      "user_age": {
        "type": "integer",
        "null_value": -1 // 当user_age字段的值为null或缺失时,将其设置为默认值-1
      }
    }
  }
}

接下来,我们可以通过向该索引添加文档来测试null_value的行为。我们将添加两个文档,一个包含user_age字段的值,另一个不包含该字段或将其设置为null

// 添加一个包含user_age字段值的文档
POST /my_index/_doc/1
{
  "user_age": 30
}

// 添加一个不包含user_age字段或将其设置为null的文档
POST /my_index/_doc/2
{
  "user_age": null // 或者完全省略"user_age"字段也会触发null_value的使用
}

现在,如果我们执行一个查询来检索这两个文档,并查看user_age字段的值,我们将看到第一个文档中的user_age值为30,而第二个文档中的user_age值将被替换为我们在映射中指定的null_value,即-1。

下面是一个简单的查询示例:

GET /my_index/_search
{
  "query": {
    "match_all": {} // 匹配所有文档
  }
}

在查询结果中,你将看到类似于以下的输出(仅包含相关字段):

{
  "hits": {
    "hits": [
      {
        "_id": "1",
        "_source": {
          "user_age": 30 // 第一个文档的user_age字段值为30
        }
      },
      {
        "_id": "2",
        "_source": {
          "user_age": -1 // 第二个文档的user_age字段被替换为null_value指定的默认值-1
        }
      }
    ]
  }
}

使用null_value需要谨慎。虽然它可以为缺失或null字段提供默认值,但它可能会引入歧义或误导性数据。确保你了解使用null_value的潜在影响,并根据你的业务需求和数据模型做出决策。在某些情况下,更好的做法是在应用程序层面处理null值,而不是依赖Elasticsearch的null_value功能。

2.8 format
  • 用途:主要用于日期字段,指定日期的格式。这告诉Elasticsearch如何解析和格式化日期字段的值。
  • 默认值:无默认值,必须为日期字段显式指定格式,除非使用默认的日期格式。
  • 注意事项:确保指定的格式与您的日期数据匹配,否则Elasticsearch可能无法正确解析日期。
2.9 ignore_above
  • 用途:对于keyword类型的字段,此选项指定了一个字符数限制。如果字段值的字符数超过此限制,则该字段不会被索引。这有助于防止非常大的字段值消耗过多的索引空间。
  • 默认值:无默认值,需要显式设置。
2.10 eager_global_ordinals
  • 用途:此选项用于控制是否预先加载字段的全局序数(global ordinals)。全局序数用于加速聚合和排序操作。如果设置为true,则全局序数将在索引刷新时计算并加载到内存中。
  • 默认值:通常为false,因为预先加载全局序数会增加索引的刷新时间和内存使用量。但在需要高频聚合的场景下,将其设置为true可能会提高性能。
2.11 meta
  • 用途:此选项允许您在字段定义中包含任意的元数据。这些元数据不会用于索引或搜索,但可以在检索字段信息时返回。这对于存储与字段相关的额外信息(如描述、标签等)非常有用。
  • 默认值:无默认值。您可以根据需要添加任意数量和类型的元数据。
2.12 copy_to
  • 用途:此选项允许您将字段的内容复制到其他字段中。这在您希望在不更改查询逻辑的情况下对多个字段进行搜索时非常有用。例如,您可以将一个字段的内容复制到另一个用于全文搜索的字段中。
  • 默认值:无默认值。您需要显式指定要复制到的字段名。

请注意,不是所有的索引选项都适用于所有字段类型。在选择和配置索引选项时,请务必参考Elasticsearch的官方文档以了解每个选项的适用性和限制。索引选项的设置应根据字段的具体用途和查询需求进行配置。不正确的设置可能会导致性能下降、存储空间浪费或无法满足搜索需求。因此,在创建或更新索引映射时,请仔细考虑每个字段的索引选项。

以下是一个mapping示例:

PUT /test_index
{
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 1
    },
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": { // 自定义分析器,用于索引时处理文本
          "type": "standard",
          "stopwords": ["and", "the"] // 停用词列表
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text", // 文本字段类型
        "analyzer": "my_custom_analyzer", // 使用自定义分析器进行索引
        "search_analyzer": "standard", // 使用标准分析器进行搜索
        "fields": {
          "keyword": {
            "type": "keyword" // 子字段,用于精确匹配
          }
        }
      },
      "content": {
        "type": "text", // 文本字段类型
        "fielddata": true, // 启用fielddata以支持排序和聚合(注意:这可能会消耗大量内存)
        "fields": {
          "raw": {
            "type": "keyword" // 子字段,用于精确匹配原始内容
          }
        }
      },
      "date_published": {
        "type": "date", // 日期字段类型
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" // 支持的日期格式列表
      },
      "is_published": {
        "type": "boolean" // 布尔字段类型
      },
      "price": {
        "type": "float" // 浮点数字段类型
      },
      "stock_count": {
        "type": "integer", // 整数字段类型
        "doc_values": false, // 禁用doc_values以减少磁盘使用(注意:这可能会影响性能)
        "ignore_above": 1000 // 忽略大于1000的值
      },
      "product_tags": {
        "type": "keyword", // 关键字字段类型
        "norms": false, // 禁用norms以减少磁盘使用(注意:这可能会影响相关性评分)
        "eager_global_ordinals": true // 启用以加速聚合和排序操作(注意:可能会增加内存消耗)
      },
      "description": {
        "type": "text", // 文本字段类型
        "index": false, // 设置为不可索引
        "store": true // 存储原始值(注意:这会增加存储需求)
      },
      "meta_data": {
        "type": "object", // 对象字段类型(可以包含任意JSON对象)
        "enabled": false // 禁用该字段(注意:这意味着它不会被索引或搜索)
      },
      "all_fields": {
        "type": "text", // 文本字段类型(用于将所有内容复制到一个字段中)
        "copy_to": ["title", "content"] // 将内容复制到title和content字段中(注意:这会增加索引大小)
      }
    }
  }
}

请注意,在注释中提到了某些选项可能带来的性能或存储影响。在实际应用中,您应该根据您的具体需求和资源限制来仔细选择这些选项。此外,随着Elasticsearch版本的变化,某些选项的行为和默认值可能会发生变化,因此请务必查阅与您正在使用的Elasticsearch版本相对应的官方文档。

3. 多字段

多字段(Multi-fields)是一种允许您在同一个字段上定义多种不同索引和搜索方式的功能。通过为字段定义多个子字段,每个子字段可以有不同的映射类型和分析器设置,以满足不同的搜索和索引需求。例如,您可以将一个字段同时定义为textkeyword类型,以便同时支持全文搜索和精确匹配。

多字段在Elasticsearch的映射定义中非常灵活,并且可以用于多种场景。以下是多字段的一些常见用法和示例:

  1. 不同分析器:您可以为同一个文本字段定义多个子字段,并为每个子字段指定不同的分析器。例如,一个字段可以使用标准分析器进行全文搜索,而另一个子字段可以使用关键字分析器进行精确匹配。

  2. 不同数据类型:除了文本类型外,您还可以为数字、日期等类型的字段定义多字段。例如,一个日期字段可以有一个子字段用于日期范围搜索,而另一个子字段可以将其存储为字符串以支持更复杂的文本匹配。

  3. 多语言支持:如果您的应用程序需要支持多种语言,您可以为每种语言定义一个子字段,并为每个子字段指定适当的语言分析器。

  4. 自定义搜索逻辑:通过定义多个子字段,您可以实现更复杂的搜索逻辑。例如,您可以有一个子字段用于全文搜索,另一个子字段用于实现拼音搜索或前缀搜索。

请注意,多字段不会增加原始文档中的字段数量或更改其结构。它们只是在索引时根据映射定义生成额外的索引项,并在搜索时提供不同的搜索选项。因此,多字段是一种在不修改原始数据的情况下增强搜索功能的强大工具。

4. 元字段

虽然Elasticsearch提供了一些特殊的元字段(如_source_field_names),但在7.x版本中已弃用了_all字段。因此,如果需要跨多个字段进行搜索,请使用multi_match查询。

5. 动态映射

当向Elasticsearch索引中插入未在映射中明确定义的字段时,动态映射会自动推断字段的类型。虽然这提供了灵活性,但在生产环境中建议谨慎使用,并考虑关闭此功能或为其配置严格的规则。

6. 分析器与Normalizer

对于text类型的字段,分析器定义了如何将文本拆分为词项。Elasticsearch提供了许多内置的分析器,并支持自定义分析器以满足特定需求。Normalizer则主要用于keyword字段的文本转换,如小写化或去除重音符号等。

三、设置与优化建议

  1. 明确定义字段类型:避免使用动态映射带来的不确定性,明确指定每个字段的类型和索引选项。这将确保数据的准确性和一致性,并提高查询性能。
  2. 利用多字段进行灵活搜索:通过使用多字段功能,您可以为同一个数据字段创建不同类型的索引,以满足不同的搜索需求。例如,您可以同时支持全文搜索、精确匹配和排序/聚合操作。
  3. 合理配置分析器和Normalizer:根据您的数据和查询需求选择合适的分析器和Normalizer。这将确保文本被正确地处理和索引,从而提高搜索准确性和性能。
  4. 监控和调整索引性能:定期使用Elasticsearch提供的监控工具检查索引的性能和资源使用情况。如果发现性能瓶颈或资源浪费,及时调整映射设置或优化查询语句。
  5. 测试与验证:在生产环境中应用映射更改之前,始终在测试环境中进行测试和验证。确保更改不会导致意外的行为或性能下降,并确保数据的完整性和准确性得到维护。

四、结论

通过深入了解Elasticsearch 7.6+的索引映射功能,您可以更好地控制数据的存储和检索方式。正确设置和优化映射将有助于提高查询性能、减少资源消耗并确保数据的准确性和一致性。随着Elasticsearch的不断发展和改进,掌握这些映射技巧将使您能够充分利用这个强大搜索引擎的潜力。

你可能感兴趣的:(DB,elasticsearch,java)