inverted index,doc_values,store 及 source—Elastic Stack 实战手册

简介: Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索

970X90.png

· 更多精彩内容,请下载阅读全本《Elastic Stack实战手册》

· 加入创作人行列,一起交流碰撞,参与技术圈年度盛事吧

创作人:欧阳楚才

倒排索引

Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。

假设我们有两个文档,每个文档的正文字段包含如下内容:

  1. The quick brown fox jumped over the lazy dog
  2. Quick brown foxes leap over lazy dogs in summer

为了创建倒排索引,我们首先将每个文档的正文字段,拆分成单独的词(我们称它为词条或 Tokens),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。

结果如下所示:

词条 文档1 文档2
Quick X
The X
brown X X
dog X
dogs X
fox X
foxes X
in X
jumped X
lazy X X
leap X
over X X
quick X
summer X
the X

现在,如果我们想搜索 Quick brown,我们只需要查找包含每个词条的文档:

词条 文档1 文档2
brown X X
quick X
匹配词条数量 2 1

两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用,仅计算匹配词条数量的简单相似性算法,那么我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。

但是,我们目前的倒排索引有一些问题:

· Quick 和 quick 以独立的词条出现,然而用户可能认为它们是相同的词。

· fox 和 foxes 非常相似,就像 dog 和 dogs,他们有相同的词根。

· jumped 和 leap,尽管没有相同的词根,但他们是同义词。

使用前面的索引搜索 + Quick + fox 不会得到任何匹配文档。(记住,+ 前缀表明这个词必须存在)只有同时出现 Quick 和 fox 的文档才满足这个查询条件,但是第一个文档包含 quick fox,第二个文档包含 Quick foxes。

我们的用户可以合理的期望两个文档与查询匹配,我们可以做的更好。

如果我们将词条规范为标准模式,那么我们可以找到与用户搜索的词条不完全一致,但具有足够相关性的文档,例如:

· Quick 可以小写化为 quick

· foxes 可以词干提取 -- 变为词根的格式 -- 为 fox。类似的,dogs 可以为提取为 dog

· jumped 和 leap 是同义词,可以索引为相同的单词 jump

现在索引看上去像这样:

词条 文档1 文档2
brown X X
dog X X
fox X X
in X
jump X X
lazy X X
over X X
quick X X
summer X
the X

这还远远不够。我们搜索 +Quick +fox 仍然会失败,因为在我们的索引中,已经没有 Quick 了。但是,如果我们对搜索的字符串,使用与正文字段相同的标准化规则,会变成查询 +quick +fox,这样两个文档都会匹配。

禁用索引

默认情况下,Elasticsearch 文档每个字段都会被索引。如果某些字段不需要支持查询,可以在映射中配置 "index": false ,减少存储空间占用,并且提升写入速度。尽管这个字段不能被搜索,但是它并不妨碍做聚合(如果该字段是可以聚合的字段)。

例如,文章的标题、正文、发布时间字段,需要创建索引,文章的 url 字段不需要被索引,创建索引映射时可以按以下方式禁用它:

PUT news
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "content": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "createtime": {
        "type": "date"
      },
      "url": {
        "type": "keyword",
        "index": false
      }
    }
  }
}

文档值

在 Elasticsearch 中,Doc Values 是一种列式存储结构。Doc Values 默认对所有字段启用,除了 text 和 annotated_text 类型字段。

Doc Values 是在索引时创建的,当字段索引时,Elasticsearch 为了能够快速检索,会把字段的值加入倒排索引中,同时它也会存储该字段的 Doc Values。

Elasticsearch 中的 Doc Values 常被应用到以下场景:

· 对一个字段进行排序

· 对一个字段进行聚合

· 某些过滤,比如地理位置过滤

· 某些与字段相关的脚本计算

· 使用 docvalue_fields 返回搜索结果部分字段值

因为文档值被序列化到磁盘,可以依靠操作系统的帮助来快速访问。当数据集远小于节点的可用内存,操作系统会自动将所有的 Doc Values 保存在内存中,使得其读写十分高速;当其远大于可用内存,操作系统会自动把 Doc Values 加载到系统的页缓存中,从而避免了 JVM 堆内存溢出异常。

列式存储的压缩

Doc Values 本质上是一个序列化的列式存储,适用于聚合、排序、脚本等操作。这种存储方式也非常便于压缩,特别是数字类型,这样可以节约磁盘空间,并且提高访问速度。

来看一组数字类型的 Doc Values,了解它如何压缩数据:

文档 词条
文档1 100
文档2 1000
文档3 1500
文档4 1200
文档5 300
文档6 1900
文档7 4200

按列布局意味着我们有一个连续的数据块:[100,1000,1500,1200,300,1900,4200]。

因为我们已经知道他们都是数字,而不是像文档或行中看到的异构集合,所以我们可以使用统一的偏移来将他们紧紧排列。

而且,针对这样的数字有很多种压缩技巧。你会注意到这里每个数字都是 100 的倍数,Doc Values 会检测一个段里面的所有数值,并使用一个最大公约数,方便做进一步的数据压缩。

