首页 > 其他分享 >再谈elasticsearch下的深度分页

再谈elasticsearch下的深度分页

时间:2023-05-07 19:32:25浏览次数:42  
标签:search 分页 after 再谈 查询 elasticsearch es size


Elasticsearch 在业务系统中使用也越来越广,一些开发规范也需要慢慢重视起来。 我们知道在关系型数据库中,我们被告知要注意甚至被明确禁止使用深度分页,在es中也应该尽量避免使用深度分页。

es提供的分页查询是通过fromsize参数来完成,from默认是0,size默认为10,比如:

{ "from" : 100000, "size" : 50, "query" : { "term" : { "user" : "alex" } } }

注: from+size不能大于 index.max_result_window 的默认设置10000

回归mysql

当我们使用深度分页,此时 select * from base_product_shop_sap limit 100000,50 时就会出现慢查询,它相当于先遍历了前100000个,然后取了第100000到100050个,舍弃了前100000个。 通常我们的优化方式是依赖覆盖索引( 只遍历索引本身-- 查询的列均是索引字段 ) ,而表现形式也不外乎以下几种:

  1. 子查询,比如 : SELECT * FROM base_product_shop_sap WHERE ID > =(select id from product limit 100000, 1) limit 50
  2. join,比如: SELECT * FROM base_product_shop_sap a JOIN (select id from base_product_shop_sap limit 100000, 50) b ON a.ID = b.id

当然,这种方法的好处性能较好,可以实现快速查询,但局限性也很明显:依赖于主键的自增长特性,不适合复杂查询条件的分页逻辑。

es分页查询原理

在es中, 搜索一般包括query 和 fetch 阶段 两个阶段,关于es默认的搜索类型为 QUERY_THEN_FETCH

之前也有分享过

query 过程:首先 Client 发送一次搜索请求,node1 接收到请求,然后,node1 创建一个大小为 from + size 的优先级队列用来存结果,我们管 node1 叫 coordinating node ;然后 coordinating node将请求广播到涉及到的 shards,每个 shard 在内部执行搜索请求,然后,将结果存到内部的大小同样为 from + size 的优先级队列里,可以把优先级队列理解为一个包含 top N 结果的列表; 每个 shard 把暂存在自身优先级队列里的数据返回给 coordinating node,coordinating node 拿到各个 shards 返回的结果后对结果进行一次合并,产生一个全局的优先级队列,存到自身的优先级队列里。

fetch 过程:首先 coordinating node 发送 GET 请求到相关shards;然后 shard 根据 doc 的 _id 取到数据详情,然后返回给 coordinating node 。其中 coordinating node 优先级队列里有 from + size 个 _docId

那上面的分页在es中执行, CPU、内存、IO和网络带宽消耗非常明显, 在 query 阶段即使是每条数据只返回_docId和 _score ,这数据量也很大了 ,而且这个数据量是很多 shards 中获取的。

既然 深度分页的请求并不合理 ( 很少人为的看很后面的请求 ),因此很多公司坚持二八原则(20%的才需要深度分页) 直接限制分页,不允许深度分页。

但是, 深度分页确实存在 ,在很多情况下无法回避,因此es官方也给出了两种解决方案:scroll 和 search_after

注:也有人提到过 search_type=scan,其实在 2.1.0 系列已经被官方废弃,可参阅

scroll

scroll 其实不难理解,有点类似关系型数据库的游标,so, scroll 并不适合用来做实时搜索,而更适用于后台批处理任务(接受明显的延迟)。

scroll 会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标 scroll_id 移动,获取下一页下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。

用法这里就不展开讲,具体可参阅官方文档

缺点也和明显:

  • 不能随机地跳跃分页
  • 时效性,初始化时必须指定 scroll 参数 用于指定保存搜索结果的时间,会存在超时而失败 。通常可以通过,每次请求都要传参数 scroll来刷新搜索结果的缓存时间
  • 要关注内存空间的消耗,毕竟空间有限,可通过 nodes stats API进行监控

注: scroll 分为初始化和遍历两步,初始化时将搜索结果缓存起来,可以理解为快照,在遍历时直接从这个快照里取数据,但是一旦初始化后再对索引插入、删除、更新数据都不会影响遍历结果 

searchAfter

大概从 es 5.0版本开始,es提供了新的参数 search_after 来解决分页的性能及时效性问题,search_after 提供了一个活的游标来拉取从上次返回的最后一个请求开始拉取下一页的数据。search_after有点mysql的依赖主键id的味道,它是无状态的, 可以并行的拉取大量数据, 能用于用户的实时搜索。

