一、分页查询方式
在应用中经常需要用到分页查询来访问数据,同时由于分页查询产生相应的性能问题也比较多,通过梳理使用分页查询的使用方式,避免性能问题。
分页查询有以下几中方式:
- from ... size
- search after
- scroll
- pit
二、分页查询的使用方式
示例数据
代码语言:txt复制DELETE my_index
POST /_bulk
{ "index": { "_index": "my_index", "_id": "1" } }
{ "timestamp": "2024-08-01T00:00:00", "user_id": 1, "message": "Message 1" }
{ "index": { "_index": "my_index", "_id": "2" } }
{ "timestamp": "2024-08-01T00:01:00", "user_id": 2, "message": "Message 2" }
{ "index": { "_index": "my_index", "_id": "3" } }
{ "timestamp": "2024-08-01T00:02:00", "user_id": 3, "message": "Message 3" }
{ "index": { "_index": "my_index", "_id": "4" } }
{ "timestamp": "2024-08-01T00:03:00", "user_id": 4, "message": "Message 4" }
{ "index": { "_index": "my_index", "_id": "5" } }
{ "timestamp": "2024-08-01T00:04:00", "user_id": 5, "message": "Message 5" }
{ "index": { "_index": "my_index", "_id": "6" } }
{ "timestamp": "2024-08-01T00:05:00", "user_id": 6, "message": "Message 6" }
{ "index": { "_index": "my_index", "_id": "7" } }
{ "timestamp": "2024-08-01T00:06:00", "user_id": 7, "message": "Message 7" }
{ "index": { "_index": "my_index", "_id": "8" } }
{ "timestamp": "2024-08-01T00:07:00", "user_id": 8, "message": "Message 8" }
{ "index": { "_index": "my_index", "_id": "9" } }
{ "timestamp": "2024-08-01T00:08:00", "user_id": 9, "message": "Message 9" }
{ "index": { "_index": "my_index", "_id": "10" } }
{ "timestamp": "2024-08-01T00:09:00", "user_id": 10, "message": "Message 10"
}
1、from 和 size
代码语言:txt复制POST /my_index/_search
{
"from": 3,
"size": 3,
"sort": [
{"timestamp": "asc"}
]
}
特点:
- 动态分页,用户需要跳转到特定页。
- 简化实现,特别是数据集较小或临时查询。
- 排序字段非唯一,重复值较多。
- 需要跳过大量记录,但只返回少量结果。
2、search after
代码语言:txt复制第一次查询
POST /my_index/_search
{
"size": 3,
"sort": [{"timestamp": "asc"}, {"user_id": "asc"}]
}
#第二次查询使用第一次查询的timestamp结果和user_id结果
POST /my_index/_search
{
"size": 3,
"sort": [{"timestamp": "asc"}, {"user_id": "asc"}],
"search_after": ["2024-08-01T00:02:00", 3]
}
基本原理就是每次查询取后面的排序字段,进行一轮查询,这个通常用来替换from,size的深度分页中from比较大的查询。
但是要求是排序字段要唯一,不能存在多个值。
特点:
- 深度分页,查询结果较大。
- 避免性能问题,特别是当 from 值较大时。
- 需要稳定的排序结果,每次查询都依赖上一次查询的排序值。
- 实时性要求高,确保每次查询都能反映最新数据。
3、 scroll
代码语言:txt复制POST /my_index/_search?scroll=2m
{
"size": 3,
"sort": [{"timestamp": "asc"}, {"user_id": "asc"}]
}
POST /_search/scroll
{
"scroll": "2m",
"scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFkNnSWRNQTc0UWtPZnh2eEZmdFl0dncAAAAAACzXABZ2d2pCVlFINlJiQ0oyVDhaUkJlUWxR"
}
注意点:
1、在scroll=2m的周期内,数据有上下文的参与,在这个周期内数据是一致的。
2、_reindex接口内部就是使用scroll方式分批取数据。
3、由于要维护上下文,会占用一部分内存
特点:
- 需要处理大规模的结果集
- 一致性要求高
- 需要进行长时间的批量数据处理。
4、pit
代码语言:txt复制POST /my_index/_pit?keep_alive=2m
POST /_search
{
"size": 3,
"sort": [{"timestamp": "asc"}, {"user_id": "asc"}],
"pit": {"id": "4YyPBAEIbXlfaW5kZXgWRTdqdDlNQXlTSVcwdnlISTR6anFzZwAWdndqQlZRSDZSYkNKMlQ4WlJCZVFsUQAAAAAAACzZThZDZ0lkTUE3NFFrT2Z4dnhGZnRZdHZ3AAEWRTdqdDlNQXlTSVcwdnlISTR6anFzZwAA"}
}
三、分页查询产生的常见问题
1、内存溢出
原因:滚动查询(scroll)会在内存中保持一个快照,如果数据量太大或 scroll 持续时间过长,可能会导致内存溢出。
解决办法:
减少单次请求的 size: 将每次请求的数据量减少。
优化查询: 使用过滤条件来减少查询结果集。
增加集群节点: 提升集群的资源配置。
2、scroll超时
滚动上下文在指定时间后会过期,如果在此时间内没有进行下一次请求,滚动上下文会被删除。
解决办法:
延长 scroll 时间: 根据需要延长 scroll 的保留时间。
及时获取下一批数据: 确保在 scroll 时间内获取下一批数据。
3. search_context_missing_exception
原因:滚动上下文已过期或不存在。
处理办法
检查 scroll 保留时间: 确保在 scroll 时间内发起请求。
使用 Point in Time (PIT): 作为替代方案,可以使用 PIT 来处理长时间的查询。