elasticsearch-利用游标查询 'Scroll'来做分页查询

本文介绍Elasticsearch中的游标查询'Scroll'机制,包括如何初始化查询、批量拉取结果及保持游标状态。此外,还提供了一个Python示例,演示了如何使用游标查询来高效检索大量文档。

游标查询 'Scroll'

scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。

游标查询允许我们 先做查询初始化,然后再批量地拉取结果。 这有点儿像传统数据库中的 cursor 。

游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 '视图' 一样。

深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。 游标查询用字段 _doc 来排序。 这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。

启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。 设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

GET /old_index/_search?scroll=1m (1)
{
    "query": { "match_all": {}},
    "sort" : ["_doc"], (2)
    "size":  1000
}
  1. 保持游标查询窗口一分钟。

  2. 关键字 _doc 是最有效的排序顺序。

这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串 。 现在我们能传递字段 _scroll_id 到 _search/scroll 查询接口获取下一批结果:

GET /_search/scroll
{
    "scroll": "1m", (1)
    "scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
}
  1. 注意再次设置游标查询过期时间为一分钟。

这个游标查询返回的下一批结果。 尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。 当查询的时候, 字段 size 作用于单个分片,所以每个批次实际返回的文档数量最大为 size * number_of_primary_shards 。

[python]  view plain  copy
  1. def query_Data(self,mindex,mtype,mstr,msize=20):  
  2.         '''''查询数据库中指定表所有字段出现的值 
  3.         :mindex 查询的数据库 
  4.         :mtype 查询的数据库表 
  5.         :mstr 匹配的字段 
  6.         :mfrom 返回的起始位置 
  7.         :msize 需要查询的总条数 
  8.         return 返回一个dict 
  9.         '''  
  10.         if not Ela.es:  
  11.             return False  
  12.         if not (mindex and mtype and mstr):  
  13.             return False  
  14.         data=[]  
  15.         try:  
  16.             querydata = Ela.es.search(index=mindex,doc_type=mtype,scroll='5m',timeout='3s',\  
  17.                         body={"query": {"bool": {"must": [{"query_string": {"default_field""_all","query": mstr}}]}},"size": msize})  
  18.             mdata = querydata.get("hits").get("hits")  
  19.             if not mdata:  
  20.                 return -1 #没有查询到数据  
  21.             #解析返回的值  
  22.             data = [d.get("_source"for d in mdata]  
  23.             sid = querydata['_scroll_id']  
  24.             while True:  
  25.                 rs = Ela.es.scroll(scroll_id=sid,scroll='10s')  
  26.                 temp = rs.get("hits").get("hits")  
  27.                 if not temp:  
  28.                     break  
  29.                 data += [d.get("_source"for d in temp]  
  30.             logger.info("共查询到: %d条数据"%data.__len__())  
  31.             return data  
  32.         except Exception as ex:  
  33.             logger.warnning("Elasticsearch数据库查询发生异常"+str(ex))  
  34.             return False  
原文链接: https://blog.youkuaiyun.com/chuan442616909/article/details/55195024
<think>嗯,用户的问题是关于Elasticsearch使用Scroll进行模糊查询分页时,因为游标不足导致数据缺失的解决方案。我需要仔细分析可能的原因,然后给出对应的解决办法。首先,我得回忆一下ElasticsearchScroll机制是怎么工作的。Scroll主要用于处理深度分页,通过保持游标来持续获取数据,但可能会有时间限制或者游标过期的问题。 用户提到使用模糊查询,可能用的是wildcard或者fuzzy查询,这些查询本身比较消耗资源,尤其是在大数据集上,可能会影响性能,导致游标未能及时获取所有数据。另外,游标设置的时间太短,可能在分页过程中游标已经过期,导致后续的数据无法获取,从而出现数据缺失。 接下来,我需要考虑可能的解决方案。首先,调整scroll的保持时间,确保在分页过程中游标不会过期。不过设置过长可能会增加集群负担,所以需要平衡。其次,模糊查询的优化,比如避免左通配符,或者使用更高效的查询方式,比如ngram分词,或者将部分查询转移到索引阶段,比如预处理时生成ngram tokens,这样查询时可以用term查询代替wildcard,提高效率。 另外,处理游标时,需要确保每次请求都使用最新的scroll_id,并且及时清理不再使用的游标,避免资源泄露。还有,可能需要监控集群的性能,确保有足够的资源来处理长时间的scroll请求,比如调整JVM堆内存或者分片策略,减少单个分片的数据量,提升查询速度。 还要考虑分页逻辑是否正确,比如是否在循环中正确获取和更新scroll_id,是否有遗漏的情况导致游标提前关闭。此外,如果数据量实在太大,可能需要考虑其他分页方式,比如search_after,结合排序字段和上一页的结果来实现更高效的分页,避免游标长时间占用资源。 最后,可能需要检查Elasticsearch的版本,因为不同版本对scroll的处理可能有差异,升级到较新的版本可能会获得更好的支持或者更优的解决方案。同时,参考官方文档和社区的最佳实践,确保建议的方法是最新且有效的。</think>### 问题分析 在使用ElasticsearchScroll游标进行模糊查询分页时,数据缺失通常由以下原因导致: 1. **游标超时**:默认的`scroll`存活时间(如1分钟)不足,导致后续分页请求时游标已失效[^2][^4]。 2. **模糊查询性能瓶颈**:模糊查询(如`wildcard`或`fuzzy`)可能导致查询延迟,未及时处理全部结果前游标已释放。 3. **分页逻辑错误**:未正确处理`scroll_id`迭代或分页终止条件。 4. **集群资源限制**:内存或线程池资源不足,导致部分分页请求失败。 --- ### 解决方案 #### 1. 调整Scroll存活时间 - **增大`scroll`参数值**:根据数据量设置合理的存活时间(例如`5m`)。 ```java SearchRequest request = new SearchRequest("index"); request.scroll(TimeValue.timeValueMinutes(5)); // 设置更长的存活时间 ``` - **定期刷新游标**:每次分页请求后更新游标存活时间: ```java SearchResponse response = client.searchScroll(new SearchScrollRequest(scrollId).scroll(TimeValue.timeValueMinutes(2))); ``` #### 2. 优化模糊查询性能 - **避免左通配符**:`wildcard*`类查询会显著降低性能,优先使用`ngram`分词器预处理字段[^4]。 ```json PUT /index { "settings": { "analysis": { "analyzer": { "ngram_analyzer": { "tokenizer": "ngram_tokenizer" } }, "tokenizer": { "ngram_tokenizer": { "type": "ngram", "min_gram": 3, "max_gram": 5 } } } } } ``` - **替换为`match_phrase`或`term`查询**:利用预处理后的字段进行精确匹配。 #### 3. 分页逻辑修正 - **严格遵循Scroll迭代流程**: ```java String scrollId = response.getScrollId(); // 每次分页需使用最新scroll_id[^3] while (true) { SearchHit[] hits = response.getHits().getHits(); if (hits == null || hits.length == 0) break; // 处理数据 response = client.searchScroll(new SearchScrollRequest(scrollId).scroll(TimeValue.timeValueMinutes(2))); } ``` - **显式清除游标**:分页完成后主动释放资源: ```java ClearScrollRequest clearRequest = new ClearScrollRequest(); clearRequest.addScrollId(scrollId); client.clearScroll(clearRequest); ``` #### 4. 集群资源调优 - **增加JVM堆内存**:避免因内存不足导致查询中断。 - **调整分片策略**:减少单个分片数据量,提升查询效率。 --- ### 替代方案:Search After 若数据频繁更新,建议改用`search_after`分页: ```json { "query": { "match": { "field": "value*" } }, "size": 100, "sort": [{"_id": "asc"}] } ``` 后续分页通过上一页最后一条记录的排序值继续查询[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值