Elasticsearch倒排索引、文档值、存储和原文

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 本文介绍Elasticsearch索引的基础概念,包括倒排索引(inverted index)、文档值(doc_values)、存储(store)和原文(source)。

倒排索引


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 字段不需要被索引,创建索引映射时可以按以下方式禁用它:

PUTnews{
"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


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


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


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


PUTmy_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 字段会导致占用更多的存储空间。如果业务上不需要存储原始文档,可以按以下方式禁用它:


PUTmy-index-000001{
"mappings": {
"_source": {
"enabled": false    }
  }
}


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



包含/排除部分字段


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


PUTlogs{
"mappings": {
"_source": {
"includes": [
"*.count",
"meta.*"      ],
"excludes": [
"meta.description",
"meta.other.*"      ]
    }
  }
}
PUTlogs/_doc/1{
"requests": {
"count": 10,
"foo": "bar"  },
"meta": {
"name": "Some metric",
"description": "Some metric description", 
"other": {
"foo": "one", 
"baz": "two"    }
  }
}
GETlogs/_search{
"query": {
"match": {
"meta.other.foo": "one"    }
  }
}


相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
7天前
|
JSON 自然语言处理 算法
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
DSL查询文档、RestClient查询文档、全文检索查询、精准查询、复合查询、地理坐标查询、分页、排序、高亮、黑马旅游案例
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
|
7天前
|
JSON 自然语言处理 数据库
ElasticSearch基础1——索引和文档。Kibana,RestClient操作索引和文档+黑马旅游ES库导入
概念、ik分词器、倒排索引、索引和文档的增删改查、RestClient对索引和文档的增删改查
ElasticSearch基础1——索引和文档。Kibana,RestClient操作索引和文档+黑马旅游ES库导入
|
20天前
|
存储 搜索推荐 API
探究:Elasticsearch 文档的 _id 是 Lucene 的 docid 吗?
【8月更文挑战第31天】在深入探索Elasticsearch(简称ES)这一强大的搜索引擎时,了解其底层存储机制——特别是与Lucene的关系,对于优化查询性能、设计高效的数据模型至关重要。其中,一个常见且容易引发误解的问题便是:Elasticsearch中文档的_id字段是否直接等同于Lucene的docid?本文将通过图文并茂的方式,详细剖析这一问题,帮助读者理解两者之间的微妙关系。
31 0
|
1月前
|
JSON 测试技术 API
黑马商城 Elasticsearch从入门到部署 RestClient操作文档
这篇文章详细介绍了如何使用Java的RestHighLevelClient客户端与Elasticsearch进行文档操作,包括新增、查询、删除、修改文档以及批量导入文档的方法,并提供了相应的代码示例和操作步骤。
|
1月前
|
JSON 自然语言处理 Java
Elasticsearch从入门到部署 文档操作 RestAPI
这篇文章详细介绍了Elasticsearch中文档的增删改查操作,并通过Java的RestHighLevelClient客户端演示了如何通过REST API与Elasticsearch进行交云,包括初始化客户端、索引库的创建、删除和存在性判断等操作。
|
1月前
|
消息中间件 监控 数据挖掘
Elasticsearch 使用误区之二——频繁更新文档
【8月更文挑战第15天】在大数据与搜索技术日益成熟的今天,Elasticsearch 作为一款分布式、RESTful 风格的搜索与数据分析引擎,凭借其强大的全文搜索能力和可扩展性,成为了众多企业和开发者的首选。然而,在使用 Elasticsearch 的过程中,一些常见的误区可能会导致性能下降或数据不一致等问题,其中“频繁更新文档”便是一个不容忽视的误区。本文将深入探讨这一误区的根源、影响及解决方案,帮助读者更好地利用 Elasticsearch。2
47 0
|
1月前
|
自然语言处理 Java 索引
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
28 0
|
2月前
|
存储 缓存 自然语言处理
【Elasticsearch】Elasticsearch倒排索引详解
【Elasticsearch】Elasticsearch倒排索引详解
134 12
|
2月前
|
存储 SQL 自然语言处理
Elasticsearch 索引与文档的常用操作总结二:复杂条件查询
Elasticsearch 索引与文档的常用操作总结二:复杂条件查询
66 0
|
2月前
|
JSON API 数据格式
Elasticsearch 索引与文档的常用操作总结一
Elasticsearch 索引与文档的常用操作总结一
32 0