Elasticsearch查询速度这么快的原因是什么

技术Elasticsearch查询速度这么快的原因是什么这篇文章主要介绍“Elasticsearch查询速度这么快的原因是什么”,在日常操作中,相信很多人在Elasticsearch查询速度这么快的原因是什么问题上存在疑

本文主要介绍“Elasticsearch这么快的原因是什么”。在日常操作中,我相信很多人对Elasticsearch如此快速的原因有所怀疑。边肖查阅了各种资料,整理出简单易用的操作方法,希望能帮助大家解答“Elasticsearch为什么这么快”的疑惑!接下来,请和边肖一起学习!

这甚至比使用MySQL在本地查询主键还要快。

Elasticsearch查询速度这么快的原因是什么

Elasticsearch查询速度这么快的原因是什么

为此,我搜索了相关信息:

Elasticsearch查询速度这么快的原因是什么

这类问题在网上有很多答案,大致意思如下:ES是基于Lucene的全文搜索引擎,可以对数据进行分段并保存索引,擅长管理大量的索引数据。与MySQL相比,ES不擅长频繁更新数据和关联查询。

说的不是很透彻,没有分析相关原理;但是,既然指数被反复提及,我们就从指数的角度来比较两者的区别。

MySQL 索引

先从MySQL说起。我想大家都知道索引这个词,它通常存在于一些查询场景中,是空间换时间的典型案例。以下内容以InnoDB引擎为例。

常见的数据结构

假设我们自己设计MySQL索引,有哪些选择?

散列表

我们首先应该想到的是哈希表,这是一种非常常见和高效的数据结构,用于查询和编写。与Java相对应的是HashMap。

Elasticsearch查询速度这么快的原因是什么

这种数据结构不宜过多介绍,其写入效率很高(1)。例如,当我们想要查询id=3的数据时,我们需要散列3,然后在这个数组中找到对应的位置。

但如果我们想打听1le闲置;6、哈希表不能很好的满足。因为是无序的,所以需要遍历所有数据,才能知道哪些数据属于这个区间。

有序数组

Elasticsearch查询速度这么快的原因是什么

有序数组查询也非常高效。当我们想要查询id=4的数据时,我们只需要二分搜索法高效地定位数据O(logn)。

同时,由于数据有序,自然可以支持区间查询。那么有序数组适合做索引吗?

自然是不行的,它还有另一个重大问题;假设我们插入id=2.5的数据,我们必须同时将所有后续数据移动一位,这种写入效率会变得非常低。

平衡二叉树

既然有序数组的写效率不高,那就看看写效率,很容易想到二叉树。

这里我们以平衡二叉树为例:

Elasticsearch查询速度这么快的原因是什么

由于平衡二叉树的特点,左节点小于父节点,右节点大于父节点。

因此,如果要查询id=11的数据,只需要查询10rarr12rarr11,时间复杂度为O(logn),写数据时也是O(logn)。

然而,它仍然不能很好地支持区间范围搜索。假设我们要查询5le闲置;20、您需要先查询10个节点的左子树,然后查询10个节点的右子树,最后才能查询所有数据。因此,查询效率不高。

跳表

跳转表可能没有上面提到的哈希表、有序数组、二叉树那么常见,但实际上Redis中的排序集是通过跳转表实现的。这里我们简单介绍下一跳表实现的数据结构的优点。

我们都知道,即使查询有序链表也没有效率,因为它不能使用数组下标。

进行二分查找,所以时间复杂度是 o(n)。

但我们也可以巧妙的优化链表来变相的实现二分查找,如下图:

Elasticsearch查询速度这么快的原因是什么

我们可以为最底层的数据提取出一级索引、二级索引,根据数据量的不同,我们可以提取出 N  级索引。当我们查询时便可以利用这里的索引变相的实现了二分查找。

假设现在要查询 id=13 的数据,只需要遍历 1→7→10→13 四个节点便可以查询到数据,当数越多时,效率提升会更明显。

同时区间查询也是支持,和刚才的查询单个节点类似,只需要查询到起始节点,然后依次往后遍历(链表有序)到目标节点便能将整个范围的数据查询出来。

