Elasticsearch 倒排索引 和 正排索引

一、倒排索引

倒排索引是 Elasticsearch 实现高效全文搜索的核心技术。它通过将词项与文档 ID 关联,支持快速检索、短语查询、布尔查询和相关性评分。尽管倒排索引在存储和更新方面有一定的开销,但通过词典优化、倒排列表压缩、分片和缓存等技术,Elasticsearch 能够高效地处理大规模数据的搜索需求。

1.1 倒排索引的结构

倒排索引的核心是:词典(Term Dictionary) 和 倒排列表(Posting List)。

  1. 词典(Term Dictionary):
    • 作用:存储所有文档中出现的词项(Terms),并按字典序排序。
    • 特点:
      • 支持快速查找某个词项是否存在。
      • 存储词项的元数据,如文档频率(Document Frequency, DF)。
    • 数据结构:
      • 通常使用 FST(Finite State Transducer) 存储,支持高效的前缀查找和范围查询。
  2. 倒排列表(Posting List):
    • 作用:记录包含某个词项的所有文档及其相关信息。
    • 内容:
      • 文档 ID(DocID):标识包含该词项的文档。
      • 词频(Term Frequency, TF):词项在文档中出现的次数。
      • 位置信息(Positions):词项在文档中的具体位置(用于短语查询或高亮显示)。
      • 偏移量(Offsets):词项在文档中的起始和结束位置(可选)。
    • 存储优化:
      • 使用 差值编码(Delta Encoding) 压缩文档 ID 和位置信息。
      • 使用 FOR(Frame of Reference) 或 RLE(Run-Length Encoding) 等压缩算法减少存储空间。

示例:

  • 文档内容

    • 文档 1:"Elasticsearch is powerful"
    • 文档 2:"Elasticsearch is fast and powerful"
  • 分词结果

    经过分词和标准化处理后,生成的词项(Terms)如下:

    • 文档 1 的词项:["elasticsearch", "is", "powerful"]
    • 文档 2 的词项:["elasticsearch", "is", "fast", "and", "powerful"]
  • 倒排索引结构

    1. 词典(Term Dictionary)

      词典存储所有词项及其元数据(如文档频率):

      词项(Term) 文档频率(DF)
      "and" 1
      "elasticsearch" 2
      "fast" 1
      "is" 2
      "powerful" 2
    2. 倒排列表(Posting List)

      每个词项对应一个倒排列表,记录包含该词项的文档及其相关信息(如文档 ID、词频、位置信息、偏移量等)。

      • 词项 "elasticsearch"

        文档 ID(DocID) 词频(TF) 位置信息(Positions) 偏移量(Offsets)
        1 1 [0] [(0, 13)]
        2 1 [0] [(0, 13)]
      • 词项 "is"

        文档 ID(DocID) 词频(TF) 位置信息(Positions) 偏移量(Offsets)
        1 1 [1] [(14, 16)]
        2 1 [1] [(14, 16)]
      • 词项 "powerful"

        文档 ID(DocID) 词频(TF) 位置信息(Positions) 偏移量(Offsets)
        1 1 [2] [(17, 25)]
        2 1 [4] [(27, 35)]
      • 词项 "fast"

        文档 ID(DocID) 词频(TF) 位置信息(Positions) 偏移量(Offsets)
        2 1 [2] [(17, 21)]
      • 词项 "and"

        文档 ID(DocID) 词频(TF) 位置信息(Positions) 偏移量(Offsets)
        2 1 [3] [(22, 25)]
    3. 压缩与存储优化

      在实际存储中,Elasticsearch 会对倒排列表进行压缩和优化:

      • 文档 ID:使用差值编码(Delta Encoding)压缩。例如,文档 ID [1, 2] 存储为 [1, 1](差值)。
      • 位置信息:使用差值编码压缩。例如,位置信息 [0, 1, 2] 存储为 [0, 1, 1](差值)。
      • 偏移量:使用差值编码压缩。
    4. 完整倒排索引结构

      bash 复制代码
      {
        "terms": {
          "and": {
            "doc_freq": 1,
            "postings": [
              {
                "doc_id": 2,
                "term_freq": 1,
                "positions": [3],
                "offsets": [(22, 25)]
              }
            ]
          },
          "elasticsearch": {
            "doc_freq": 2,
            "postings": [
              {
                "doc_id": 1,
                "term_freq": 1,
                "positions": [0],
                "offsets": [(0, 13)]
              },
              {
                "doc_id": 2,
                "term_freq": 1,
                "positions": [0],
                "offsets": [(0, 13)]
              }
           ]
          },
          "fast": {
            "doc_freq": 1,
            "postings": [
              {
                "doc_id": 2,
                "term_freq": 1,
                "positions": [2],
                "offsets": [(17, 21)]
              }
            ]
          },
          "is": {
            "doc_freq": 2,
            "postings": [
              {
                "doc_id": 1,
                "term_freq": 1,
                "positions": [1],
                "offsets": [(14, 16)]
              },
              {
                "doc_id": 2,
                "term_freq": 1,
                "positions": [1],
                "offsets": [(14, 16)]
              }
            ]
          },
          "powerful": {
            "doc_freq": 2,
            "postings": [
              {
                "doc_id": 1,
                "term_freq": 1,
                "positions": [2],
                "offsets": [(17, 25)]
              },
              {
                "doc_id": 2,
                "term_freq": 1,
                "positions": [4],
                "offsets": [(27, 35)]
              }
            ]
          }
        }
      }
