大规模数据量下ES如何实现高性能检索?

这篇具有很好参考价值的文章主要介绍了大规模数据量下ES如何实现高性能检索?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

ElasticSearch,是基于Lucene库的搜索引擎。它提供了一个分布式、多租户的全文搜索引擎,具有HTTP web接口和无模式JSON文档。根据DB引擎排名,Elasticsearch是最受欢迎的企业搜索引擎。ES的特点是分布式、高扩展以及近实时。那么,大规模数据量下ES是如何实现高性能检索的呢?

倒排索引

说到ES,就不得不说倒排索引了。我们平时通过ES进行模糊查询,其底层原理就依赖于倒排索引。

首先来介绍一下正向索引与倒排索引的区别:

  1. 正向索引:用户发起查询时(假设查询为一个关键词),搜索引擎会依次扫描索引库中的所有文档,找出所有包含该关键词的文档。
  2. 倒排索引:用户发起查询时(假设查询为一个关键词),搜索引擎会在关键词到文档id的映射中,找到所有包含该关键词的文档id,然后找到对应的文档。

假设需要对下面两个工地的信息进行倒排索引的创建:

  1. 文档id为001,工地名称为“北京海淀花园小区”;
  2. 文档id为002,工地名称为“北京朝阳星空小区”。

首先,ES将文档交给分析器进行处理,处理的过程包括字符过滤、分词和分词过滤,最终的处理结果是文档内容被表示为一系列关键词信息(关键词本身以及它在文档中出现的位置信息和词性信息)的集合。如下图所示。大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

其次,ES根据分析结果建立文档-词语矩阵,用以表示词语和文档的包含关系,如图所示。

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

文档-词语矩阵建立完成之后,接着需要建立基于词语的倒排索引。ES会遍历文档词语矩阵中的每一个词语,然后将包含该词语的文档信息与该词语建立一种映射关系。映射关系中的词语集合叫作Term Dictionary。映射中的文档集合信息不仅包含文档id,还包含词语在文档中的位置和词频信息,包含这些文档信息的结构叫作Posting List。对于一个规模很大的文档集合来说,可能包含几十万甚至上百万的词语集合,能否快速定位某个词语,直接影响搜索时的响应速度。因此需要一种高效的数据结构对映射关系中的词语集合进行索引,这种结构叫作Term Index。上述3种结构结合在一起就构成了ES的倒排索引结构,逻辑关系如图所示。

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

本例中的倒排索引结构如图所示。

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

Term Index 的组织形式

在前一小节我们说到,需要一种高效的数据结构对映射关系中的词语集合也就是Term Dictionary进行索引,这种结构叫作Term Index。那么,Term Index是以怎样的形式进行组织的呢?

最简单的理解,Term Index就像一本词典的目录一样,但是实际上Term可以是任意的byte数组,而不仅仅是英文字符,因此实际的Term Index是一棵Trie Tree:

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

这棵Trie Tree不会包含所有的term,它包含的是term的一些前缀。通过Term Index可以快速地定位到Term Dictionary的某个offset,然后从这个位置再往后顺序查找。再加上一些压缩技术比如FST,Term Index需要的存储空间非常小,可能只有所有term占用空间的几十分之一,因此Term Index被完全缓存在内存中是没有问题的。

使用了Term Index后,ES的倒排索引整体效果如下:

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

在数据量比较大的情况下,如果在MySQL中想要检索到一条记录,即使建立了索引,但索引是以B+ Tree的形式存储在磁盘上的,因此需要若干次的random access的磁盘操作。而ES在Term Dictionary(相当于MySQL中的索引)的基础上,又添加了Term Index这一结构,并且直接以Trie Tree的形式缓存在内存中。在检索一个term时,只需要从Term Index查找到对应的Term Dictionary的block的位置,再到磁盘上去访问对应的数据,大大减少了对磁盘的random access操作的次数。因此我们可以说,在数据量比较大的情况下,ES的检索比MySQL快得多。

