百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

图解Elasticsearch神一般的索引机制,此篇带你领悟新世界

toyiye 2024-06-21 11:57 9 浏览 0 评论

前言

随着Elastic的上市,ELK不仅在互联网大公司得到长足的发展,而且在各个中小公司都得到非常广泛的应用,甚至连"婚庆网站"都开始使用Elasticsearch了。随之而来的是 Elasticsearch 相关部署、框架、性能优化的文章早已铺天盖地。

因为ES家族团队已在设计阶段做了大量的设计,所以整个ELK家族的组件使用起来非常容易上手,对于初学者甚至会进入幻觉—:"一键部署、导入数据、检索&聚合、动态扩展",这些都是如此简单。

但,实际上呢?仅就 Elasticsearch 索引设计,请回答如下几个问题:

  • 倒排索引是什么?
  • 有限状态转换的机制是什么?
  • 对于亿级数据,ES是如何做到毫秒级甚至秒级回复,它有哪些独特的原理?

本文和大家一起深入探讨一下Elasticsearch 索引设计。

相比于大多数人熟悉的MySQL数据库的索引,Elasticsearch的索引机制是完全不同于MySQL的B+Tree结构。索引会被压缩放入内存用于加速搜索过程,这一点在效率上是完爆MySQL数据库的。但是Elasticsearch会对全部text字段进行索引,必然会消耗巨大的内存,为此Elasticsearch针对索引进行了深度的优化。在保证执行效率的同时,尽量缩减内存空间的占用。

为了方便,以下统一用ES来代替Elasticsearch。

简介

便于未曾接触过ES的人有个大致了解,这里简单介绍下ES

elasticsearch是一个基于Lucene库的开源搜索引擎,它提供分布式的实时文件存储和搜索,可扩展性好,并且支持通过HTTP网络接口交互,数据以JSON格式展示。

Elasticsearch因为其极快的搜索和聚合速度通常被应用在各种搜索应用中,比如在自己的app里面加一个搜索框或者分析实时日志(ELK系统)。

Elasticsearch会对所有输入的文本进行处理,建立索引放入内存中,从而提高搜索效率。在这一点上ES要优于MySQL的B+树的结构,MySQL需要将索引放入磁盘,每次读取需要先从磁盘读取索引然后寻找对应的数据节点,但是ES能够直接在内存中就找到目标文档对应的大致位置,最大化提高效率。并且在进行组合查询的时候MySQL的劣势更加明显,它不支持复杂的组合查询比如聚合操作,即使要组合查询也要事先建好索引,但是ES就可以完成这种复杂的操作,默认每个字段都是有索引的,在查询的时候可以各种互相组合。

索引

首先需要申明的是在ES中索引的概念和MySQL里面的概念不太一样,这里列出一下ES和MySQL的对应的概念,方便大家理解。

MySQLES库(database)索引(index)表(table)类型(type)行(row)文档(doc)列(column)字段(field)

在ES中每个字段都是被索引的,所以不会像MySQL中那样需要对字段进行手动的建立索引。

ES在建立索引的时候采用了一种叫做倒排索引的机制,保证每次在搜索关键词的时候能够快速定位到这个关键词所属的文档。

倒排索引

举例说明:

比如有三句话在数据库中:

  1. Winter is coming
  2. Ours is fury
  3. Ths choice is yours

如果没有倒排索引(Inverted Index),想要去找其中的choice,需要遍历整个文档,才能找到对应的文档的id,这样做效率是十分低的,所以为了提高效率,我们就给输入的所有数据的都建立索引,并且把这样的索引和对应的文档建立一个关联关系,相当于一个词典。当我们在寻找choice的时候就可以直接像查字典一样,直接找到所有包含这个数据的文档的id,然后找到数据。

Lucene在对文档建立索引的时候,会给词典的所有的元素排好序,在搜索的时候直接根据二分查找的方法进行筛选就能够快速找到数据。不过是不是感觉有点眼熟,这不就是MySQL的索引方式的,直接用B+树建立索引词典指向被索引的文档。

ES做的要更深一点,ES希望把这个词典“搬进”内存,直接从内存读取数据不就比从磁盘读数据要快很多吗!问题在于对于海量的数据,索引的空间消耗十分巨大,直接搬进来肯定不合适,所以需要进一步的处理,建立词典索引(term index)。通过词典索引可以直接找到搜索词在词典中的大致位置,然后从磁盘中取出词典数据再进行查找。所以大致的结构图就变成了这样:

ES通过有限状态转化器,把term dictionary变成了term index,放进了内存,所以这个term index 究竟是怎么样的?

有限状态转换

有限状态转换器(Finite State Transducers)相当于是一个Trie前缀树,可以直接根据前缀就找到对应的term在词典中的位置。

比如我们现在有已经排序好的单词mop、moth、pop、star、stop和top以及他们的顺序编号0..5,可以生成一个下面的状态转换图:

当遍历上面的每一条边的时候,都会加上这条边的输出,比如当输入是stop的时候会经过s/3和o/1,相加得到的排序的顺序是4;而对于mop,得到的排序的结果是0

但是这个树并不会包含所有的term,而是很多term的前缀,通过这些前缀快速定位到这个前缀所属的磁盘的block,再从这个block去找文档列表。为了压缩词典的空间,实际上每个block都只会保存block内不同的部分,比如mop和moth在同一个以mo开头的block,那么在对应的词典里面只会保存p和th,这样空间利用率提高了一倍。