如果我们保存100 作为此段的除数,我们可以对每个数字都除以100,然后得到: [1,10,15,12,3,19,42]。

现在这些数字变小了,只需要很少的位就可以存储下,也减少了磁盘存放的大小。Doc Values 在压缩过程中使用如下技巧,它会按依次检测以下压缩模式:

  1. 如果所有的数值各不相同或缺失,设置一个标记并记录这些值
  2. 如果这些值小于 256,将使用一个简单的编码表
  3. 如果这些值大于 256,检测是否存在一个最大公约数
  4. 如果没有存在最大公约数,从最小的数值开始,统一计算偏移量进行编码

你会发现这些压缩模式不是传统的通用压缩算法,比如 DEFLATE 或者 LZ4。因为列式存储的结构是严格且良好定义的,我们可以通过使用专门的模式来达到,比通用压缩算法(如 LZ4)更高的压缩效果。

禁用 Doc Values

Doc Values 默认对所有字段启用,除了 text 和 annotated_text 类型字段。也就是说所有的数字、地理坐标、日期、IP 和 keyword 类型都会默认开启。

Text 类型字段不能使用 Doc Values,文本经过分析流程生成很多 Token,使得 Doc Values 不能高效运行。

因为 Doc Values 默认启用,你可以选择对你数据集里面的大多数字段,进行聚合和排序操作。如果你知道你永远也不会对某些字段进行聚合、排序或是使用脚本操作,你可以通过禁用特定字段的 Doc Values。这样不仅节省磁盘空间,也会提升索引的速度。

要禁用 Doc Values,在字段的映射 (mapping) 设置 doc_values: false 即可。例如,这里我们创建了一个新的索引,字段 "session_id" 禁用了 Doc Values:

PUT my_index
{
  "mappings": {
    "properties": {
      "session_id": {
        "type": "keyword",
        "doc_values": false
      }
    }
  }
}

通过设置 doc_values: false,这个字段将不能被用于聚合、排序以及脚本操作。

反过来也是可以进行配置的:让一个字段可以被聚合,通过禁用倒排索引,使它不能被正常搜索,例如:

PUT my_index
{
  "mappings": {
    "properties": {
      "customer_token": {
        "type": "keyword",
        "doc_values": true,
        "index": false
      }
    }
  }
}

通过设置 doc_values: true 和 index: false,我们得到一个只能被用于聚合/排序/脚本的字段。无可否认,这是一个非常少见的情况,但很有用。

存储

默认情况下,字段原始值会被索引用于查询,但是不会被存储。为了展示文档内容,通过一个叫 _source 的字段用于存储整个文档的原始值。

在字段的映射 (mapping) 设置 store: true,可以使索引单独保存这个字段。通常情况下,如果文档本身十分庞大,而一些字段又会经常单独使用,那么这样的字段,就可以设置为单独存储,然后可以使用 stored_fields 单独检索这些字段。

例如,如果你的文档包含标题、时间和一个很大的正文字段,你可能只需要检索标题、时间字段,没必要从很大的 _source 原文中解析出这些字段:

#创建索引,指定常用字段store属性
PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "store": true 
      },
      "date": {
        "type": "date",
        "store": true 
      },
      "content": {
        "type": "text"
      }
    }
  }
}

#插入记录
PUT /my-index-000001/_doc/1
{
  "title":   "短文本标题",
  "date":    "2021-05-01",
  "content": "很长很长很长的正文字段..."
}

#查询结果返回stored_fields指定字段
GET /my-index-000001/_search
{
  "stored_fields": [ "title", "date" ] 
}

注意:stored_fields 返回结果是数组格式。如果你需要获取原始文档,可以通过_source字段替代。

原文

_source 字段包含索引时发送的原始 JSON 文档。_source 字段本身不建索引,但是存储原始文档,以便在执行查询请求时,可以将其返回。可以通过设置,禁用原文字段,或者只存储特定字段。

_source 在 Lucene 中是映射为一个特殊的字段:

Field Index IndexType Analyzer DocValues Store
_source No No No No Yes

Elasticsearch 中 _source 字段的主要目的,是通过 doc_id 读取该文档的原始内容,所以只需要存储 Store 即可。

Elasticsearch 中使用 _source 字段可以实现以下功能:

Update:

部分更新时,需要从读取文档保存在 _source 字段中的原文,然后和请求中的部分字段合并为一个完整文档。如果没有 _source,则不能完成部分字段的 Update 操作。

Reindex:

可以通过 Reindex API 完成索引重建,过程中不需要从其他系统导入全量数据,而是从当前文档的 _source 中读取。如果没有 _source,则不能使用 Reindex API。

Script:

不管是 Index 还是 Search 的 Script,都可能用到存储在 Store 中的原始内容,如果禁用了 _source,则这部分功能不再可用。

Summary:

摘要信息也是来源于 _source 字段。

禁用 _source

尽管使用非常方便,但是 _source 字段会导致占用更多的存储空间。如果业务上不需要存储原始文档,可以按以下方式禁用它:

PUT my-index-000001
{
  "mappings": {
    "_source": {
      "enabled": false
    }
  }
}