1.2 倒排索引的构建过程

Elasticsearch 的倒排索引构建过程是其高效搜索能力的核心。以下是倒排索引构建的详细步骤:

  1. 文档接收与解析:

    • Elasticsearch 接收文档(通常为 JSON 格式),并解析文档中的字段。
    • 每个文档会被分配一个唯一的文档 ID(_id),用于标识该文档。
  2. 文本分析与分词:

    • 对文档中的文本字段进行分析,生成标准化的词项(Terms)。
    • 分析过程包括以下步骤:
      1. 字符过滤:去除无用字符(如 HTML 标签)。
      2. 分词(Tokenization):将文本拆分为单独的单词或词条。
      3. 标准化(Normalization):
        • 小写转换(Lowercasing)。
        • 去除停用词(Stop Words Removal)。
        • 词干提取(Stemming)。
      4. 生成词项:最终生成一组标准化的词项。
  3. 构建倒排索引:

    • 倒排索引的核心是将词项与文档 ID 建立映射关系。
    • 构建过程包括以下步骤:
      1. 词项与文档映射:
        • 对每个词项,记录包含该词项的文档 ID。
      2. 倒排列表(Posting List):
        • 为每个词项创建一个倒排列表,包含以下信息:
          • 文档 ID:标识包含该词项的文档。
          • 词频(Term Frequency):词项在文档中出现的次数。
          • 位置信息(Positions):词项在文档中的具体位置(用于短语查询或高亮显示)。
          • 偏移量(Offsets):词项在文档中的起始和结束位置(可选)。
      3. 词典(Term Dictionary):
        • 将所有词项按字典序排序,并存储到词典中,方便快速查找。
  4. 索引段(Segment)的生成

    • Elasticsearch 的索引由多个不可变的段(Segment)组成。
    • 构建过程包括以下步骤:
      1. 内存中的索引:
        • 新写入的文档会先在内存中构建倒排索引。
      2. 刷新(Refresh):
        • 每隔一段时间(默认 1 秒),内存中的索引会被刷新到磁盘,生成一个新的索引段。
      3. 段文件:
        • 每个段包含一个完整的倒排索引,以及词典、倒排列表等信息。
  5. 段的合并(Segment Merging)

    • 随着文档的不断写入,段的数量会逐渐增加,影响查询性能。
    • Elasticsearch 会定期执行段合并:
      1. 选择小段:选择多个小段进行合并。
      2. 合并索引:将小段的倒排索引合并为更大的段。
      3. 删除文档清理:在合并过程中,标记为删除的文档会被物理删除,释放存储空间。
  6. 索引存储与压缩

    • 倒排索引以高效的文件格式存储,并使用压缩技术减少存储空间。
    • 存储与压缩包括以下内容:
      1. 词典存储:
        • 使用 FST(Finite State Transducer)等数据结构存储词典,支持快速查找。
      2. 倒排列表存储:
        • 使用 FOR(Frame of Reference)、RLE(Run-Length Encoding)等压缩算法存储倒排列表。
      3. 位置信息存储:
        • 使用差值编码(Delta Encoding)等技术压缩位置信息。
  7. 分布式环境下的索引构建

    • Elasticsearch 是分布式系统,索引构建过程涉及分片和副本:
      1. 分片(Sharding):
        • 索引被分成多个分片,分布在不同节点上,每个分片是一个独立的倒排索引。
      2. 副本(Replication):
        • 每个分片可以有多个副本,提高可用性和查询性能。
      3. 分布式写入:
        • 文档写入时,会根据路由规则(如 _id 哈希)分配到对应的分片。
      4. 一致性保证:
        • Elasticsearch 使用主从复制机制,确保数据一致性。
1.3 倒排索引的优势

