我们知道ES的查询是非常快的,且每个索引的数据是存储在多个分片中的,查询压力负载到多个分片机器上,最后汇合查询结果,减轻了机器压力同时加快了速度。
但是当用户分页获取查询结果时,ES又是怎么获得全局的排序并取得分页结果呢?
这不光是ES会遇到的问题,所有的分布式存储数据排序都会有这个问题。
逐步分析
假设我们的索引有5个分片,每个分片上10万个文档数据
获取1~10条
我们知道单个节点数据都在本地,排序是很方便的。我们很容易将各个节点数据进行排序,分别取排序为1~10的文档数据,然后汇总从5(分片数) * 10(单片文档数)=50
条数据中获得1~10的文档。
获取11~20条
一个误区:有人会想当然的认为从上一个步骤中获取的50
条数据中,直接取出11~20即为结果。他们觉得这些数据都是各个分区的前10组成,那么组成的结果理所当然的就是所有数据中排名的前50。
这是不对的。因为你只是取了各分片的前10条数据,它们只在各自分片下排序有用,无法证明这10条就一定比其它分片的11~n条数据大。
了解了误区,类别第一步,我们很容易知道应该获取5(分片数) * 20(单片文档数)=100
条数据,然后整体排序,取得第11~20条数据。
获取第pageNumber页数据
由前两步可知: 当我们想获取分页数据时: es需要总共获得: (pageNumber * pageSize * 分片数)
条数据,然后汇总排序,再取得所需结果。
如果pageNumber 和 pageSize 非常大, es每个分片所需获取的数据就会非常多,网络带宽以及后续的汇总会占用很多资源,严重影响性能。
因此使用es非常不建议进行深分页
数据汇总
我们知道es从各个节点总共获取到(pageNumber * pageSize * 分片数)
条数据,需要排序取得总排名为1
~pageNumber * pageSize
的数据,然后再取其最后10条数据。
如何处理排序呢?
构建一个长度为(pageNumber * pageSize * 分片数)
的数组,然后调用排序算法将整体排序,然后再获取结果即可。
可以但不可行。但pageNumber或pageSize或分片数,任意一个参数变大时,总数组长度增加的太快,太耗费内存。
使用优先队列:
许多时候我们需要处理有序的元素,但不一定要求他们全部有序,或是不一定要一次就将他们排序,这种时候就需要优先队列这种数据结构。
优先队列需要支持两种操作:删除最大元素和插入元素
效果:可从不间断的输入数据中,始终筛选出当前输入数据中指定长度的最大的(或最小的)元素。
主分片创建优先队列,将本节点的符合条件的数据存入优先队列中,汇总其它节点数据时,只需将数据一个一个输入到优先队列中,队列会自动完成过滤,非常方便,也无需构建那么长的数组。