Elasticsearch中的基本概念,Elasticsearch 深度分页的几种方式

文章详细介绍了Elasticsearch中的关键概念,包括集群、节点、索引、分片和副本,以及文档的搜索、写入、删除和更新过程。同时,讨论了深度分页的三种策略:from+size、scroll和SearchAfter,分析了各自的优缺点和适用场景。

Elasticsearch中的基本概念

集群(Cluster) : ES可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。

节点(Node) : 形成集群的每个服务器称为节点

索引(index) : 在 ES 中, 索引是一组文档的集合。(类似数据库中的一张表,文档id类似表中的主键)

分片(shard) :当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起。(类似于数据库的分库分表,把某个表中的数据分散到不同的库不同的表中)

副本(Replia) :为提高查询吞吐量或实现高可用性,可以使用分片副本。副本是一个分片的精确复制,每个分片可以有零个或多个副本。ES中可以有许多相同的分片,其中之一被选择更改索引操作,这种特殊的分片称为主分片。当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提升为新的主分片。

描述一下Elasticsearch中文档的搜索过程?

搜索分为两阶段过程(称之为 Query Then Fetch),在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索(搜索出满足搜索条件的文档)并构建一个匹配文档的大小为 from + size 的优先队列。每个分片返回各自优先队列中 所有文档的 ID 和排序值(只带文档id和排序条件) 给协调节点,协调节点合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。

接下来就是 取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。

描述一下Elasticsearch中文档的写入过程,删除或更新过程?

写入过程: 客户端发送写请求的时候,先通过协调节点(收到请求的节点此时作为协调协调节点)计算文档ID的hash值,找到要写入的分片节点,分片节点收到写请求后,先写入内存中(同时写入translog),然后定时1秒刷到文件缓存中,写到文件缓存之后这个文档可以被检索到,之后才会被写到磁盘中(默认30分钟或者日志过大超过缓存的时候强制写到磁盘)。每个写请求都会记录translog日志,记录被写到磁盘之后会删除translog

删除或更新过程: 因为ElasticsearchS中文档是不可更改的,删除的时候记录先被标记为del,检索的时候该文档依然能匹配查询,但是会在结果中被过滤掉。更新的时候Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。之后merge的时候会删除这些被标记删除的记录。

并发情况下Elasticsearch如何保证读写一致?

可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;

Elasticsearch 深度分页的几种方式

from + size

这是Elasticsearch默认采用的分页方式,虽然有点类似数据库的分页方式,但是实际做法却非常耗内存

GET /student/student/_search
{
  "query":{
    "match_all": {}
  },
  "from":990,
  "size":10
}

假如查询: from = 990 , size = 10 , 分片数为:4,es会在每个分片获取1000条文档,通过协调节点(Coordinating Node) 汇总各个节点的数据,再通过排序选择前1000个文档返回 最后的结果:

缺点:

这些数据都是在内存中操作的,随着翻页越深入,数据越多,内存消耗越多,这样的效率是非常低的。另外es为了性能,限制了我们分页的深度,es目前支持的最大的 max_result_window = 10000 (可以通过接口调整)

scroll

为了满足深度分页的场景,es 提供了 scroll 的方式进行分页读取。原理上是对某次查询生成一个游标 scroll_id(类似数据库中的cursor游标) , 后续的查询只需要根据这个游标去取数据,直到结果集中返回的 hits 字段为空,就表示遍历结束。scroll_id 的生成可以理解为建立了一个临时的历史快照,在此之后的增删改查等操作不会影响到这个快照的结果,所有文档获取完毕之后,需要手动清理掉 scroll_id。

第一次请求,设置scroll=5m,表示该窗口过期时间为5分钟,分页size大小为10

GET /student/student/_search?scroll=5m
{
  "query": {
    "match_all": {}
  },
  "size": 10
}
返回
{
  "_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB",
 ...
 }

之后每次请求,只要带上scroll_id即可,每次返回size大小的记录数,直到返回结果集中为空,表示没有更多的数据了。

缺点:

scroll 方式是针对本次查询生成一个临时快照,实时变动的数据无法查询到,scroll_id会占用大量的资源(特别是排序的请求)