倒排索引的主要优势在于:

  1. 高效的全文搜索:

    • 快速定位文档:通过词项(Term)快速找到包含该词项的所有文档。

    • 支持布尔查询:支持 AND、OR、NOT 等逻辑操作,便于构建复杂的查询条件。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从倒排索引中查找 "fast" 对应的文档 ID 列表,并返回相关文档。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "match": {
            "content": "fast"
          }
        }
      }
  2. 支持短语查询:

    • 精确匹配短语:可以查找包含特定短语的文档。

    • 支持位置查询:支持基于词项位置的查询(如邻近查询)。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会查找 "is" 和 "fast" 在文档中相邻的位置,并返回匹配的文档。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "match_phrase": {
            "content": "is fast"
          }
        }
      }
  3. 支持相关性评分:

    • TF-IDF 评分:基于词频和文档频率计算文档的相关性。

    • BM25 评分:更先进的评分算法,适用于现代搜索引擎。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会计算每个文档的相关性评分,并返回最相关的文档。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "match": {
            "content": "elasticsearch"
          }
        }
      }
  4. 支持多字段搜索

    • 跨字段查询:可以在多个字段中搜索同一个词项。

    • 字段优先级:可以为不同字段设置不同的权重,影响相关性评分。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会在 title 和 content 字段中搜索 "fast",并返回匹配的文档。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "multi_match": {
            "query": "fast",
            "fields": ["title", "content"]
          }
        }
      }
  5. 高效的布尔查询

    • AND 查询:查找同时包含多个词项的文档。

    • OR 查询:查找包含任意一个词项的文档。

    • NOT 查询:排除包含特定词项的文档。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会查找同时包含 "fast" 和 "powerful" 的文档。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "bool": {
            "must": [
              { "match": { "content": "fast" } },
              { "match": { "content": "powerful" } }
            ]
          }
        }
      }
  6. 支持前缀和通配符查询

    • 前缀查询:查找以特定前缀开头的词项。

    • 通配符查询:查找符合特定模式的词项。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会查找以 "elast" 开头的词项(如 "elasticsearch")。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "wildcard": {
            "content": "elast*"
          }
        }
      }
  7. 支持模糊查询

    • 容错搜索:支持查找与目标词项相似的词项。

    • 编辑距离:可以指定允许的最大编辑距离。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会查找与 "elastik" 相似的词项(如 "elasticsearch")。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "fuzzy": {
            "content": "elastik"
          }
        }
      }
  8. 高效的压缩存储

    • 差值编码(Delta Encoding):对有序的文档 ID 列表进行压缩。
    • FOR(Frame of Reference):对数值型文档 ID 列表进行压缩。
    • RBM(Roaring Bitmaps):对稀疏文档 ID 列表进行压缩。
  9. 支持动态更新

    • 实时搜索:新文档可以快速被索引并支持搜索。
    • 增量更新:只更新受影响的倒排列表,减少更新开销。
1.4 倒排索引的局限性

尽管倒排索引非常强大,但它也有一些局限性:

  1. 存储开销:
    • 词项存储:倒排索引需要存储所有文档中的词项及其对应的文档列表,词项数量庞大时,存储开销较大。
    • 位置信息:为支持短语查询,还需存储词项在文档中的位置信息,进一步增加存储需求。
  2. 更新开销:
    • 实时性差:倒排索引的更新需要重建部分索引,频繁更新会影响性能。
    • 删除操作复杂:删除文档时,标记删除而非立即移除,可能导致索引膨胀,需定期合并段(segment merging)来清理。
  3. 查询性能
    • 长尾词项:某些词项出现在大量文档中,查询这些词项时性能较差。
    • 复杂查询:布尔查询、短语查询等复杂操作需要合并多个词项的倒排列表,计算量大,性能下降。
  4. 内存消耗
    • 缓存需求:为提升查询性能,常将部分倒排索引缓存到内存,词项多时内存消耗大。
    • 堆内存压力:Elasticsearch 依赖 JVM 堆内存,大规模数据时可能引发 GC 问题,影响性能。
  5. 不适合数值范围查询:
    • 倒排索引主要用于文本搜索,对于数值范围查询(如 age > 30),Elasticsearch 使用 BKD Tree 等其他数据结构。
  6. 文本分析的局限性
    • 分词依赖:倒排索引依赖分词器,分词效果直接影响查询准确性,不同语言和领域的分词效果可能不理想。
    • 同义词与语义理解:难以处理同义词和语义理解,需额外配置同义词词典或使用 NLP 技术。
1.5 倒排索引的用途

