【导读】 一篇非常好的博客,我就是根据这个实现的
https://blog.csdn.net/andybegin/article/details/83864171
search_after的理念是,在不同分片上(假设有5个分片),先按照指定顺序排好,根据我们传的search_after值 ,然后仅取这个值之后的size个文档。这 5*size 个文档拿到Es内存中排序后,返回前size个文档即可。避免了浅分页导致的内存爆炸情况,经实际使用性能良好,ES空闲状态下查询耗时稳定在50ms以内,平均10~20ms。
为了每次文档排序的一致性,每个文档必须有一个全局唯一值,一般采用 _id 。
啥意思呢?想象一个场景,假设我们仅按照create_time倒序排列,如果doc1和doc2的create_time相同,那么完全可能第一次排序是doc1在前,第二次排序doc2在前。①
在取第n页时,es排序结果是(……,doc1 ,doc2,……),我们取走了(……,doc1),那么第n+1页是不是应该取走(doc2,……),
可是再次排序结果可能变了,变成了(……,doc2 ,doc1,……),那么doc2就漏了。
怎么解决这个问题呢?
就是用create_time+_id 进行排序,create_time可能相同,但是_id全局唯一,各个文档按照_id排序每次排序结果都是一样的,这就保证了每次排序都是一致的,不会出现①的情况。
【实现方案】
1、es 指定sort后,每个返回的文档中都会添加一个sort数组,如图,es 就是拿这个数组的组合值去排序的,我们称之为 游标。
1.png因此代码里做的第一个改动是,将这个sort数组暴露出来,塞到返回值中,记为sortColumns字段 。
然后拿每页最后一个文档的sortColumns作为下一页的search_after,就可以取排在这个文档之后的文档。
2、添加search_after 参数,当有search_after参数时,在查询体里塞一个search_after,query 变成这样,注意sort的列和search_after的列要一一对应。
5.png
这样才能用search_after 去 切割 排序结果。
网友评论