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中至少有一条全局唯一变量(每个文档具有一个唯一值的字段应该用作排序规范)
缺点:
-
由于实时查询,因此在查询期间的变更(增加删除更新文档)可能会导致跨页面的不一值。
-
至少需要制定一个唯一的不重复字段来排序
-
它不适用于大幅度跳页查询,或者全量导出,对第N页的跳转查询相当于对es不断重复的执行N次search after,而全量导出则是在短时间内执行大量的重复查询
分页方式 | 性能 | 优点 | 缺点 | 场景 |
---|---|---|---|---|
from + size | 低 | 灵活性好,实现简单 | 深度分页问题 | 数据量比较小,能容忍深度分页问题 |
scroll | 中 | 解决了深度分页问题 | 无法反应数据的实时性(快照版本)维护成本高,需要维护一个 scroll_id | 海量数据的导出需要查询海量结果集的数据 |
search_after | 高 | 性能最好不存在深度分页问题能够反映数据的实时变更 | 实现复杂,需要有一个全局唯一的字段连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果,它不适用于大幅度跳页查询 | 海量数据的分页 |