同时由于我们在索引上不会存储真正的数据,只是存放一个指针,相对于最底层存放数据的链表来说占用的空间便可以忽略不计了。

平衡二叉树的优化

但其实 MySQL 中的 InnoDB 并没有采用跳表,而是使用的一个叫做 B+ 树的数据结构。

这个数据结构不像是二叉树那样大学老师当做基础数据结构经常讲到,由于这类数据结构都是在实际工程中根据需求场景在基础数据结构中演化而来。

比如这里的 B+ 树就可以认为是由平衡二叉树演化而来。刚才我们提到二叉树的区间查询效率不高,针对这一点便可进行优化:

Elasticsearch查询速度这么快的原因是什么

在原有二叉树的基础上优化后:所有的非叶子都不存放数据,只是作为叶子节点的索引,数据全部都存放在叶子节点。

这样所有叶子节点的数据都是有序存放的,便能很好的支持区间查询。只需要先通过查询到起始节点的位置,然后在叶子节点中依次往后遍历即可。

当数据量巨大时,很明显索引文件是不能存放于内存中,虽然速度很快但消耗的资源也不小;所以 MySQL 会将索引文件直接存放于磁盘中。

这点和后文提到 Elasticsearch 的索引略有不同。由于索引存放于磁盘中,所以我们要尽可能的减少与磁盘的 IO(磁盘 IO  的效率与内存不在一个数量级)。

通过上图可以看出,我们要查询一条数据至少得进行 4 次IO,很明显这个 IO 次数是与树的高度密切相关的,树的高度越低 IO  次数就会越少,同时性能也会越好。

那怎样才能降低树的高度呢?

Elasticsearch查询速度这么快的原因是什么

我们可以尝试把二叉树变为三叉树,这样树的高度就会下降很多,这样查询数据时的 IO 次数自然也会降低,同时查询效率也会提高许多。这其实就是 B+  树的由来。

使用索引的一些建议

其实通过上图对 B+树的理解,也能优化日常工作的一些小细节;比如为什么需要最好是有序递增的?

假设我们写入的主键数据是无序的,那么有可能后写入数据的 id 小于之前写入的,这样在维护 B+树索引时便有可能需要移动已经写好数据。

如果是按照递增写入数据时则不会有这个考虑,每次只需要依次写入即可。所以我们才会要求数据库主键尽量是趋势递增的,不考虑分表的情况时最合理的就是自增主键。

整体来看思路和跳表类似,只是针对使用场景做了相关的调整(比如数据全部存储于叶子节点)。

ES 索引

MySQL 聊完了,现在来看看 Elasticsearch 是如何来使用索引的。

正排索引

在 ES 中采用的是一种名叫倒排索引的数据结构;在正式讲倒排索引之前先来聊聊和他相反的正排索引。

Elasticsearch查询速度这么快的原因是什么

以上图为例,我们可以通过 doc_id 查询到具体对象的方式称为使用正排索引,其实也能理解为一种散列表。

本质是通过 key 来查找 value。比如通过 doc_id=4 便能很快查询到 name=jetty wang,age=20 这条数据。

倒排索引

那如果反过来我想查询 name 中包含了 li 的数据有哪些?这样如何高效查询呢?

仅仅通过上文提到的正排索引显然起不到什么作用,只能依次将所有数据遍历后判断名称中是否包含 li ;这样效率十分低下。

但如果我们重新构建一个索引结构:

Elasticsearch查询速度这么快的原因是什么

当要查询 name 中包含 li 的数据时,只需要通过这个索引结构查询到 Posting List  中所包含的数据,再通过映射的方式查询到最终的数据。

这个索引结构其实就是倒排索引。

Term Dictionary

但如何高效的在这个索引结构中查询到 li 呢,结合我们之前的经验,只要我们将 Term 有序排列,便可以使用二叉树搜索树的数据结构在 o(logn)  下查询到数据。

将一个文本拆分成一个一个独立Term 的过程其实就是我们常说的分词。

而将所有 Term 合并在一起就是一个 Term Dictionary,也可以叫做单词词典。