使用FST压缩Term Index

在前一小节我们说到,Term Index要被直接缓存在内存中以提升查找速度,那么就必然要采用某种结构来压缩Term Index。Term Index在内存中就是以FST(Finite State Transducers,有限状态转换机)的形式来存储的。FST相对于Trie Tree来说,唯一的不同是Trie Tree只共享了前缀,而FST不仅共享了前缀,也共享了后缀。FST的数据结构可以理解成key -> value的形式,其中key是term,value是对应的Term Dictionary的block的位置。经典FST算法要求key必须按照字典序从小到大加入到FST中。

FST具有两大优点:

  1. 空间占用小:通过对前缀和后缀的重复利用,压缩了存储空间;
  2. 查询速度快:时间复杂度为O(len(str));

说到FST,就不得不提FSM(Finite State Machines,有限状态机):表示有限个状态集合以及这些状态之间转移和动作的数学模型。其中一个状态被标记为开始状态,0个或多个状态被标记为final状态。一个FSM同一时间只处于一个状态。

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

在图中,椭圆形中代表的是状态,而箭头则代表了转移。图中这个FSM代表了对人的一天之内活动的一个抽象,但是未标注开始状态与结束状态。人在同一时间只能处于一种状态,并且从一个状态转移到下一个状态需要一个输入。如果在这个FSM中,人的初始状态为“睡觉”,然后接收的输入依次为:睡醒了、吃饱了、累了、有新电影上映、困了,那么这个人的状态就会依次变化:睡觉 → 吃饭 → 工作 → 逛街 → 睡觉。

前面说到,FST的数据结构可以理解成key -> value的形式,且经典FST算法要求key必须按照字典序从小到大加入到FST中,因此FST其实是一种Ordered Map结构,其key是有序的。
为了方便理解,我们先使用一个DAFSA(Deterministric Acyclic Finite State Acceptor,确定无环有限状态接收机)来实现一个Ordered Set。然后在此基础上,再使用FST来实现一个Ordered Map。

DAFSA的特性如下:

  1. 确定:意味着指定任何一个状态,只可能最多有一个转移可以访问到;
  2. 无环: 不可能重复遍历同一个状态;
  3. 接收机:有限状态机只“接受”特定的输入序列,并终止于final状态;
    如果只有一个元素:“bei”,DAFSA是这样的:大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

如果要查询“bei”是否属于该集合,只需要按照字符依次输入:

  1. 输入”b“,DAFSA的状态从0到1;
  2. 输入”e“,DAFSA的状态从1到2;
  3. 输入”i“,DAFSA的状态从2到3;

这时,该DAFSA已经到达final状态3,因此“bei”是属于该集合的;

如果要查询“bao”是否处于该集合,在输入“a”的时候,DAFSA会在状态1无法继续移动,因此可以判断”bao“不属于该集合;

查找某个key是否存在于该集合中的时间复杂度为O(length(key));

现在我们往这个DAFSA里面再添加一个元素:”kay“,DAFSA会变成这样:大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

start状态0此时有了两个转移:“b”和“k”。

如果我们再添加一个元素“ke",DAFSA会变成这样:
大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

接下来我们再看一下由“october“,“november“和”december“构成的DAFSA:

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

其中,“october“,“november“和”december“共有的后缀“ber”在DAFSA中只出现了一次;“november“和”december“共有的后缀“ember”在DAFSA中也只出现了一次;

接下来,我们用一个DAFST(Deterministric Acyclic Finite State Transducer,确定无环有限状态转换器)来实现一个Ordered Map。DAFST的特性如下:

  1. 确定:意味着指定任何一个状态,只可能最多有一个转移可以访问到;
  2. 无环: 不可能重复遍历同一个状态;
  3. 转换器:接受特定的输入序列,终止于final状态,同时会输出一个值;

DAFST与DAFSA的区别在于,对于一个指定的key,DAFST不仅可以判断其是否存在,还可以输出一个与之关联的value,因此可以用于构建一个Ordered Map。

