有一位学长突然问我你知道为啥es比数据库mysql查询的快吗?之前对于es的印象就是高效的搜索框架,实际应用过,但是没想过原理,后来查了一些资料,现在做一下总结。

希望还是小伙伴们可以了解一些es的知识之后再来看这一个文章吧。

es为何查询速度快?
es采用的是倒序索引,这种索引方式和我们的正常索引不太一样,我们以一张表来说明,看一下对比:

idagesexname
118female北大
220male河北大学青年
318male爱国青年


在mysql中,是以id简历b+树索引,然后通过目录页对应到数据页,然后找到数据。对于传统的增删改查(用id)没有任何问题,速度也很快,但是对于全文检索来说,就很尴尬。比如查询like %北大%。这样是走不到索引的,需要全表扫描。但是对于es来说,这就好办多了。

倒序索引:以name为倒序索引来看。

我们是将内容进行了分词(这里是最细粒划分)。然后指向了我们document的一个唯一的标识,能够找到位置的地址。

这样,当我们在程序发出一个查询请求后,比如“北大青年”。首先会把这个查询内容分词:“北大”、“青年”。然后就找到对应的数据[1,2,3]。这三条数据了,比我们在mysql中模糊查询快的多。这是其中的一个原因。

我们将“北大”、“河北”、“大学...这样的叫做term。如果有很多个term,那么我们如何找到对应的term呢。我们以term是英文为例:假如有Carla,Sara,Elin,Ada,Patty,Kate,Selena。

第一个方法:遍历?遍历是不可能遍历的,这辈子都不可能遍历的。

第二个方法:采用二分查找(悄悄的告诉你,mysql的inndb中在目录页的查找过程中和数据页的查找对应的数据中均有体现)可以用 logN 次磁盘查找得到目标。但是磁盘的随机读操作仍然是非常昂贵的(一次random access大概需要10ms的时间)。而相比于mysql,term的dictionary要大得多。无法完整地放到内存里,于是就有了第三个方法。

第三个方法:term index。term index有点像一本字典的大的章节表。如果所有的term都是英文字符的话,可能这个term index就真的是26个英文字符表构成的了。但是实际的情况是,term未必都是英文字符,term可以是任意的byte数组。而且26个英文字符也未必是每一个字符都有均等的term,比如x字符开头的term可能一个都没有,而s开头的term又特别多。实际的term index是一棵trie 树:

 

这里只考虑前缀并不考虑完整的分词字,例子是一个包含 "A", "to", "tea", "ted", "ten", "i", "in", 和 "inn" 的 trie 树。这棵树不会包含所有的term,它包含的是term的一些前缀。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。再加上一些压缩技术(搜索 Lucene Finite State Transducers) term index 的尺寸可以只有所有term的尺寸的几十分之一,使得用内存缓存整个term index变成可能。整体上来说就是这样的效果。

 

这种方式就很快就能够查找到对应的分词,然后在对应的分词就找到了对应的主键,然后就可以直接找到对应的数据了。
 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