英文的分词相对简单,只需要通过空格、标点符号将文本分隔便能拆词,中文则相对复杂,但也有许多开源工具做支持(由于不是本文重点,对分词感兴趣的可以自行搜索)。

当我们的文本量巨大时,分词后的 Term 也会很多,这样一个倒排索引的数据结构如果存放于内存那肯定是不够存的,但如果像 MySQL  那样存放于磁盘,效率也没那么高。

Term Index

所以我们可以选择一个折中的方法,既然无法将整个 Term Dictionary 放入内存中,那我们可以为 Term Dictionary  创建一个索引然后放入内存中。

这样便可以高效的查询 Term Dictionary ,最后再通过 Term Dictionary 查询到 Posting List。

相对于 MySQL 中的 B+树来说也会减少了几次磁盘 IO。

Elasticsearch查询速度这么快的原因是什么

这个 Term Index 我们可以使用这样的 Trie 树,也就是我们常说的字典树来存放。

Elasticsearch查询速度这么快的原因是什么

如果我们是以 j 开头的 Term 进行搜索,首先第一步就是通过在内存中的 Term Index 查询出以 j 打头的 Term 在 Term  Dictionary 字典文件中的哪个位置(这个位置可以是一个文件指针,可能是一个区间范围)。

紧接着在将这个位置区间中的所有 Term 取出,由于已经排好序,便可通过二分查找快速定位到具体位置;这样便可查询出 Posting List。

最终通过 Posting List 中的位置信息便可在原始文件中将目标数据检索出来。

更多优化

当然 Elasticsearch 还做了许多针对性的优化,当我们对两个字段进行检索时,就可以利用 Bitmap 进行优化。

比如现在需要查询 name=li and age=18 的数据,这时我们需要通过这两个字段将各自的结果 Posting List 取出。

Elasticsearch查询速度这么快的原因是什么

最简单的方法是分别遍历两个集合,取出重复的数据,但这个明显效率低下。

这时我们便可使用 Bitmap 的方式进行存储(还节省存储空间),同时利用先天的位与计算便可得出结果。

[1, 3, 5] ⇒ 10101  [1, 2, 4, 5] ⇒ 11011

这样两个二进制数组求与便可得出结果:

10001 ⇒ [1, 5]

最终反解出 Posting List 为 [1, 5],这样的效率自然是要高上许多。同样的查询需求在 MySQL  中并没有特殊优化,只是先将数据量小的数据筛选出来之后再筛选第二个字段,效率自然也就没有 ES 高。

当然在最新版的 ES 中也会对 Posting List 进行压缩,具体压缩规则可以查看官方文档,这里就不具体介绍了。

总结

最后我们来总结一下:

Elasticsearch查询速度这么快的原因是什么

通过

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/49662.html

(0)

相关推荐

  • 黑曼巴英文,用英文介绍科比的黑曼巴精神

    技术黑曼巴英文,用英文介绍科比的黑曼巴精神曼巴精神focused on being in the momentMamba mentality is aconstantquest
    to find answers
    曼巴精神是

    生活 2021年10月28日
  • LINQ表达式树的示例分析

    技术LINQ表达式树的示例分析小编给大家分享一下LINQ表达式树的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!using Syste

    攻略 2021年11月23日
  • GET和POST两种基本请求方法的区别有哪些

    技术GET和POST两种基本请求方法的区别有哪些本篇内容主要讲解“GET和POST两种基本请求方法的区别有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“GET和POST两

    攻略 2021年10月27日
  • 在.NET中实现Actor模型的不同方式是怎样的

    技术在.NET中实现Actor模型的不同方式是怎样的在.NET中实现Actor模型的不同方式是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。《实现领域

    攻略 2021年11月16日
  • PostgreSQL数据库如何实现客户端验证

    技术PostgreSQL数据库如何实现客户端验证这篇文章将为大家详细讲解有关PostgreSQL数据库如何实现客户端验证,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。身份验证是数据库服

    攻略 2021年11月25日
  • Sql Server中存储过程中输入和输出参数是什么

    技术Sql Server中存储过程中输入和输出参数是什么本篇文章为大家展示了Sql Server中存储过程中输入和输出参数是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。[s

    攻略 2021年12月1日