倒排索引主要用于以下场景:

  1. 全文搜索
    • 支持对文本字段的高效全文搜索,通过词项(Term)快速定位包含该词项的文档。
    • 例如,搜索 "fast" 时,Elasticsearch 会查找倒排索引中 "fast" 对应的文档 ID 列表。
  2. 短语查询
    • 支持搜索包含特定短语的文档,倒排索引中存储了词项的位置信息(Positions),可以精确匹配短语中词项的顺序和位置。
    • 例如,搜索 "Elasticsearch is" 时,可以找到文档中连续出现这两个词项的位置。
  3. 近似查询
    • 支持搜索词项之间距离在一定范围内的文档,利用倒排索引中的位置信息,计算词项之间的距离。
    • 例如,搜索 "Elasticsearch" 和 "powerful" 之间距离不超过 5 个词的文档。
  4. 高亮显示
    • 在搜索结果中高亮显示匹配的词项,倒排索引中存储了词项的偏移量(Offsets),可以精确定位词项在文档中的起始和结束位置。
    • 根据偏移量提取匹配的词项并进行高亮显示。
  5. 相关性评分
    • 根据文档与查询的相关性对搜索结果进行排序,倒排索引中存储了词频(Term Frequency, TF)和文档频率(Document Frequency, DF)。
    • 使用 TF-IDF 或 BM25 等算法计算文档的相关性评分。
    • 评分越高,文档与查询的相关性越强。
  6. 聚合分析
    • 支持对搜索结果进行统计分析,倒排索引中存储了词项的文档频率(DF),可以快速计算词项的全局统计信息。
    • 例如,计算某个词项在所有文档中出现的频率。
  7. 自动补全
    • 支持搜索时的自动补全功能,倒排索引的词典(Term Dictionary)支持前缀查找,可以快速匹配用户输入的部分词项。
    • 例如,输入 "elas" 时,可以自动补全为 "Elasticsearch"。
  8. 过滤
    • 支持对搜索结果进行过滤,倒排索引可以快速判断某个词项是否存在于文档中。
    • 例如,过滤出包含 "powerful" 但不包含 "slow" 的文档。
  9. 排序
    • 支持对搜索结果按指定字段排序,倒排索引可以快速访问文档的元数据(如文档 ID、词频等),用于排序。
    • 例如,按文档的创建时间或评分进行排序。
1.6 倒排索引的优化

Elasticsearch 在倒排索引的设计和实现中进行了多种优化,以提高存储效率、查询性能和扩展性。以下是 Elasticsearch 对倒排索引的主要优化措施:

  1. 压缩技术

    • 目的:减少存储空间,提高内存和磁盘的利用率。
    • 具体优化:
      • 文档 ID 压缩:使用 差值编码(Delta Encoding) 对文档 ID 进行压缩。
      • 位置信息压缩:使用差值编码对词项的位置信息进行压缩。
      • 倒排列表压缩:使用 FOR(Frame of Reference) 和 RLE(Run-Length Encoding) 等算法压缩倒排列表。
      • 词典压缩:使用 FST(Finite State Transducer) 压缩词典,支持高效的前缀查找和范围查询。
  2. 索引段(Segment)管理

    • 目的:提高写入性能和查询效率。
    • 具体优化:
      • 分段存储:索引由多个不可变的段(Segment)组成,每个段是一个独立的倒排索引。
      • 近实时刷新:新写入的文档会先在内存中构建倒排索引,然后定期刷新到磁盘生成新的段。
      • 段合并:定期将多个小段合并为更大的段,减少段的数量,提升查询性能。
      • 删除文档清理:在段合并过程中,标记为删除的文档会被物理删除,释放存储空间。
  3. 缓存机制

    • 目的:加速查询性能。
    • 具体优化:
      • 过滤器缓存(Filter Cache):缓存常用的过滤查询结果,减少重复计算。
      • 字段数据缓存(Field Data Cache):缓存字段的倒排索引数据,加速聚合和排序操作。
      • 查询结果缓存(Query Cache):缓存查询结果,适用于重复查询。
      • 分片查询缓存(Shard Query Cache):缓存分片级别的查询结果。
  4. 分布式架构

    • 目的:支持大规模数据和高并发查询。
    • 具体优化:
      • 分片(Sharding):将索引分成多个分片,分布在不同节点上,每个分片是一个独立的倒排索引。
      • 副本(Replication):每个分片可以有多个副本,提高可用性和查询性能。
      • 分布式查询:查询时,协调节点将请求分发到相关分片,合并结果后返回给客户端。
  5. 高效的查询执行

    • 目的:加速复杂查询的执行。
    • 具体优化:
      • 跳过不匹配的文档:利用倒排列表的压缩和分块存储,快速跳过不匹配的文档。
      • 布尔查询优化:对布尔查询(AND、OR、NOT)进行优化,减少不必要的计算。
      • 短语查询优化:利用位置信息快速匹配短语。
  6. 近实时搜索

    • 目的:支持近实时的数据搜索。
    • 具体优化:
      • 内存索引:新写入的文档会先在内存中构建倒排索引。
      • 定期刷新:每隔一段时间(默认 1 秒),内存中的索引会被刷新到磁盘,生成新的段。
      • 可搜索性:刷新后,新文档可以立即被搜索到。
  7. 动态更新与删除

    • 目的:支持高效的文档更新和删除。
    • 具体优化:
      • 标记删除:删除文档时,不会立即从倒排索引中移除,而是标记为删除。
      • 段合并清理:在段合并过程中,标记为删除的文档会被物理删除。
      • 更新操作:更新文档时,会生成新版本的文档,旧版本的文档会被标记为删除。
  8. 字段数据类型的优化

    • 目的:针对不同数据类型优化存储和查询。
    • 具体优化:
      • 文本字段:使用倒排索引支持全文搜索。
      • 数值字段:使用 BKD Tree 等数据结构支持范围查询和排序。
      • 地理位置字段:使用 GeoHash 或 Quadtree 支持地理位置查询。