注意:禁用 _source 会导致更新、重建索引、摘要功能不可用,生产环境慎用。考虑节省存储空间,可以通过修改索引设置 index.codec 提高压缩效率。

包含/排除部分字段

包含/排除 _source 部分字段可以按以下方式设置它:

PUT logs
{
  "mappings": {
    "_source": {
      "includes": [
        "*.count",
        "meta.*"
      ],
      "excludes": [
        "meta.description",
        "meta.other.*"
      ]
    }
  }
}

PUT logs/_doc/1
{
  "requests": {
    "count": 10,
    "foo": "bar" 
  },
  "meta": {
    "name": "Some metric",
    "description": "Some metric description", 
    "other": {
      "foo": "one", 
      "baz": "two" 
    }
  }
}

GET logs/_search
{
  "query": {
    "match": {
      "meta.other.foo": "one" 
    }
  }
}
相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。  
相关文章
|
6月前
|
监控 BI
Quick BI使用案例15:交叉表中使用多字段组合实现累计利润率计算
本文详解交叉表中使用多字段组合计算月累计利润率计算方案。通过交叉表配置日期累计、多字段组合(利润/订单金额)及动态日期控件,实现月中实时监控经营健康度,助力及时决策纠偏。
|
6月前
|
SQL BI 数据库
企业智能问数平台的真正分水岭:本体语义层与预置指标层到底差在哪?
企业智能问数平台成败关键不在大模型或界面,而在于底层数据治理逻辑:是构建“预置指标层”(稳态可控、适合成熟BI体系),还是打造“本体语义层”(弹性扩展、支撑跨域复杂分析)。选型需权衡建设成本、维护负担与长期演进能力。
|
8月前
|
安全 关系型数据库 数据库
2026年阿里云针对企业用户有哪些扶持政策?最新中小企业扶持权益解析
2026年阿里云推出多项企业扶持政策,建站方面,提供.com域名1元购、多后缀域名特惠及建站模板、营销推广服务;业务上云推出99元/年经济型e实例、199元/年通用算力型u1实例及云数据库、OSS等特惠产品;协同办公包含无影云电脑59元/3个月特价、钉钉、企业邮箱等优惠;专项补贴涵盖算力补贴(单客户上不封顶)、出海扶持金及创业者计划;企业可免费试用140余款云产品并获专业技术支持,全面降低企业上云成本,提升市场竞争力。
849 8
|
自然语言处理 JavaScript 前端开发
vue3-ts-vite:vue 项目 配置 多页面应用
vue3-ts-vite:vue 项目 配置 多页面应用
3581 0
|
机器学习/深度学习 人工智能 自然语言处理
云上一键部署通义千问 QwQ-32B 模型,阿里云 PAI 最佳实践
3月6日阿里云发布并开源了全新推理模型通义千问 QwQ-32B,在一系列权威基准测试中,千问QwQ-32B模型表现异常出色,几乎完全超越了OpenAI-o1-mini,性能比肩Deepseek-R1,且部署成本大幅降低。并集成了与智能体 Agent 相关的能力,够在使用工具的同时进行批判性思考,并根据环境反馈调整推理过程。阿里云人工智能平台 PAI-Model Gallery 现已经支持一键部署 QwQ-32B,本实践带您部署体验专属 QwQ-32B模型服务。
|
缓存 应用服务中间件 网络安全
10个顶级Linux开源反向代理服务器 - 解析与导航
10个顶级Linux开源反向代理服务器 - 解析与导航
1330 2
|
消息中间件 搜索推荐 小程序
开源埋点用户行为分析方案-ClickLog埋点(ClkLog)
ClkLog 是一款记录用户行为分析和画像的免费可商用开源软件,技术人员可快速搭建私有的应用系统。项目基于神策分析SDK,采用ClickHouse数据库对采集数据进行存储,采用前后端分离的方式来实现的访问统计和用户画像分析系统。在这里,你可以轻松看到用户访问网页、APP、小程序或业务系统的行为轨迹,同时也可以从时间、地域、渠道、用户访客类型等多维度了解用户的全方位信息,完美助力大数据用户画像、实时归因/离线归因分析、漏斗分析、大数据推荐场景。
2799 2
开源埋点用户行为分析方案-ClickLog埋点(ClkLog)
|
机器学习/深度学习 前端开发
【机器学习】机器学习30个笔试题
本文提供了一份包含30个问题的机器学习笔试试题集,覆盖了回归模型、极大似然估计、特征选择、模型评估、正则化方法、异常值检测、分类问题等多个机器学习领域的关键知识点。
1679 0
【机器学习】机器学习30个笔试题
|
存储 自然语言处理 测试技术
Llama3.1-8B模型中文版!OpenBuddy发布新一代跨语言模型
7月23日,Meta发布了新一代开源模型系列:Llama3.1。其中405B参数的版本刷新了开源模型性能的上限,在多种指标上的测试成绩接近GPT-4等闭源模型的水平,甚至在部分基准测试中展现出来了超越头部闭源模型的潜力。
|
缓存 Python
Conda太慢?试试这个加速工具!
Conda太慢?试试这个加速工具!
1559 0

热门文章

最新文章