12 es怎么解决深分页的问题及索引优化有哪些方法

vvEcho 2025-02-20 15:48:02
Categories: > Tags:

Elasticsearch深分页问题解决方案

方案1:Search After(游标分页)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 首次查询(按时间倒序)
GET /order_index/_search
{
"size": 20,
"sort": [
{ "create_time": "desc" },
{ "id": "desc" } // 唯一排序字段保证稳定性
]
}

// 后续查询(携带上一页最后一条的排序值)
GET /order_index/_search
{
"size": 20,
"search_after": ["2025-01-01T00:00:00", "order_12345"],
"sort": [
{ "create_time": "desc" },
{ "id": "desc" }
]
}

后续查询(携带上一页最后一条的排序值)
优势:
仅扫描当前页数据,性能恒定(百万数据查询耗时<100ms)。
支持跨分片高效查询(结合您项目中分库分表同步ES的经验)

方案2:Scroll API(快照遍历)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 初始化Scroll(适合全量数据导出)
GET /order_index/_search?scroll=5m
{
"size": 1000,
"sort": ["_doc"]
}

// 后续遍历
GET /_search/scroll
{
"scroll": "5m",
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAA..."
}

适用场景:
数据全量导出和非实时性要求高的后台任务

业务妥协与前端优化
限制最大分页深度:仅允许用户访问前1000页(如电商场景),后续需缩小查询范围(如按时间过滤)。
滚动加载(Infinite Scroll):结合search_after实现无感知分页(类似京东商品列表页)。
缓存热点数据:对高频访问的深分页结果(如Top 1000商品)缓存至Redis,减少ES压力

Elasticsearch索引优化方法

分片与副本策略

1
2
3
4
5
6
7
8
PUT /order_index
{
"settings": {
"number_of_shards": 6", // 按数据量估算(如单分片不超过50GB)
"number_of_replicas": 1", // 生产环境建议至少1副本
"refresh_interval": "30s" // 写入高峰期调大,减少段合并开销
}
}

优化收益:写入吞吐量提升(京东项目中日志采集场景实测提升40%)
Mapping设计:

1
2
3
4
5
6
7
8
9
10
11
{
"properties": {
"order_no": { "type": "keyword" }, // 精确查询
"merchant_id": { "type": "keyword" }, // 路由字段(优化查询性能)
"amount": { "type": "scaled_float", "scaling_factor": 100 }, // 避免浮点精度问题
"tags": {
"type": "text",
"fields": { "keyword": { "type": "keyword" } } // 支持聚合
}
}
}

最佳实践:
避免使用dynamic: true,严格定义字段类型(参考您项目中数据清洗规则配置)。
对分页排序字段(如create_time)禁用doc_values以节省内存(仅排序场景)

路由(Routing)优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 写入时指定路由(如merchant_id)
PUT /order_index/_doc/1?routing=merchant_123
{
"merchant_id": "merchant_123",
"create_time": "2025-01-01"
}

// 查询时携带路由
GET /order_index/_search
{
"query": {
"term": { "merchant_id": "merchant_123" }
},
"routing": "merchant_123" // 直接定位到目标分片
}

减少跨分片查询开销(适合项目中按商户分库的场景)

索引预过滤

1
2
3
4
5
6
7
8
9
10
11
GET /order_index/_search
{
"query": {
"bool": {
"filter": [ // 不计算相关性得分,结果可缓存
{ "range": { "create_time": { "gte": "2025-01-01" } } },
{ "term": { "status": "completed" } }
]
}
}
}

固定条件筛选(如订单状态、时间范围)

冷热数据分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 按时间范围分索引(如order_2025Q1、order_2025Q2)
PUT /order_2025Q1
{
"settings": {
"index.routing.allocation.require.box_type": "hot" // 热节点(SSD)
}
}

PUT /order_2024Q4
{
"settings": {
"index.routing.allocation.require.box_type": "warm" // 冷节点(HDD)
}
}

降低存储成本(冷数据迁移至廉价存储),提升热点数据查询性能(SSD加速)

总结:通过Search After替代传统分页、路由优化及冷热数据分离,结合您项目中分库分表同步ES的经验,可有效解决深分页性能问题。索引设计需严格定义Mapping、合理分配分片,并利用硬件资源分层存储