二、正排索引

在 Elasticsearch 中,正排索引(Forward Index) 也称为 行式存储(Row-based Storage) 或 文档存储(Document Store),用于存储完整的文档内容,以便在搜索完成后快速返回文档的原始数据。与倒排索引(Inverted Index)不同,正排索引的主要作用是快速获取某个文档的内容,而不是查找包含某个词项的文档。正排索引的结构与倒排索引(Inverted Index)相辅相成,共同支持 Elasticsearch 的高效搜索和检索功能。

2.1 正排索引的结构

正排索引的结构可以理解为以文档为中心的存储方式,每个文档的所有字段值存储在一起。以下是正排索引的主要组成部分:

  1. 文档 ID(DocID)
    • 作用:唯一标识一个文档。
    • 特点:
      • 文档 ID 是正排索引的主键,用于快速定位文档。
      • 文档 ID 与倒排索引中的文档 ID 一致。
  2. 字段值(Field Values)
    • 作用:存储文档中每个字段的原始值。
    • 特点:
      • 字段值按字段名存储,支持快速访问。
      • 字段值可以是文本、数值、日期、布尔值、数组等类型。
  3. Doc Values
    • 作用:为字段提供列式存储,用于高效支持聚合、排序和脚本计算。
    • 特点:
      • Doc Values 是正排索引的一部分,存储字段值的列式数据。
      • 默认情况下,所有支持聚合和排序的字段都会启用 Doc Values。

示例:

  • 文档内容

    • 文档 1:

      bash 复制代码
      {
        "title": "Elasticsearch Guide",
        "author": "John Doe",
        "year": 2023,
        "tags": ["search", "database"]
      }
    • 文档 2:

      bash 复制代码
      {
        "title": "Introduction to Elasticsearch",
        "author": "Jane Smith",
        "year": 2022,
        "tags": ["tutorial", "search"]
      }
  • 正排索引结构

    1. 文档 ID(DocID)

      • 每个文档有一个唯一的文档 ID,用于标识文档。
    2. 字段值(Field Values)

      • 每个文档的所有字段值按字段名存储。
    3. 完整正排索引

      文档 ID(DocID) 字段名 字段值
      1 title "Elasticsearch Guide"
      1 author "John Doe"
      1 year 2023
      1 tags ["search", "database"]
      2 title "Introduction to Elasticsearch"
      2 author "Jane Smith"
      2 year 2022
      2 tags ["tutorial", "search"]
    4. Doc Values 结构

      Doc Values 是正排索引的一部分,用于支持聚合、排序和脚本计算。以下是 year 和 tags 字段的 Doc Values 结构:

      • year 字段的 Doc Values

        文档 ID(DocID) year
        1 2023
        2 2023
      • tags 字段的 Doc Values

        文档 ID(DocID) Tag
        1 "search"
        1 "database"
        2 "tutorial"
        2 "search"
    5. 存储优化

      • 压缩:字段值可以使用压缩算法(如 LZ4)减少存储空间。
      • 列式存储:Doc Values 使用列式存储,优化聚合和排序性能。
    6. 正排索引的 JSON 表示

      bash 复制代码
      {
        "documents": [
          {
            "doc_id": 1,
            "fields": {
              "title": "Elasticsearch Guide",
              "author": "John Doe",
              "year": 2023,
              "tags": ["search", "database"]
            }
          },
          {
            "doc_id": 2,
           "fields": {
              "title": "Introduction to Elasticsearch",
              "author": "Jane Smith",
              "year": 2022,
              "tags": ["tutorial", "search"]
            }
          }
        ],
        "doc_values": {
          "year": [
            {"doc_id": 1, "value": 2023},
            {"doc_id": 2, "value": 2022}
          ],
          "tags": [
            {"doc_id": 1, "value": "search"},
            {"doc_id": 1, "value": "database"},
            {"doc_id": 2, "value": "tutorial"},
            {"doc_id": 2, "value": "search"}
          ]
        }
      }
