1. 深度分页的含义
- 定义:深度分页指的是在分页查询中,随着页码的增大,需要跳过的数据量也随之增加。例如:
- 页码:
pageNo
- 每页数量:
pageSize
- SQL 查询:
LIMIT (pageNo-1) * pageSize, pageSize
- 第 1000 页时,需要跳过 999 页数据才能获取到目标数据,性能开销很大。
- 页码:
- 问题:数据量很大时(如千万级别),需要处理和跳过大量的文档,导致性能显著下降。
2. Elasticsearch 中的 search_after
-
概念:
search_after
是 Elasticsearch 推荐的一种深度分页解决方案,避免了传统分页的高性能开销。- 它不基于
from
和size
的跳过逻辑,而是通过上一页最后一条文档的排序值,直接查询下一页。
- 它不基于
-
使用方法:
- 必须定义排序字段(如
"_id"
或自定义字段)。 - 使用
search_after
指定上一页最后一条文档的排序值。
- 必须定义排序字段(如
-
示例代码:
GET /items/_search { "query": { "match_all": {} }, "size": 5, "sort": ["price", "_id"], "search_after": [300, "id_123"] }
sort
: 排序字段。search_after
: 上一页最后一条文档的排序值。
-
注意事项:
- 必须对排序字段建立索引。
- 分页是单向的,适合顺序翻页,不支持随机跳转。
-
生活类比:
如果分页查询是翻书:- 传统分页(
from
和size
):翻到第 100 页,需要逐页翻到 99 页再找到目标。 search_after
:记录第 99 页最后一个词语,从这个词开始查找第 100 页。
- 传统分页(
3. 深度分页的性能问题
- 问题:如果数据量达到千万级别,第一次使用
search_after
仍需要排序,可能存在性能开销。 - 原因:排序需要扫描符合查询条件的所有文档,特别是字段未建立索引时。
- 解决方法:
- 提前定义排序字段:如
_id
或常用查询字段,减少排序计算。 - 使用索引优化:对需要排序的字段建立索引。
- 数据分片和分层:将数据按业务逻辑拆分为多个分片,减少单次查询的数据量??
- 提前定义排序字段:如
4. 关于代码书写风格
-
用户提到分页代码:
int pageNo = 1, pageSize = 5;
- 这是完全合法的 Java 代码,但很少用于实际开发,因为:
- 不符合一般的代码可读性习惯(每行一条声明更清晰)。
- 开发中常用框架(如 Spring Boot)通常有专门的分页工具类。
- 这是完全合法的 Java 代码,但很少用于实际开发,因为:
5. 关于数组访问
-
示例代码:
String hlName = name.getFragments()[0].toString();
- 为什么直接用
[0]
:getFragments()
返回的是一个数组,Java 中数组使用[]
访问元素。- 与列表或集合的
.get(index)
不同,数组直接支持索引访问。
- 适用场景:
- 如果你确定数组有数据且第一个元素存在,直接访问
[0]
是合法的。 - 如果存在空数组或越界风险,需要先检查长度避免
ArrayIndexOutOfBoundsException
。
- 如果你确定数组有数据且第一个元素存在,直接访问
- 为什么直接用
6. Elasticsearch 的度量聚合
- 定义:度量聚合用于计算某字段的统计数据,比如总和、平均值、最大值等。
- 常见度量聚合类型:
- Sum 聚合:求和。
- Avg 聚合:计算平均值。
- Max/Min 聚合:最大值或最小值。
- Count 聚合:文档数量。
- Stats 聚合:同时返回多种统计值(最大、最小、平均等)。
- 历史类比:像秦始皇统一度量衡,将杂乱无章的数值通过统一规则量化,方便统计分析。
7. Elasticsearch 聚合语法错误
-
用户请求中存在错误:
GET /items/_search { "query": { "range": { "price": { "gte": 300 } } }, "size": 0, "brand_agg": { "terms": { "field": "brand", "size": 10 } } }
- 错误原因:
- 聚合字段
brand_agg
的定义未放入aggs
(或aggregations
)字段中。 brand_agg
被放在了顶层,导致解析错误。
- 聚合字段
- 错误原因:
-
正确写法:
GET /items/_search { "query": { "range": { "price": { "gte": 300 } } }, "size": 0, "aggs": { "brand_agg": { "terms": { "field": "brand", "size": 10 } } } }
- 聚合定义必须放在
aggs
字段中。
- 聚合定义必须放在
-
解析的返回结果示例:
{ "aggregations": { "brand_agg": { "buckets": [ { "key": "Apple", "doc_count": 2 }, { "key": "Samsung", "doc_count": 1 }, { "key": "Sony", "doc_count": 1 } ] } } }
- buckets:每个品牌及其文档数量。