Elasticsearch调研深度查询
1.from/size 浅分页查询
一般的分页需求我们可以使用from和size的方式实现,但是这种的分页方式在深分页的场景下应该是避免使用的。深分页的页次增加,所消耗的内存和时间的增长也是成比例的增加,为了避免深度分页产生的问题,Elasticsearch从2.0版本楷书,增加了一个限制: index.max_result_window = 10000 # elasticsearch的一种保护机制,限制1w条数据,但是可以修改最大限根据实际需求,不过最好不要修改会影响性能。
# 引入scroll from/size查询查询在10000-50000条数据(1000到5000页)以内的时候还是可以的,但是如何数据量过多的话,就会出现深分页的问题 # 为了解决以上问题,elasticsearch提出了一个scroll滚动的方式。
1.scroll的原理是讲索引数据备份,每次记住最后一次查询的位置,下一次从最后一次位置开始查询。 2.使用scroll,通过size的查询最大限制,scroll返回一个代表最后一个size查询的位置scroll_id可以不断的获取下一页的内容。
GET dlp-prd-rabbitmq-tagfile-*/_search?scroll=10m { "query": { "match_all": {} }, "sort": [{"stime": {"order": "desc"}}], "from": 0, "size": 10000 }
1.scroll=5m表示设置scroll_id保留5分钟可用。 2.使用scroll必须要将from设置为0。 3.size决定后面每次调用_search搜索返回的数量
1.然后我们可以通过数据返回的_scroll_id读取下一页内容,每次请求将会读取下10000条数据,直到数据读取完毕或者scroll_id保留时间截止 2.请求的接口不再使用索引名了,而是 _search/scroll,其中GET和POST方法都可以使用。
GET /_search/scroll { "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAC7EWa1U5THR6U0xSSXVOZjJTaUhpeHY2dw==", "scroll": "10m" }
1.根据官方文档的说法,scroll的搜索上下文会在scroll的保留时间截止后自动清除,但是我们知道scroll是非常消耗资源的,所以一个建议就是当不需要了scroll数据的时候,尽可能快的把scroll_id显式删除掉,而且会生成历史快照,对于数据的变更不会反映到快照上。
DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAC7EWa1U5THR6U0xSSXVOZjJTaUhpeHY2dw==
1.可以看出scroll不适合支持那种实时的和用户交互的前端分页工作,其主要用途用于从ES集群分批拉取大量结果集的情况,一般都是offline的应用场景。 比如需要将非常大的结果集拉取出来,存放到其他系统处理,或者需要做大索引的reindex等等。
3.深分页search_after
建议使用Scroll api进行高效深度滚动,但滚动上下文代价很高,建议不要将其用于实时用户请求。该search_after参数通过提供实时游标来解决此问题。
search_after使用
1.search_after是ES5.0及之后版本提供的新特性,search_after有点类似scroll,但是和scroll又不一样,它提供一个活动的游标,通过上次查询的最后一条数据来进行下一次查询。 2.search_after分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。 3.但是需要注意,因为每一页的数据都是依赖于上一页最后一条数据,所以无法跳页请求。
1.search after 不能指定页数,只能一页挨一页问下查。第一次查询时带上sort,返回最后一个文档的id字段
1.每个文档具有一个唯一值的字段应该用作排序规范的仲裁器。否则,具有相同排序值的文档的排序顺序将是未定义的。建议的方法是使用字段_id,它肯定包含每个文档的一个唯一值。 2.为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。
GET dlp-prd-rabbitmq-tagfile-*/_search { "query": { "match_all": {} }, "sort": [ {"stime": {"order": "desc"}}, {"_id": {"order": "desc"}} ], "from": 0, "size": 10000 }
# 为了根据最后一条数据的唯一标识来查询1w后面的数据 1.可以使用id的唯一标识,或者id加stime时间,来查询1w的后面数据。 2.当前我们search_after使用的唯一标识有id和stime,那么sort排序就必须加入stime和id排序不然会报错。
2.下一次查询带上search after参数
1.上面的请求会为每一个文档返回一个包含sort排序值的数组。这些sort排序值可以被用于 search_after 参数里以便抓取下一页的数据。比如,我们可以使用最后的一个文档的sort排序值,将它传递给 search_after 参数:
GET dlp-prd-rabbitmq-tagfile-*/_search { "query": { "match_all": {} }, "sort": [ {"stime": {"order": "desc"}}, {"_id": {"order": "desc"}} ], "search_after": ["2022-09-25T12:38:42.142Z", "pxOodIMBf53JeIypLfhE"], "from": 0, "size": 10000 }
使用search_after必须要设置from=0。 这里我使用timestamp和_id作为唯一值排序。 我们在返回的最后一条数据里拿到sort属性的值传入到search_after。
3.删除和特性
1.search_after不是自由跳转到随机页面而是并行滚动多个查询的解决方案。它与滚动API非常相似,但与它不同,search_after参数是无状态的,它始终针对最新版本的搜索器进行解析。因此,排序顺序可能会在步行期间发生变化,具体取决于索引的更新和删除。
4.search_after应用场景
1.不支持跳页查询 # 另外在ES6.5的文档中有这样一句话需要注意一下: 2.另外,search_after并不是随机的查询某一页数据,而是并行的滚屏查询;search_after的查询顺序会在更新和删除时发生变化,也就是说支持实时的数据查询 3.大致的意思就是,如果search_after中的关键字为654,那么654323的文档也会被搜索到,所以在选择search_after的排序字段时需要谨慎,可以使用比如文档的id或者时间戳等