2.2 正排索引的构建过程
  1. 文档接收与解析

    • 输入文档:Elasticsearch 接收文档(通常为 JSON 格式),并解析文档中的字段。
    • 文档 ID:为每个文档分配一个唯一的文档 ID(_id),用于标识该文档。
  2. 字段提取与存储

    • 字段提取:从文档中提取所有字段及其值。
    • 字段类型:根据字段的映射(Mapping)确定字段的类型(如文本、数值、日期等)。
    • 存储格式:
      • 正排索引以行为单位存储文档的所有字段值。
      • 对于支持聚合和排序的字段,Elasticsearch 会额外生成 Doc Values(列式存储)。
  3. 正排索引的构建

    • 行式存储:

      • 每个文档的所有字段值存储在一起,形成一行数据。
      • 例如,文档 1 的正排索引行:
      bash 复制代码
      {
        "title": "Elasticsearch Guide",
        "author": "John Doe",
        "year": 2023,
        "tags": ["search", "database"]
      }
    • 列式存储(Doc Values):

      • 对于需要支持聚合、排序和脚本计算的字段,Elasticsearch 会生成 Doc Values。
      • Doc Values 以列为单位存储字段值,例如:
        • year 字段的 Doc Values:

          文档 ID(DocID) year
          1 2023
          2 2023
        • tags 字段的 Doc Values:

          文档 ID(DocID) Tag
          1 "search"
          1 "database"
          2 "tutorial"
          2 "search"
  4. 存储优化

    • 压缩:正排索引中的字段值可以使用压缩算法(如 LZ4)减少存储空间。
    • 延迟加载:正排索引的内容可以按需加载,减少内存占用。
  5. 索引段(Segment)的生成

    • 内存中的正排索引:新写入的文档会先在内存中构建正排索引。
    • 刷新(Refresh):每隔一段时间(默认 1 秒),内存中的索引会被刷新到磁盘,生成一个新的索引段(Segment)。
    • 段文件:每个段包含完整的正排索引和 Doc Values。
  6. 段的合并(Segment Merging)

    • 后台合并:Elasticsearch 会定期将多个小段合并为更大的段,以减少段的数量,提升查询性能。
    • 删除文档清理:在合并过程中,标记为删除的文档会被物理删除,释放存储空间。
  7. 分布式环境下的正排索引

    • 分片(Sharding):索引被分成多个分片,分布在不同节点上,每个分片是一个独立的正排索引。
    • 副本(Replication):每个分片可以有多个副本,用于提高可用性和查询性能。
    • 分布式写入:文档写入时,会根据路由规则(如 _id 哈希)分配到对应的分片。
2.3 正排索引的优势

正排索引的主要优势在于:

  1. 高效文档内容检索:

    • 快速访问文档内容:通过文档 ID 可以直接获取文档的字段值,而不需要遍历倒排索引。

    • 适合文档展示:在搜索结果中显示文档的标题、摘要或其他字段时,正排索引能够提供高效的访问性能。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从正排索引中快速获取 title 和 content 字段的值。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "match": {
            "title": "Elasticsearch"
          }
        },
        "stored_fields": ["title", "content"]
      }
  2. 高效的聚合分析:

    • 列式存储:Doc Values 将字段值按列存储,便于高效计算聚合结果(如求和、平均值、唯一值数量等)。

    • 支持复杂聚合:正排索引支持嵌套聚合、多字段聚合等复杂操作。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从 Doc Values 中获取 age 字段的值,并计算平均值。
      bash 复制代码
      GET /my_index/_search
      {
        "size": 0,
        "aggs": {
          "avg_age": {
            "avg": {
              "field": "age"
            }
          }
        }
      }
  3. 支持排序:

    • 快速排序:通过 Doc Values 可以快速访问字段值,并对文档进行排序。

    • 支持多字段排序:正排索引支持基于多个字段的排序操作。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从 Doc Values 中获取 age 和 name 字段的值,并对文档进行排序。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "match_all": {}
        },
        "sort": [
          { "age": "asc" },
          { "name": "desc" }
        ]
      }
  4. 支持脚本计算

    • 快速访问字段值:脚本可以直接从 Doc Values 中获取字段值,而不需要加载整个文档。

    • 支持复杂计算:脚本可以基于字段值进行计算,并返回自定义结果。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从 Doc Values 中获取 age 字段的值,并计算其两倍。
      bash 复制代码
      GET /my_index/_search
      {
        "script_fields": {
          "double_age": {
            "script": {
              "source": "doc['age'].value * 2"
            }
          }
        }
      }
  5. 高亮显示

    • 快速获取字段值:正排索引可以快速获取文档的原始内容,用于高亮显示匹配的词项。

    • 支持多字段高亮:正排索引支持对多个字段进行高亮显示。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从正排索引中获取 content 字段的值,并高亮显示匹配的词项 "fast"。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "match": {
            "content": "fast"
          }
        },
        "highlight": {
          "fields": {
            "content": {}
          }
        }
      }
  6. 存储效率

    • 列式存储:Doc Values 将字段值按列存储,便于压缩和高效访问。
    • 支持压缩:Doc Values 支持多种压缩算法(如 LZ4、DEFLATE),减少存储空间。
  7. 适合数值和关键字字段

    • 高效访问:数值和关键字字段的值可以直接从 Doc Values 中获取,而不需要分词或倒排索引。

    • 支持范围查询:正排索引支持高效的数值范围查询(如 age > 30)。

    • 示例:

      • 在下面这个例子中,Elasticsearch 会从 Doc Values 中获取 age 字段的值,并查找大于等于 30 的文档。
      bash 复制代码
      GET /my_index/_search
      {
        "query": {
          "range": {
            "age": {
              "gte": 30
            }
          }
        }
      }