如果只有一个元素:“bei”,其对应的value为5,DAFST是这样的:

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

如果要查询“bei”,只需要按照字符依次输入:

  1. 输入”b“,DAFST的状态从0到1,value = 5;
  2. 输入”e“,DAFST的状态从1到2,value = 5 + 0 = 5;
  3. 输入”i“,DAFST的状态从2到3,value = 5 + 0 = 5;

这时,该DAFST已经到达final状态3,因此“bei”是属于该集合的,且对应的value为5;

现在我们往这个DAFST里面再添加一个元素:”kay“,其对应value为7,DAFST会变成这样:大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

如果我们再添加一个元素“ke",其对应value为9,DAFST会变成这样:

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

我们可以发现,状态4到状态3,多了一个转移“e”,其weight为2;这样就可以使“kay”和“ke“共用从状态0经过转移“k”到状态4的weight,同时保证能够分别找到自己的正确的值。

建立FST之后,就可以很轻松地根据一个key找到对应的value了。例如:查找“kay”,我们查找的路径为:0 → 4 → 5 → 3,“kay”这个term对应的Term Dictionary的block的位置就是7 + 0 + 0 = 7;

再举一个FST同时复用前缀和后缀的例子:

我们按照如下的key -> value构建一个如下的FST:

  • “aaaaa” -> 1
  • “aaber” -> 2
  • “bbaaa” -> 3
  • “bbbbb” -> 4
  • “bbbcr” -> 5
  • “bbber” -> 6

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索
可以看到,“aaaaa”对应的转移路径为:0 -> 1 -> 2 -> 3 -> 4 -> 5,对应的value为1 + 0 + 0 + 0 + 0 = 1;

“bbbcr”对应的转移路径为:0 -> 8 -> 9 -> 10 -> 7 -> 5,对应的value为:3 + 0 + 1 + 1 + 0 = 5;

“bbber”对应的转移路径同样为:0 -> 8 -> 9 -> 10 -> 7 -> 5,但是在10 -> 7的时候,走的转移为“e”,对应的value为:3 + 0 + 1 + 2 + 0 = 6;

因此,ES通过使用FST对Term Index进行前缀和后缀的重复利用,在最大限度上降低了检索的时间复杂度和空间复杂度,查找某个key的时间复杂度为O(length(key));

使用Frames of Reference 压缩 Posting List

Elasticsearch里除了用FST压缩Term Index外,对Posting List也有压缩技巧。 可能有同学会问了,Posting List不是已经只存储文档id和offset这些信息了吗?应该已经很小了呀?还需要压缩?让我们试想一下,如果Elasticsearch需要对同学的性别进行索引会怎样?如果有上千万个同学,而只有“男/女“这样两个性别,每个Posting List都会至少包含数百万个文档id。 这时候是不是需要压缩了呢?

那么Elasticsearch是如何有效地对这些文档id压缩的呢?

答案是Frames Of Reference。

比如,文档列表包含[73, 300, 302, 332, 343, 372],经过Delta-encode之后,得到[73, 227, 2, 30, 11, 29]。Lucene用的就是这个技术,会对Posting List中的文档id进行编码,将每256个文档id分成一组,每个组分别使用Delta-encode压缩,然后计算该组中每个数字所需要的存储空间大小,存储到该组的元信息(头信息)中。这种编码方式就叫做Frame Of Reference (FOR) ,从Lucene 4.1开始沿用。

下图为Frames Of Reference的示例(将每3个文档id分成一组):

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

该图中的左边的block,最大的delta是227,最接近的2的整数次幂是256(8bits),于是规定这个block里的每一个数字都用8bits来编码;右边的block,最大的delta是30,最接近的2的整数次幂是32(5bits),于是规定这个block里都用5bits来编码。

在返回结果的时候,其实也并不需要把所有的数据直接解压然后全部返回,而是可以返回一个iterator ,然后通过iterator的next方法逐一取出被压缩的文档id,这样可以极大地节省算力和内存开销。