为了提高性能,scroll分页中还有其他的优化部分,例如:Scroll Scan 结果没有排序,其他与普通scroll类似,但是分页效率要高一些。还有Sliced Scroll 可以并发来进行数据遍历等。

Search After

ES 5 引入的一种分页查询机制,其原理几乎就是和scroll一样,维护一个实时游标,它以上一次查询的最后一条记录为游标,方便对下一页的查询,它是一个无状态的查询,因此每次查询的都是最新的数据,由于它采用记录作为游标,因此SearchAfter要求doc中至少有一条全局唯一变量(每个文档具有一个唯一值的字段应该用作排序规范)

缺点:

  1. 由于实时查询,因此在查询期间的变更(增加删除更新文档)可能会导致跨页面的不一值。

  2. 至少需要制定一个唯一的不重复字段来排序

  3. 它不适用于大幅度跳页查询,或者全量导出,对第N页的跳转查询相当于对es不断重复的执行N次search after,而全量导出则是在短时间内执行大量的重复查询

分页方式性能优点缺点场景
from + size灵活性好,实现简单深度分页问题数据量比较小,能容忍深度分页问题
scroll解决了深度分页问题无法反应数据的实时性(快照版本)维护成本高,需要维护一个 scroll_id海量数据的导出需要查询海量结果集的数据
search_after性能最好不存在深度分页问题能够反映数据的实时变更实现复杂,需要有一个全局唯一的字段连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果,它不适用于大幅度跳页查询海量数据的分页

 

Elasticsearch 中实现分页查询时,选择合适的分页策略对于性能优化至关重要。根据不同的数据规模和使用场景,可以采用以下几种分页方式: ### 基于 `from + size` 的分页 这是最简单的分页方式,适用于小数据量场景。通过设置 `from` 和 `size` 参数来控制查询的起始位置和返回文档数量。例如: ```json { "query": { "match_all": {} }, "from": 0, "size": 10 } ``` 这种方式在小数据量下表现良好,但在大数据量下会出现性能下降的问题,因为每次请求都需要从每个分片中获取 `from + size` 条数据,然后在协调节点进行合并和排序,最终返回指定范围的结果。随着 `from` 值的增大,性能会显著降低 [^2]。 ### 基于 `search_after` 的分页 为了克服 `from + size` 在深度分页时的性能问题,Elasticsearch 提供了 `search_after` 参数,它允许基于排序字段的值进行分页。该方法要求结果集必须按照一个或多个唯一排序字段进行排序,通常使用时间戳或自增ID作为排序字段。例如: ```json { "query": { "match_all": {} }, "size": 10, "sort": [ {"_id": "asc"} ], "search_after": [12345] } ``` 这种方式避免了深度分页带来的性能问题,因为 `search_after` 直接从上一次查询结果的位置继续获取数据,而不需要重新扫描整个索引。适用于需要深度分页且数据量较大的场景 [^1]。 ### 基于 `scroll` 的分页 `scroll` API 主要用于批量处理数据,而不是用于实时分页。它通过创建一个快照来遍历整个索引的数据,适用于需要处理大量数据的场景,比如数据迁移或批量导出。使用 `scroll` 时,首先发起一个带有 `?scroll=5m` 的请求,然后通过返回的 `_scroll_id` 进行后续请求: ```json GET /_search?scroll=5m { "query": { "match_all": {} }, "size": 1000 } ``` 后续请求需要使用 `_scroll` 端点和 `_scroll_id`: ```json POST /_search/scroll { "scroll": "5m", "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WNjBkZjJiMjE0ZTA2NjM2MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJjOWQ4MDJc=" } ``` `scroll` 会保持快照一段时间(如 `5m`),在此期间可以通过 `_scroll_id` 获取下一批数据。但需要注意的是,`scroll` 不适合实时查询,因为快照是静态的,不会反映索引的最新更改 [^3]。 ### 总结 - **小数据量**:使用 `from + size`,简单易用。 - **深度分页**:使用 `search_after`,性能更优。 - **批量处理**:使用 `scroll`,适合数据迁移或导出。 在实际应用中,应根据具体需求选择合适的分页策略,以确保查询性能和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云哲-吉吉2021

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值