Elasticsearch 查询超过10000 的解决方案 - Python

Elasticsearch 查询超过10000 的解决方案 - Python

法1:修改 设置 max_result_size (不推荐)

# 调大查询窗口大小,比如100w (不推荐,慎用)
PUT test/_settings
{
  "index.max_result_window": "1000000"
}

# 查看 查询最大数
GET test/_settings
---
{
  "demo_scroll" : {
    "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "demo_scroll",
        "max_result_window" : "1000000",
        "creation_date" : "1680832840425",
        "number_of_replicas" : "1",
        "uuid" : "OLV5W_D9R-WBUaZ_QbGeWA",
        "version" : {
          "created" : "6082399"
        }
      }
    }
  }
}

法2: scroll 分页

    def getData(self):
        current_time = datetime.datetime.now()
        one_hour_ago = current_time - datetime.timedelta(hours=24)
        current_time_str = current_time.strftime('%Y-%m-%d %H:%M:%S')
        hours_ago_str = one_hour_ago.strftime('%Y-%m-%d %H:%M:%S')

        # 改为从elasticsearch读取数据
        es = Elasticsearch(hosts='http://127.0.0.1/9200',
                           timeout=1200)
        size = 10000
        query_scroll = {
            "size": size,
            "query": {
                "range": {
                    "create_time.keyword": {
                        "gte": hours_ago_str.__str__(),
                        "lte": current_time_str.__str__()
                    }
                }
            },
            "_source": ["ip_address", "OS", "host", "user", "create_time"],
        }
        scroll = "10m" # 该次连接超时时间设置
        result = []
        # first
        init_res = es.search(index="nac-users", body=query_scroll, scroll=scroll)
        scroll_id = init_res["_scroll_id"]
        for item in init_res["hits"]["hits"]:
            result.append({
                'id': item['_id'],
                'ip_address': item['_source']['ip_address'],
                'operating_system': item['_source']['OS'],
                'hostname': item['_source']['host'],
                'username': item['_source']['user'],
                'date_t': item['_source']['create_time'],
            })
        i = 0
        while i < 16:  # 剩下的数据 一天 24 小时数据估计不会超过 160000
            res = es.scroll(scroll_id=scroll_id, scroll=scroll)
            if len(res["hits"]["hits"]) == 0:
                break
            for item in res["hits"]["hits"]:
                result.append({
                    'id': item['_id'],
                    'ip_address': item['_source']['ip_address'],
                    'operating_system': item['_source']['OS'],
                    'hostname': item['_source']['host'],
                    'username': item['_source']['user'],
                    'date_t': item['_source']['create_time'],
                })
            i = i + 1
            
        # 原始的    
        # {"query": {"match_all": {}}, "size": 10000}
        # res = es.search(index="nac-users", body=query_scroll)
        #
        # result = []
        # for item in res['hits']['hits']:
        #     result.append({
        #         'id': item['_id'],
        #         'ip_address': item['_source']['ip_address'],
        #         'operating_system': item['_source']['OS'],
        #         'hostname': item['_source']['host'],
        #         'username': item['_source']['user'],
        #         'date_t': item['_source']['create_time'],
        #     })
        self.data = pd.DataFrame(result)

法3: search_after 分页

def getData(self):
      current_time = datetime.datetime.now()
      one_hour_ago = current_time - datetime.timedelta(hours=24)
      current_time_str = current_time.strftime('%Y-%m-%d %H:%M:%S')
      hours_ago_str = one_hour_ago.strftime('%Y-%m-%d %H:%M:%S')

      # 改为从elasticsearch读取数据
      es = Elasticsearch(hosts='http://127.0.0.1:9200',
                         timeout=1200)
      size = 10000
      query_scroll = {
          "size": size,
          "query": {
              "range": {
                  "create_time.keyword": {
                      "gte": hours_ago_str.__str__(),
                      "lte": current_time_str.__str__()
                  }
              }
          },
          "sort": [
              {
                  "create_time.keyword": {
                      "order": "desc"
                  }
              }
          ],
          "_source": ["ip_address", "OS", "host", "user", "create_time"],
      }
      result = []
      init_res = es.search(index="nac-users", body=query_scroll)
      if len(init_res["hits"]["hits"]) == 0:
          self.data = pd.DataFrame(result)
          return
      sort = init_res["hits"]["hits"][0]["sort"] # 我这里是用时间来排序的,所以取到的是时间字段
      for item in init_res["hits"]["hits"]:
          result.append({
              'id': item['_id'],
              'ip_address': item['_source']['ip_address'],
              'operating_system': item['_source']['OS'],
              'hostname': item['_source']['host'],
              'username': item['_source']['user'],
              'date_t': item['_source']['create_time'],
          })
      i = 0
      while i < 16:
          query_scroll["search_after"] = sort
          res = es.search(index="nac-users", body=query_scroll)
          sort = res["hits"]["hits"][0]["sort"]
          if len(res["hits"]["hits"]) == 0:
              break
          for item in res["hits"]["hits"]:
              result.append({
                  'id': item['_id'],
                  'ip_address': item['_source']['ip_address'],
                  'operating_system': item['_source']['OS'],
                  'hostname': item['_source']['host'],
                  'username': item['_source']['user'],
                  'date_t': item['_source']['create_time'],
              })
          i = i + 1
    self.data = pd.DataFrame(result)        