通过以上的方式可以极大的节省posting list的空间消耗,提高查询性能。不过ES为了提高filter过滤器查询的性能,还做了更多的工作,那就是缓存。

使用Roaring Bitmaps缓存常用filter查询的结果

在使用ES的时候,常常会使用filter查询。ES会缓存一些频率比较高的filter查询的结果。但是这个缓存与Posting List不同:

  1. 因为只需要缓存那些常用的filter查询的结果,因此压缩率并不是要考虑的首要条件;
  2. 因为需要这些filter查询的结果比真正执行filter查询要快,所以使用好的数据结构很重要;
  3. 缓存的常用的filter查询的结果是保存在内存中的,Posting List是保存在磁盘的;

Roaring Bitmap 是由 Integer Array 和 Bitmap 这两个数据结构改良过的成果——Integer Array 速度快但是空间消耗大,Bitmap相对来说空间消耗小,但是存储空间随着文档个数线性增长。

Roaring Bitmap首先会根据每个id的高16位分配id到对应的block里面,比如第一个block里面id应该都是在0到65535之间,第二个block的id在65536和131071之间,以此类推。

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

对于每一个block里面的数据,如果文档id数量小于4096,就用 Integer Array 保存,文档id数量大于等于4096,就使用 Bitmap 保存。

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

为什么要设定一个4096的阈值呢?其实很简单,因为如果文档id数量小于4096,我们直接使用Integer Array,每个文档id需要2 bytes来存储(在每一个block里面,一个数字实际上只需要2 bytes 来保存就行了,因为高16位在这个block里面都是相同的),总的存储空间小于8192 bytes;而如果要使用Bitmap,因为每个block的大小是65536,因此所需Bitmap的大小是65536 bits 也就是8192 bytes;所以在文档id数量小于4096时直接使用 Integer Array 保存就可以了;

通过对Posting List取交集实现联合索引

在ES中,不需要特意添加联合索引,就可以快速检索同时满足多个条件的文档。
假如有三个查询关键词,我们会先得到每个关键词对应的Posting List,如下图所示:

大数据量检索,ES,NoSQL,数据结构与算法,elasticsearch,搜索引擎,全文检索

然后对所有的Posting List取交集。

如果使用跳表,可以对最短的 Posting List 中的每个id,逐个在另外的Posting List中查找,判断其是否存在,最后得到交集的结果。

如果使用位图,可以直接进行按位与运算,得到的结果就是最后的交集。

然后根据交集中的文档id去找真正的文档就可以了。

总结

回到我们的文章标题,大规模数据量下ES是如何实现高性能检索的呢?文章来源地址https://www.toymoban.com/news/detail-590254.html

  1. ES通过分词然后对每一个单词及其对应文档建立倒排索引,使得能够快速根据关键词找到对应文档id;
  2. 建立了 Term Index,并通过FST压缩,将其缓存在内存中,能够高效地对映射关系中的 Term Dictionary 进行索引;
  3. 使用 Frames of Reference 对 Posting List 进行压缩,使其不至于太大,并且在返回结果的时候,返回一个iterator ,然后通过iterator的next方法逐一取出被压缩的文档id,提升查询性能;
  4. 使用 Roaring Bitmaps 将常用filter查询的结果直接缓存到内存中;
  5. 通过对多个查询条件对应的 Posting List 取交集实现联合索引;