2.4 正排索引的局限性

尽管正排索引非常有用,但它也有一些局限性:

  1. 存储开销:
    • 完整文档存储:正排索引存储了文档的所有字段值,对于包含大量字段或大字段(如长文本)的文档,存储开销较大。
    • 冗余存储:如果文档中有大量重复的字段值(如枚举类型的字段),正排索引会重复存储这些值,导致存储空间浪费。
  2. 内存占用:
    • 字段数据缓存:正排索引中的字段值(尤其是文本字段)需要加载到内存中以支持快速检索,这可能导致内存占用过高。
    • 堆内存压力:Elasticsearch 依赖 JVM 堆内存,正排索引的字段数据缓存可能占用大量堆内存,增加垃圾回收(GC)的压力。
  3. 不适合全文搜索:
    • 文本字段检索效率低:正排索引存储的是字段的原始值,无法直接支持高效的全文搜索(如模糊查询、短语查询等)。
  4. 更新开销
    • 文档更新效率低:正排索引以文档为单位存储字段值,更新文档时需要重新写入整个文档,效率较低。
    • 段合并开销:更新操作会生成新的段,旧段会被标记为删除,定期合并段会增加系统开销。
  5. 不适合高基数字段
    • 高基数字段:对于高基数(Cardinality)字段(如用户 ID、IP 地址等),正排索引的存储和检索效率较低。
    • Doc Values 限制:虽然 Doc Values 支持高基数字段的聚合和排序,但其存储和计算开销仍然较大。
  6. 字段类型限制
    • 不支持嵌套对象的高效存储:正排索引对嵌套对象(Nested Objects)的支持有限,嵌套对象的存储和检索效率较低。
    • 大字段性能问题:对于大字段(如长文本、二进制数据),正排索引的存储和检索性能较差。
  7. 分布式环境下的局限性
    • 分片管理:正排索引分布在多个分片上,查询时需要从多个分片获取字段值,增加了网络开销和延迟。
    • 副本同步:正排索引的副本需要与主分片保持同步,增加了写入开销。
  8. 不适合实时分析
    • 实时计算性能不足:正排索引主要用于存储和检索字段值,对于实时分析(如复杂聚合、机器学习特征提取)的支持有限。
  9. 字段数据加载延迟
    • 延迟加载:正排索引的字段数据通常是按需加载的,首次访问字段时可能会有延迟。
    • 冷数据性能问题:对于不常访问的字段,正排索引的性能可能较差。
  10. 存储格式的局限性
    • 行式存储的局限性:正排索引采用行式存储,对于需要频繁访问某些字段的场景(如聚合分析),效率不如列式存储。
    • 压缩效率有限:虽然正排索引支持压缩,但对于某些字段类型(如稀疏字段),压缩效率较低。
2.5 正排索引的用途

正排索引主要用于以下场景:

  1. 文档内容展示:
    • 根据文档 ID 快速获取文档的原始内容。
    • 例如,在搜索结果中显示文档的标题和摘要。
  2. 支持字段检索:
    • 在搜索完成后,正排索引用于快速返回文档的特定字段值。
    • 例如,搜索完成后,返回文档的 title 和 author 字段。
  3. 支持聚合分析
    • 正排索引中的字段值用于支持聚合操作,如统计、分组、计算等。
    • 例如,对 year 字段进行聚合,统计每年的文档数量。
  4. 支持排序
    • 正排索引中的字段值用于对搜索结果进行排序。
    • 例如,按 year 字段对搜索结果进行降序排序。
  5. 支持脚本计算:
    • 正排索引中的字段值可以在脚本中使用,用于自定义评分或计算。
    • 例如,使用脚本计算文档的评分。
  6. 支持高亮显示:
    • 正排索引中的字段值用于高亮显示搜索结果中的匹配词项。
    • 例如,高亮显示 title 字段中的匹配词项。
  7. 支持字段值过滤:
    • 正排索引中的字段值用于过滤搜索结果。
    • 例如,过滤出 year 字段值为 2023 的文档。
  8. 支持嵌套对象:
    • 正排索引可以存储嵌套对象的字段值,支持对嵌套对象的检索和聚合。
  9. 支持多字段检索:
    • 正排索引可以存储多个字段的值,支持同时检索多个字段。
    • 例如,同时检索 title 和 author 字段。
