ES 源码分析(3)from size, scroll 和 search after
前两天突然被业务部的同事问了一句:“我现在要做搜索结果全量导,该用哪个接口,性能要好的?”之前虽然是知道这三种方法都是可以做分页的深度查询,但是由于具体的代码实现细节没看过,因此心里一下子就没有了底气,只好回答说先看看。
from size
from size是家喻户晓的,也是暴力的,需要查询from + size 的条数时,coordinate node就向该index的其余的shards 发送同样的请求,等汇总到(shards * (from + size))条数时在coordinate node再做一次排序,终抽取出真正的 from 后的 size 条结果,所以from size 的源码也懒得过了,这里只是顺带提一下。实在没弄明白Elasticsearch的from size机制的必须先做功课,下面的文章带图,通俗易懂:
http://lxwei.github.io/posts/%E4%BD%BF%E7%94%A8scroll%E5%AE%9E%E7%8E%B0Elasticsearch%E6%95%B0%E6%8D%AE%E9%81%8D%E5%8E%86%E5%92%8C%E6%B7%B1%E5%BA%A6%E5%88%86%E9%A1%B5.html
所以说当索引非常大时(千万级或亿级)时是无法用这个方法做深度分页的(启用routing机制可以减少这种中间态的条数,降低 OOO的风险,看是始终不是长远之计,而且性能风险摆在那里。
search after
这是Elasticsearch 5 新引入的一种分页查询机制,其实原理几乎就是和scroll一样,因此代码也是几乎一样的, 简单三句话介绍search after怎么用就是:
- 它必须先要指定排序(因为一定要按排序记住坐标)
- 必须从页开始搜起(你可以随便指定一个坐标让它返回结果,只是你不知道会在全量结果的何处)
- 从页开始以后每次都带上search_after=lastEmittedDocFieldValue 从而为无状态实现一个状态,说白了就是把每次固定的from size偏移变成一个确定值lastEmittedDocFieldValue,而查询则从这个偏移量开始获取size个doc(每个shard 获取size个,coordinate node后汇总 shards*size 个。
后一点非常重要,也就是说,无论去到多少页,coordinate node向其它node发送的请求始终就是请求size个docs,是个常量,而不再是from size那样,越往后,你要请求的docs就越多,而要丢弃的垃圾结果也就越多 也就是,如果我要做非常多页的查询时,起码search after是一个常量查询延迟和开销,并无什么副作用。 有人就会问,为啥每次提供一个search_after值就可以找到确定的那一页的内容呢,Elasticsearch 不是分布式的么,每个shard只维护一部分的离散的文档,其实这个我之前也没搞懂,自从群上一小伙扔我一干货后就秒懂了,这里也推荐大家先做做功课,看看目前一些分库分表的数据查询的方式方法: 业界难题-“跨库分页”的四种方案 如果你实在懒得看完,我就贴出search_after 的实现原理吧,如下:
三、业务折衷法 “全局视野法”虽然性能较差,但其业务无损,数据精准,不失为一种方案,有没有性能更优的方案呢? “任何脱离业务的架构设计都是耍流氓”,技术方案需要折衷,在技术难度较大的情况下,业务需求的折衷能够极大的简化技术方案。 业务折衷一:禁止跳页查询 在数据量很大,翻页数很多的时候,很多产品并不提供“直接跳到指定页面”的功能,而只提供“下一页”的功能,这一个小小的业务折衷,就能极大的降低技术方案的复杂度。
相关文章