还有一个方法是在参考文章2里面提到的track_total_hits,但是我测试的时候没起作用,目前还不太清楚原因。。。

我看参考文章里说到search_after 分页要比scroll快,但是在我的数据上是scroll要快很多,不是特别清楚,可能我这里的数据暂时只有2w多一点,感觉用到search_after 分页需要排序,可能是排序的字段的问题,时间字段我存的是字符串格式,,如有可以修改的地方,欢迎大家指正~ 有更多可以参考的方法欢迎贴在评论区供大家参考~

【参考1】https://juejin.cn/post/7224369270141993019
【参考2】https://blog.youkuaiyun.com/u011250186/article/details/125483759

<think>嗯,用户遇到了Elasticsearch的报错,提示结果窗口太大,需要调整index.max_result_window参数或者使用scroll API。首先,我得确认问题的具体情况。用户提到他们试图获取的数据量超过了默认的10000条限制,导致报错。现在他们想通过Python客户端解决这个问题。 首先,解决方案可能有两个方向:调整max_result_window参数或者改用scroll API。不过,调整参数虽然直接,但可能有性能问题,特别是对于大数据量的分页。而scroll API适合深度分页,但需要维护上下文,可能不适合实时性高的场景。另外,用户可能还需要了解search_after方法,这是一种更有效的分页方式,结合排序和游标。 接下来,我需要确保提供的步骤正确无误。比如,修改索引设置时,需要使用PUT请求,这在Python客户端中可以通过indices.put_settings方法实现。同时,要注意集群可能需要重启或者这个调整对性能的影响。 对于scroll API的使用,代码示例需要正确展示如何初始化搜索、获取滚动ID,以及循环获取结果。同时要提醒用户scroll_id的有效期,避免超时问题。 另外,用户可能对这三种方法的适用场景不太清楚,需要解释清楚:调整参数适合小规模分页,scroll适合大量数据导出,search_after适合实时分页。 最后,生成的相关问题要覆盖常见的后续疑问,比如参数调整的风险、scroll和search_after的区别,以及性能优化的其他方法。确保用户能全面了解解决方案和可能的后续步骤。</think>### 问题分析 Elasticsearch默认限制单次查询返回结果窗口大小为$10,000$条数据,当`from + size > 10,000`时触发该错误。以下是三种解决方案及其实现步骤: --- ### 解决方案一:调整索引设置(适合小规模分页) 1. **修改`index.max_result_window`参数** ```python from elasticsearch import Elasticsearch es = Elasticsearch() body = { "index": { "max_result_window": 20000 # 根据实际需求调整 } } es.indices.put_settings(index='your_index_name', body=body) # 替换your_index_name ``` **注意**:此方法会增加内存消耗,可能导致性能下降[^1] 2. **查询时使用扩展窗口** ```python es.search(index="your_index", body={"query": {...}}, from_=0, size=15000) ``` --- ### 解决方案二:使用Scroll API(适合大数据量导出) Scroll API通过维护搜索上下文实现批量获取: ```python # 初始化scroll response = es.search( index="your_index", scroll="2m", # 上下文有效期 size=1000, body={"query": {...}} ) scroll_id = response['_scroll_id'] # 循环获取数据 while True: response = es.scroll(scroll_id=scroll_id, scroll="2m") hits = response['hits']['hits'] if not hits: break # 处理数据 ``` --- ### 解决方案三:Search After(推荐用于深度分页) 结合排序字段实现高效分页: ```python last_sort = None while True: body = { "query": {...}, "sort": [{"timestamp": "asc"}], # 必须包含唯一性字段 "size": 1000 } if last_sort: body["search_after"] = last_sort response = es.search(index="your_index", body=body) hits = response['hits']['hits'] if not hits: break last_sort = hits[-1]['sort'] # 获取最后一条排序值 ``` --- ### 方法对比 | 方法 | 适用场景 | 优点 | 缺点 | |---------------------|-------------------------|-----------------------|-------------------------| | 调整max_result_window | 小规模分页(≤10万) | 实现简单 | 内存压力大,性能下降 | | Scroll API | 全量数据导出/ETL | 支持超大数据量 | 上下文维护成本高 | | Search After | 实时深度分页 | 无上下文开销 | 需要唯一排序字段 | ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值