在了解了es的存储之后,我们再来看看es的搜索过程。
因为插入的文档是被分散存储到不同分片上的,所以搜索采用两阶段查询方式,
query阶段 和 fetch阶段。
我们在发起搜索请求的时候,query命令被发到任意一个节点上,这个节点就是这个请求的协调节点:
再由协调节点决定把请求分发给哪些分片:
52.png
这时不区分是主分片还是副本分片,因为不管是什么样的分片,都是lucene实例,都可以提供搜索功能,这也是副本越多系统吞吐量越高的原因,每个分片都执行同一个query,在本地生成一个长度为from+size的优先队列(priority queue ):
53.png
优先队列自身就实现了排序功能,然后各个分片的队列返回给协调节点:
54.png
协调节点合拿到有分片返回的优先队列之后,进行汇总:
55.png
比如搜索条件里设置{ "from"是 90, "size"是 10 },那么在获取到100条数据后,把前90个id丢掉,只留后10个id:
56.png
因为Query阶段是轻量级的并不需要获取真实的数据,只需要获取满足条件的【doc id列表】即可,这样即使有100万个文档,但是获取的只是doc id列表并不需要占用多少内存。
有了doc id之后,进入fetch阶段:
fetch阶段通过query阶段的返回的doc id列表,由协调节点发起多个get请求到多个分片上,同样不区分主副分片,在收到每个分片返回的结果后,由协调节点进行组装并返回给客户端,这样就完成了整个搜索操作。
搜索速度的快慢,是由倒排索引的效率决定的,所以为了更快的搜索,es需要把磁盘里的倒排索引加载到内存中进行操作:
然而存储这么多倒排索引很费内存,所以es对倒排索引又做了一次压缩,然后再放入内存,他使用的是FST(Finite State Transducers)压缩方法,最后生成一个term index(分词的索引)放到内存。
FST压缩方法是这样的,比如:
有这样几个字符串,中国联通 中国移动 中国电信,正常存储是12个字符:
59.png
但是如果这样存储:
60.png
就可以少存储两个“中国”,这样就实现了压缩。
然后每个节点存一个这个分词的倒排索引在磁盘的具体位置,这样搜索时首先在term index中进行查找,然后再加载具体数据:
61.png
这样整体查询速度提升了,内存的占用也减少了。
网友评论