面试官:你说你用过ES,那你说一下ES的分页查询方式有哪些?

385 阅读3分钟

Elasticsearch(ES)中的分页查询主要有以下四种方式,每种方式适用于不同的场景,且存在不同的性能与功能限制:


一、基础分页:from + size

  • 机制:通过from(起始位置)和size(每页数量)参数实现分页,类似于传统数据库的LIMIT offset, size

    GET /your_index/_search {
      "from": 0,
      "size": 10,
      "query": { ... }
    }
    
  • 特点

    • 简单易用:适合小数据量或浅层分页(如查询前100页)。
    • 性能瓶颈:深度分页时(如from > 10000),需在每个分片加载并排序from + size条数据,合并后再截取目标区间,导致内存和CPU消耗剧增,默认限制为max_result_window=10000
    • 不适用场景:大数据量深度分页(如导出全量数据或查询第1000页后的数据)。

二、滚动查询:scroll

  • 机制:通过创建索引快照(snapshot)维护滚动上下文,基于scroll_id逐批获取数据,类似游标遍历。

    // 初始化
    GET /your_index/_search?scroll=1m {
      "size": 1000,
      "query": { ... }
    }
    // 后续请求
    GET /_search/scroll {
      "scroll_id": "DnF1...",
      "scroll": "1m"
    }
    
  • 特点

    • 大数据量支持:适合数据导出、批量处理等离线任务,可遍历全量数据。
    • 实时性牺牲:基于初始快照查询,不反映后续数据变更。
    • 资源消耗:需维护滚动上下文,长期未清理可能导致内存泄漏。

三、实时深度分页:search_after

  • 机制:基于上一页最后一条记录的排序值(如时间戳、唯一ID)定位下一页起始点,避免全局排序。

    GET /your_index/_search {
      "size": 10,
      "query": { ... },
      "sort": [{"timestamp": "asc"}, {"_id": "asc"}],
      "search_after": [1698765432, "abc123"]
    }
    
  • 特点

    • 高性能:无需全局排序,适合深度分页且需实时性的场景(如无限滚动加载)。

    • 限制

      • 顺序遍历:仅支持顺序逐页查询,无法跳页。
      • 排序字段要求:需至少一个唯一且稳定的排序字段(如时间戳+ID组合)。
    • 结合PIT(Point in Time)​:通过pit_id固定查询快照,保证分页过程中数据一致性。


四、复合聚合分页:composite聚合

  • 机制:通过聚合分桶(如按时间区间、分类)实现分页,结合after_key参数逐批获取结果。

    GET /your_index/_search {
      "size": 0,
      "aggs": {
        "my_buckets": {
          "composite": {
            "size": 100,
            "sources": [{"field": "category"}]
          }
        }
      }
    }
    
  • 特点

    • 聚合场景专用:适合按维度分页统计(如分页展示分类商品数量)。
    • 非通用分页:不适用于文档列表分页,主要用于聚合结果分页。

总结:如何选择分页方式?

场景推荐方式注意事项
浅层分页(前100页)from + size避免超过max_result_window限制
大数据量导出或离线处理scroll及时清理scroll_id,避免内存泄漏
实时深度分页(如无限滚动)search_after需唯一排序字段,结合PIT保证一致性
按维度分页统计composite聚合仅适用于聚合结果,非文档列表分页