建es索引_ElasticSearch索引原理一卡通

我们先看下面这组关于动物的文章在行式数据库中的存储表结构如下:

61bd7f140463a4e69afc3e60f9d033a8.png

如果在常用的关系型数据中比如MYSQL INODB模式下 ID和标题建立了索引。那么我们查询”动物知识A“或者知道ID要查询整行记录,那么数据库会通过你创建的索引 B+TREE 去找到相应的数据。B-Tree/B+Tree 是结合磁盘的读取特性(顺序读/随机读)来提升数据读取效率的存储结构,它可以减少磁盘寻道次数,将多个值作为一个数组通过连续区间存放,每个区域(通常叫做page,可通过参数innodb_page_size设置页的大小)是一个节点,每次读取都以page为单位,这样一次寻道读取多个数据,同时也降低了树的高度(这点我们可以看出“标题”并不适合做索引,因为这个字段比较大,会造成树的层级过多,所以有时候我们对长文本字段进行索引的时候可以同时生成他们的MD5值然后对MD5值做索引),可以大大的提升查询的效率,这也是为什么索引查询速度会比较快的原因,也告诉我们用那些区分度不高的字段,比如男、女这种做索引是没太大意义的。

好啦,B+TREE的索引方案看起来很完美但是也存在一些不足之处。比如我们要在这些记录里找与丹顶鹤相关的记录就很难了,必须要进行全表扫描。又比如当数据量非常大的时候,随着标题索引的树形层级的增加,索引查询的效率也会明显降低。那么ES是如何解决这些问题的呢?我们先看下ES中数据的存储方式:

662f1675a7b9618b34bdc12f10415aa6.png

看了这个或许你会说,不是都差不多吗?只是做了分词,按照查询的关键词做了索引而已,真的会更快吗?而且这样建索引会不会很浪费资源?在回答这些问题前我们先看下ES的索引的几个概念:

3110a0648338dcfa7fbf343a98b559f5.png

1、term和Posting List

Elasticsearch分别为每个field都建立了一个倒排索引,丹顶鹤, 白天鹅 这些叫term,而[1,2]就是Posting List。。Posting list就是一个int的数组,存储了所有符合某个term的文档id。

2、Term Dictionary

Elasticsearch为了能快速找到某个term,将所有的term排个序,二分法查找term,logN的查找效率,就像通过字典查找一样,这就是Term Dictionary。现在再看起来,似乎和传统数据库通过B+Tree的方式类似啊,为什么说比B+Tree的查询快呢?那是因为ES在Term Dictionary之上还引入了另外一种索引结构:Term Index

B+Tree通过减少磁盘寻道次数来提高查询性能,Elasticsearch也是采用同样的思路,直接通过内存查找term,不读磁盘,但是如果term太多,term dictionary也会很大,放内存不现实,于是有了Term Index,就像字典里的索引页一样,A开头的有哪些term,分别在哪页,可以理解term index是一棵前缀颗树:

8ae4ebff6c17124f698821942f0d9689.png

这棵树不会包含所有的term,它包含的是term的一些前缀。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。term index不需要存下所有的term,而仅仅是他们的一些前缀与Term Dictionary的block之间的映射关系,所以能大大的减少索引的大小,提升查询效率。索引示意图如下:

17e7ac2bcacd3781ec7f4710b83044ff.png

好了,上面已经大概解释了为什么ES查询会更快的问题,那么剩下的关键就是如何避免索引对存储资源的消耗,前缀树固然能从一定程度上减少了存储资源的占用,但是在面对海量数据的时候这还远远不够,ES在这方面又有什么神来之笔呢?

首先ES采用了FST(Finite State Transducers - 有穷状态机)的压缩技术,可以使term index缓存到内存中。从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘随机读的次数。FST有两个优点:1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;2)查询速度快。O(len(str))的查询时间复杂度。FST技术在很多领域都有广泛应用,特别是在字典树构建方面,比如双tire树就是运用了FST技术的一种前缀树,是进行关键词过滤的一个最佳利器。

下面简单描述下FST的构造过程。我们对“cat”、 “deep”、 “do”、 “dog” 、“dogs”这5个单词进行插入构建FST(注:必须已排序)。

1)插入“cat”

插入cat,每个字母形成一条边,其中t边指向终点。

970a2da4c77f4c5846443263a7b87dfd.png

2)插入“deep”

与前一个单词“cat”进行最大前缀匹配,发现没有匹配则直接插入,P边指向终点。

d281914b4c43d772ca7ddce76907d6df.png

3)插入“do”

与前一个单词“deep”进行最大前缀匹配,发现是d,则在d边后增加新边o,o边指向终点。

61838292c9682ace7f736d01486faf28.png

4)插入“dog”

与前一个单词“do”进行最大前缀匹配,发现是do,则在o边后增加新边g,g边指向终点。

5)插入“dogs”

与前一个单词“dog”进行最大前缀匹配,发现是dog,则在g后增加新边s,s边指向终点。

789b078be0051af40ecfec7fce0e2bc8.png

最终我们得到了如上一个有向无环图。利用该结构可以很方便的进行查询,如给定一个term “dog”,我们可以通过上述结构很方便的查询存不存在,甚至我们在构建过程中可以将单词与某一数字、单词进行关联,从而实现key-value的映射。

Elasticsearch里除了上面说到用FST压缩term index外,对posting list也有压缩技巧。这种技巧就是增量编码压缩,将大数变小数,按字节存储。首先,Elasticsearch要求posting list是有序的,这样做的一个好处是方便压缩,看下面这个图例:

db5bbebe49f2a4a408d9a6b857964049.png

原理就是通过增量,将原来的大数变成小数仅存储增量值,再精打细算按bit排好队,最后通过字节存储。这种技术可以大大减少了索引文件的大小,也一定程度上提升了查询的效率。所以选择有规律的ID很重要,随机性太大的ID(比如java的UUID)不利于查询,如果ID是顺序的,或者是有公共前缀等具有一定规律性的ID,压缩比会比较高;另外一个因素: 可能是最影响查询性能的,应该是最后通过Posting list里的ID到磁盘中查找Document信息的那步,因为Elasticsearch是分Segment存储的,根据ID这个大范围的Term定位到Segment的效率直接影响了最后查询的性能,如果ID是有规律的,可以快速跳过不包含该ID的Segment,从而减少不必要的磁盘读次数。

好了,上面讲了ES的查询的原理,但ES之所以能广泛的应用在大数据搜索、分析等领域中还依赖其完美的水平扩展能力。对于大多数的数据库而言,通常需要对应用程序进行非常大的改动,才能利用上横向扩容的新增资源。 与之相反的是,ElastiSearch天生就是 分布式的。具体的原理是什么,ES分部式集群部署和应用中有哪些坑点?请听下回分解。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值