之前我们已经了解过,Elasticsearch 是一个基于 Lucene 实现的分布式全文检索引擎,其实 Elasticsearch 倒排索引就是 Lucene 的倒排索引。数据检索是 ES 的一项核心功能,它的底层实现也是离不开倒排索引的,通过倒排索引技术可以提高数据的检索效率,理解倒排索引的原理很重要。
那什么是倒排索引,我们该如何理解它呢?
我们能进行数据检索的前提条件是,已经创建好了索引库,并给里边添加了文档数据。所以我们可以按照创建索引库
、添加文档
、数据检索
这个顺序来认识倒排索引。
1、
首先是创建索引库,我们之前已经安装好了 IK 分词器,这里我们创建一个test
索引,它只有一个content
字段,添加文档时字段的分词模式是ik_max_word
,检索时关键字的分词模式是ik_smart
:
PUT test
{
"mappings": {
"properties": {
"content":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
字段的分词模式会影响最终生成的倒排索引。不了解分词器的可以参考Elasticsearch 中文分词器插件。
2、
创建好了索引,我们来添加一条文档数据:
POST test/_doc/1
{
"content": "长安十二时辰"
}
添加文档数据时,ES 会根据字段的分词模式将字段的值拆分成多个词条
(Term)(或者称作词项),创建索引库时我们指定了content
字段分词模式为ik_max_word
,则会生成如下的词条:
词条 |
---|
长安 |
十二时 |
十二 |
时辰 |
接下来就是建立倒排索引了,在这之前我们先了解两个概念词条字典
(Term Dictionary)、倒排列表
(Posting List):
- 词条字典,就是词条的集合,不会出现重复词条,同时其中的词条会被排序。
- 倒排列表,由多个
倒排列表项
组成,倒排列表项记录了词条对应的文档id、词条在对应文档字段的出现频率、出现的位置以及偏移量等信息。
ES 的倒排索引就是由词条字典
和倒排列表
两部分组成的。如下就是一个简易版的倒排索引,倒排列表项只有词条对应的文档 id:
词条 | 倒排列表项 |
---|---|
长安 | [1] |
十二时 | [1] |
十二 | [1] |
时辰 | [1] |
一个词条对应一个倒排索引项。ES 会给每个字段都建立一个倒排索引。
我们再添加一条文档数据:
POST test/_doc/2
{
"content": "十二生肖"
}
根据上边的原理,最终content
字段的倒排索引会被更新成如下结构:
词条 | 倒排列表项 |
---|---|
长安 | [1] |
十二时 | [1] |
十二 | [1, 2] |
时辰 | [1] |
十二生肖 | [2] |
生肖 | [2] |
3、
前边已经添加了文档数据,同时也生成了倒排索引,接下来就是检索数据了。在这之前还有一个知识点需要了解,那就是词条索引
(Term Index),词条索引一般只存储各个词条
的前缀(第一个字符),它和字条字典对应。之所以需要词条索引,是因为词条字典
一般都很大,不适合保存在内存中而是存储在磁盘中,检索数据时根据关键字的前缀匹配到词条索引,再根据词条索引定位到关键字在倒排索引的词条字典中大致的位置,然后进一步在词条字典中通过二分查找定位到具体的词条,这样避免了直接遍历词条字典来点位词条,大幅减少了磁盘的读取,提高了效率。
定位到了词条,就能在倒排索引中找到对应的倒排列表项,进而就知道了对应的文档 id,有了文档 id 自然也就找到了文档,这也就是 ES 检索数据大致的原理。
如下我们查询包含十二
的文档数据:
GET test/_search
{
"query": {
"match": {
"content": "十二"
}
}
}
由于我们创建索引库时指定了检索时关键字的分词模式是ik_smart
,所以十二
被分词后还是十二
,再结合上边的原理,以十二
为关键字最终可以查询到 id 为 1、2 的文档数据:
这篇最好能结合Elasticsearch 中文分词器插件一起看,这样能更容易理解些。
新手上路,不合理的地方还望大佬指点。
网友评论