使用有限状态转换器在内存消耗上面要比远比SortedMap要少,但是在查询的时候需要更多的CPU资源。维基百科的索引就是使用的FST,只使用了69MB的空间,花了大约8秒钟,就为接近一千万个词条建立了索引,使用的堆空间不到256MB。

顺带提一句,在ES中有一种查询叫模糊查询(fuzzy query),根据搜索词和字段之间的编辑距离来判断是否匹配。在ES4.0之前,模糊查询会先让检索词和所有的term计算编辑距离筛选出所有编辑距离内的字段;在ES4.0之后,采用了由Robert开发的,直接通过有限状态转换器就可以搜索指定编辑距离内的单词的方法,将模糊查询的效率提高了超过100倍。

现在已经把词典压缩成了词条索引,尺寸已经足够小到放入内存,通过索引能够快速找到文档列表。现在又有另外一个问题,把所有的文档的id放入磁盘中会不会占用了太多空间?如果有一亿个文档,每个文档有10个字段,为了保存这个posting list就需要消耗十亿个integer的空间,磁盘空间的消耗也是巨大的,ES采用了一个更加巧妙的方式来保存所有的id。

文档列表(Posting list)

所谓的posting list,就是所有包含特定term文档的id的集合,需要从词典映射到这个集合。由于整型数字integer可以被高效压缩的特质,integer是最适合放在posting list作为文档的唯一标识的,ES会对这些存入的文档进行处理,转化成一个唯一的整型id

在存储数据的时候,在每一个shard里面,ES会将数据存入不同的segment,这是一个比shard更小的分片单位,这些segment会定期合并。在每一个segment里面都会保存最多2的31次幂个文档。每个文档被分配一个唯一的id,从0到2的31次幂。

帧引用(Frame of Reference)

在进行查询的时候经常会进行组合查询,比如查询同时包含choice和the的文档,那么就需要分别查出包含这两个单词的文档的id,然后取这两个id列表的交集;如果是查包含choice或者the的文档,那么就需要分别查出posting list然后取并集。为了能够高效的进行交集和并集的操作,posting list里面的id都是有序的。同时为了减小存储空间,所有的id都会进行delta编码(delta-encoding,我觉得可以翻译成增量编码

比如现在有id列表[73, 300, 302, 332, 343, 372],转化成每一个id相对于前一个id的增量值(第一个id的前一个id默认是0,增量就是它自己)列表是[73, 227, 2, 30, 11, 29]。在这个新的列表里面,所有的id都是小于255的,所以每个id只需要一个字节存储。

实际上ES会做的更加精细,它会把所有的文档分成很多个block,每个block正好包含256个文档,然后单独对每个文档进行增量编码,计算出存储这个block里面所有文档最多需要多少位来保存每个id,并且把这个位数作为头信息(header)放在每个block 的前面。这个技术叫Frame of Reference,我翻译成索引帧。

比如对上面的数据进行压缩(假设每个block只有3个文件而不是256),压缩过程如下:

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

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

Roaring Bitmap

ES会缓存频率比较高的filter查询,其中的原理也比较简单,即生成(fitler, segment)和id列表的映射,但是和倒排索引不同,我们只把常用的filter缓存下来而倒排索引是保存所有的,并且filter缓存应该足够快,不然直接查询不就可以了。ES直接把缓存的filter放到内存里面,映射的posting list放入磁盘中。

ES在filter缓存使用的压缩方式和倒排索引的压缩方式并不相同,filter缓存使用了roaring bitmap的数据结构,在查询的时候相对于上面的Frame of Reference方式CPU消耗要小,查询效率更高,代价就是需要的存储空间(磁盘)更多。

Roaring Bitmap是由int数组和bitmap这两个数据结构改良过的成果——int数组速度快但是空间消耗大,bitmap相对来说空间消耗小但是不管包含多少文档都需要12.5MB的空间,即使只有一个文件也要12.5MB的空间,这样实在不划算,所以权衡之后就有了下面的Roaring Bitmap。

  1. Roaring Bitmap首先会根据每个id的高16位分配id到对应的block里面,比如第一个block里面id应该都是在0到65535之间,第二个block的id在65536和131071之间
  2. 对于每一个block里面的数据,根据id数量分成两类
  • 如果数量小于4096,就是用short数组保存
  • 数量大于等于4096,就使用bitmap保存

在每一个block里面,一个数字实际上只需要2个字节来保存就行了,因为高16位在这个block里面都是相同的,高16位就是block的id,block id和文档的id都用short保存。

至于4096这个分界线,因为当数量小于4096的时候,如果用bitmap就需要8kB的空间,而使用2个字节的数组空间消耗就要少一点。比如只有2048个值,每个值2字节,一共只需要4kB就能保存,但是bitmap需要8kB。

总结

ES为了提高搜索效率、优化存储空间做了很多工作。

为了能够快速定位到目标文档,ES使用倒排索引技术来优化搜索速度,虽然空间消耗比较大,但是搜索性能提高十分显著。

由于索引数量巨大,ES无法直接把全部索引放入内存,转而建立词典索引,构建有限状态转换器(FST)放入内存,进一步提高搜索效率。

数据文档的id在词典内的空间消耗也是巨大的,ES使用了索引帧(Frame of Reference)技术压缩posting list,带来的压缩效果十分明显。

ES的filter语句采用了Roaring Bitmap技术来缓存搜索结果,保证高频filter查询速度的同时降低存储空间消耗。

以上为全部内容。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码