到了这里,关于大规模数据量下ES如何实现高性能检索?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 虚拟现实与虚拟现实:如何实现大规模多用户互动

    虚拟现实(Virtual Reality, VR)是一种使用计算机生成的3D环境来模拟或扩展现实世界的技术。它通过使用特殊的头戴式显示器和输入设备,让用户感觉自己在一个完全不同的环境中。虚拟现实技术的应用范围广泛,包括游戏、娱乐、教育、医疗、军事等领域。 虚拟现实与虚拟现实

    2024年02月20日
    浏览(46)
  • Flink与Cassandra:如何在大规模数据处理中存储与管理数据

    作者:禅与计算机程序设计艺术 1.1. 背景介绍 随着大数据时代的到来,数据处理的需求也越来越大。在实际工作中,我们常常需要处理海量数据,如何高效地存储与管理数据成为了我们必须面对的问题。 1.2. 文章目的 本文旨在探讨如何在大型数据处理环境中使用 Flink 和 Ca

    2024年02月13日
    浏览(56)
  • MinHash-LSH 哈希模糊去重:如何解决医学大模型的大规模数据去重?

      问题:训练医学大模型的数据规模真的很大,其中会夹杂很多重复数据。 重复数据对于大模型微调也有较大影响,数据集必须去重后再用于模型训练。 临床数据: 20 亿条文本数据 教材数据: 1000+ 本指南 7万+ 药品说明书 N 个科室疾病培训数据 N 本古籍、教材 … 开源数据

    2024年01月19日
    浏览(45)
  • 【天衍系列 01】深入理解Flink的 FileSource 组件:实现大规模数据文件处理

    Apache Flink 是一个流式处理框架,被广泛应用于大数据领域的实时数据处理和分析任务中。在 Flink 中,FileSource 是一个重要的组件,用于从文件系统中读取数据并将其转换为 Flink 的数据流。本文将深入探讨 FileSource 的工作原理、用法以及与其他数据源的比较。 FileSource 是 Fli

    2024年02月21日
    浏览(52)
  • Spring Boot与Apache Kafka实现高吞吐量消息处理:解决大规模数据处理问题

    现代数据量越来越庞大对数据处理的效率提出了更高的要求。Apache Kafka是目前流行的分布式消息队列之一。Spring Boot是现代Java应用程序快速开发的首选框架。综合使用Spring Boot和Apache Kafka可以实现高吞吐量消息处理。 Apache Kafka采用分布式发布-订阅模式具有高度的可扩展性和可

    2024年02月05日
    浏览(52)
  • 高防服务器如何抵御大规模攻击

    高防服务器如何抵御大规模攻击?高防服务器是一种专门设计用于抵御大规模攻击的服务器,具备出色的安全性和可靠性。在当今互联网时代,网络安全问题日益严重,DDOS攻击(分布式拒绝服务攻击)等高强度攻击已成为威胁企业和组织网络安全的重要问题。为了保护网站和

    2024年02月09日
    浏览(52)
  • etcd实现大规模服务治理应用实战

         导读 :服务治理目前越来越被企业建设所重视,特别现在云原生,微服务等各种技术被更多的企业所应用,本文内容是百度小程序团队基于大模型服务治理实战经验的一些总结,同时结合当前较火的分布式开源kv产品etcd,不仅会深入剖析ectd两大核心技术Raft与boltdb的实

    2024年02月12日
    浏览(47)
  • 分布式光伏发电大规模应用,运维难题如何解?

    国家能源局数据显示,2022年我国光伏新增装机达 87.4GW,同比+59%,其中:集中式装机达36.29GW,同比+41.8%; 分布式装机达51.11GW,同比+207.9%,已连续两年超过集中式电站。 近年来,随着“双碳”行动方案的实施和“整县开发试点”工作的推进,以“就地开发、就近利用”为主

    2024年02月02日
    浏览(47)
  • 如何解决大规模并行计算中的线性代数问题

    作者:禅与计算机程序设计艺术 对大型矩阵运算而言,由于矩阵的元素之间的关系非常复杂,因此当运算过程中涉及到矩阵乘法、行列转置等运算时,通常采用并行化的方法进行加速处理。目前,主要的并行化技术包括基于硬件的多核CPU并行化技术、分布式集群并行化技术、

    2024年02月14日
    浏览(43)
  • 利用Python进行大规模数据处理

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 随着数据量的不断增长,大规模数据处理变得越来越重要。在这个领域,Hadoop和Spark是两个备受关注的技术。本文将介绍如何利用Python编程语

    2024年04月24日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包