2.6 正排索引的优化

为了提高正排索引的性能和效率,Elasticsearch 采用了多种优化技术。以下是正排索引的主要优化措施:

  1. Doc Values 列式存储
    • 优化目标:提高聚合、排序和脚本计算的性能。
    • 实现方式:
      • 将字段值按列存储,而不是按行存储。
      • 列式存储更适合批量读取和计算,减少 I/O 开销。
    • 适用场景:
      • 数值字段、日期字段、关键字字段等。
  2. 字段数据缓存(Field Data Cache)
    • 优化目标:加速字段数据的访问。
    • 实现方式:
      • 将正排索引中的字段值缓存到内存中,减少磁盘 I/O。
      • 适用于频繁访问的字段。
    • 注意事项:
      • 对于高基数字段(如文本字段),缓存可能占用大量内存。
  3. 压缩技术
    • 优化目标:减少存储空间,提高 I/O 性能。
    • 实现方式:
      • 使用高效的压缩算法(如 LZ4)压缩字段值。
      • 对于稀疏字段(如枚举类型字段),使用差值编码(Delta Encoding)减少存储空间。
    • 适用场景:
      • 文本字段、数值字段、日期字段等。
  4. 延迟加载(Lazy Loading)
    • 优化目标:减少内存占用,提高资源利用率。
    • 实现方式:
      • 正排索引的字段值按需加载,而不是一次性加载到内存中。
      • 对于不常访问的字段,延迟加载可以减少内存占用。
    • 适用场景:
      • 大字段(如长文本)、冷数据字段。

三、倒排索引 和 正排索引的区别

在 Elasticsearch 中,倒排索引(Inverted Index) 和 正排索引(Forward Index) 是两种不同的数据结构,分别用于支持不同的搜索和检索功能。以下是它们的核心区别:

  1. 定义与用途

    特性 倒排索引(Inverted Index) 正排索引(Forward Index)
    定义 存储词项到文档的映射关系,用于快速定位包含某个词项的文档。 存储文档到字段值的映射关系,用于快速返回文档的原始内容。
    主要用途 支持全文搜索、短语查询、模糊查询等。 支持字段检索、聚合分析、排序、脚本计算等。
  2. 数据结构

    特性 倒排索引(Inverted Index) 正排索引(Forward Index)
    存储方式 词项到文档的映射(Term → Document)。 文档到字段值的映射(Document → Field Values)。
    核心组成部分 -词典(Term Dictionary) -倒排列表(Posting List) - 文档 ID(DocID) - 字段值(Field Values)
  3. 存储内容

    特性 倒排索引(Inverted Index) 正排索引(Forward Index)
    存储内容 - 词项(Term) - 文档 ID(DocID) - 词频(TF) - 位置信息(Positions) - 偏移量(Offsets) - 文档 ID(DocID) - 字段值(Field Values)
  4. 查询方式

    特性 倒排索引(Inverted Index) 正排索引(Forward Index)
    查询方式 通过词项查找包含该词项的文档。 通过文档 ID 查找文档的字段值。
  5. 适用场景

    特性 倒排索引(Inverted Index) 正排索引(Forward Index)
    适用场景 - 全文搜索 - 短语查询 - 模糊查询 - 高亮显示 - 字段检索 - 聚合分析 - 排序 - 脚本计算
  6. 性能优化

    特性 倒排索引(Inverted Index) 正排索引(Forward Index)
    优化技术 - 压缩技术(如 FST、FOR、RLE) - 缓存机制 - 段合并 - Doc Values 列式存储 - 字段数据缓存 - 压缩技术
相关推荐
zxhl09271 小时前
Kafka--常见问题
大数据·分布式·kafka
Elasticsearch3 小时前
Elastic 和 Tines 合作提供 SOAR 和 AIOps
elasticsearch
白衣神棍4 小时前
如何借助es的snapshot跨集群迁移部分索引
elasticsearch
Chanson4 小时前
SparkSQL常见语法的实现原理
大数据
豪越大豪5 小时前
豪越科技:融合低空经济的消防一体化安全管控解决方案
大数据·运维·安全
zru_96027 小时前
Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务
spring boot·后端·elasticsearch
youka1509 小时前
大数据学习栈记——HBase操作(shell & java)
大数据·学习·hbase
三块钱079411 小时前
【原创】通过S3接口将海量文件索引导入elasticsearch
大数据·elasticsearch·搜索引擎·go
观测云16 小时前
使用外部事件检测接入 CDH 大数据管理平台告警
大数据·告警
Faith_xzc16 小时前
存算分离是否真的有必要?从架构之争到 Doris 实战解析
大数据·数据库·数据仓库·架构·开源