用法上官方文档已经足够的详细,这里再逻辑一遍:

  • 不能随机地跳跃分页
  • 依赖排序, sort参数里必须至少使用一个唯一的字段来进行排序,推荐的做法是使用 _id字段
  • 首次查询,search_after参数可以为空字符串不能为null,下次的search_after参数是上次查询结果返回的SearchAfterResult.searchAfter

这里有个小技巧,如果搜索结果有几万条,可以通过search_after来分页完成多次查询的功能,据说排序后的查询比默认查询的方式速度更快。

form&size / scroll / search_after 性能比较

曾有es专家对该话题进行了性能比较,无非是为了证明search_after 更值得推荐使用。

【1 - 10】【49000 - 49010】【 99000 - 99010】范围各10条数据(前提10w条)

 

1~10

49000~49010

99000~99010

form/size

8ms

30ms

117ms

scroll

7ms

66ms

36ms

search_after

5ms

8ms

7ms

尽管性能是非功能性需求,但它带来的挑战值得每个人去探索。

标签:search,分页,after,再谈,查询,elasticsearch,es,size
From: https://blog.51cto.com/alex/6252377

相关文章

  • Elasticsearch介绍
    ..参考老刘博客https://www.cnblogs.com/liuqingzheng/p/16002298.html..产生背景#1.1大规模数据如何检索如:当系统数据量上了10亿、100亿条的时候,我们在做系统架构的时候通常会从以下角度去考虑问题:1)用什么数据库好?(mysql、oracle、mongodb、hbase…)2)如何解决单点故......
  • 再谈降本增效:降本有可能,增效不确定
    降本手段一招鲜,增效方法吃遍天。互联网行业里,降本策略千奇百怪,手段却出奇一致;增效方法五花八门,手段更是花里胡哨。 对于企业来说,商业的基本形式,就是围绕供需产生的利益关系。 很多决策的执行,都是基于利益最大化考虑的。 什么是利益最大化? 更低的成本、更高的效率......
  • 再谈降本增效:降本有可能,增效不确定
    降本手段一招鲜,增效方法吃遍天。互联网行业里,降本策略千奇百怪,手段却出奇一致;增效方法五花八门,手段更是花里胡哨。 对于企业来说,商业的基本形式,就是围绕供需产生的利益关系。 很多决策的执行,都是基于利益最大化考虑的。 什么是利益最大化? 更低的成本、更高的效率......
  • 再谈降本增效:降本有可能,增效不确定
    降本手段一招鲜,增效方法吃遍天。互联网行业里,降本策略千奇百怪,手段却出奇一致;增效方法五花八门,手段更是花里胡哨。 对于企业来说,商业的基本形式,就是围绕供需产生的利益关系。 很多决策的执行,都是基于利益最大化考虑的。 什么是利益最大化? 更低的成本、更高的效率......
  • Django高级之-分页器
    目录分页推导queryset对象的切片参数数据总页数获取循环看需要展示几个li推导分页的原理代码终极大法自定义分页器封装代码自定义分页器使用后端前端分页推导分页的几个参数:当前第几页总数据量有多少(从数据库中查询出来)每页展示20条(自己规定的)总数据量/每页展示的条数......
  • 前端自定义分页
    <el-tablesize="mini"stripeborderfitheight="406px"highlight-current-row:header-cell-style="{background:'#F0F3F8'}":data="historyResearch......
  • linux 下安装和使用Elasticsearch8+php的操作
    首先安装Elasticsearch8版本不需要jdk环境,就是JVAV的环境,他本身的压缩文件里就包含了固定的jdk去官网1、下载Elasticsearch的官方地址:https://www.elastic.co/cn/下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch我这里下载的是elasticsearch-8.......
  • python操作elasticsearch 记录
    一、环境Elasticsearch5.x, python3.6, 注意Elasticsearch不同版本的区别,比如以下几条:_id 字段变为 id 字段:在Elasticsearch5中,文档的唯一标识符使用 _id 字段。而在Elasticsearch6和7中,唯一标识符改为使用 id 字段。在Elasticsearch7中,_id字段被重新引......
  • django分页器
    目录一、分页器思路二、自定义分页器的使用一、分页器思路分页器主要听处理逻辑代码最后很简单推导流程 1.queryset支持切片操作(正数) 2.研究各个参数之间的数学关系 每页固定展示多少条数据、起始位置、终止位置 3.自定义页码参数 current_page=request.GET......
  • django的web项目中重定向页面时的部分信息传输——以删除含有分页的列表记录为例
    问题:在管理系统界面往往是有分页的,初次编写列表的删除功能时很可能会出现删完之后页面跳转到第一页的问题,或者筛选完之后删除某一项结果删完之后跳转到未筛选页面。与实际分页管理的效果大庭相径。解决思路:1.起初只遇到了分页的情况,解决方法很简单,在删除按钮的href中直接传值,例......