ES的数据原理
ES的倒排索引
Elasticsearch 的倒排索引是一种特殊的数据结构,用于高效地执行全文检索。它通过将文档中的每个词(或术语)映射到包含这些词的文档列表来实现快速的搜索功能。
倒排索引的核心是以下几个方面:
- 词项(Term):每个词项(例如“猫”)都会被记录在索引中。
- 文档列表:每个词项会记录在哪些文档中出现,形成文档列表。
- 词频(Term Frequency, TF):可以记录每个词在文档中出现的次数,用于相关性计算。
- 位置信息:有时也会记录词在文档中的位置,以支持短语查询或近似匹配查询。
倒排索引的高效性使 Elasticsearch 能够快速响应复杂的查询请求,如短语匹配、模糊匹配、全文检索等。
举个例子,如果有三个文档:
文档1: "猫在桌子上"
文档2: "狗在花园里"
文档3: "猫和狗在一起玩"
倒排索引会根据这些文档构建一个类似于词典的结构,如下:
"猫" -> [文档1, 文档3]
"狗" -> [文档2, 文档3]
"在" -> [文档1, 文档2, 文档3]
"桌子" -> [文档1]
"花园" -> [文档2]
"一起" -> [文档3]
"玩" -> [文档3]
当用户搜索某个关键词(例如“猫”)时,Elasticsearch 不需要遍历所有文档,而是可以直接从倒排索引中找到与之相关的文档(例如,文档1和文档3)返回文档id 。这样做极大地提高了查询的速度,尤其是在处理海量数据时。
面试题: 分片底层时如何工作的?
答: 分片底层对应的是一个Lucene库,而Lucene底层使用倒排索引技术实现。
倒排索引(反向索引):
ES使用一种称为"倒排索引"的结构,它适用于快速的全文检索。
倒排索引中有以下三个专业术语:
词条:
指的是最小的存储和查询单元,换句话说,指的是您想要查询的关键字(词)。
对应英文而言通常指的是一个单词,而对于中文而言,对应的是一个词组。
词典(字典):
它是词条的集合,底层通常基于"Btree+"和"HASHMap"实现。
倒排表:
记录了词条出现在什么位置,出现的频率是什么。
倒排表中的每一条记录我们称为倒排项
倒排索引的搜索过程:
(1)首先根据用户需要查询的词条进行分词后,将分词后的各个词条字典进行匹配,验证词条在词典中是否存在;
(2)如果上一步搜索结果发现词条不在字典中,则结束本次搜索,如果在词典中,就需要去查看倒排表中的记录(倒排项);
(3)根据倒排表中记录的倒排项来定位数据在哪个文档中存在,而后根据这些文档的"_id"来获取指定的数据;
综上所述,假设有10亿篇文章,对于mysql不创建索引的情况下,会进行全表扫描搜索"linux"。而对于ES而言,其只需要将倒排表中返回的id进行扫描即可,而无须进行全量查询。
文档的写入操作原理
1. 文档路由和分片选择
- 当你向 Elasticsearch 集群中提交一个写入请求(例如
index
请求)时,Elasticsearch 首先会根据文档的_id
或者你指定的路由值来确定该文档应该写入到哪个主分片。 - Elasticsearch 使用哈希算法计算路由值,进而决定文档会被写入到哪个主分片。例如,如果索引有 5 个主分片,Elasticsearch 会根据哈希值计算出 0 到 4 之间的一个数值,决定写入的目标分片。
- 在一个三节点的集群中,分片会被分布在不同的节点上。每个节点上可以有多个分片(包括主分片和副本分片)。
2. 写入主分片
- 当目标主分片确定后,文档被发送到主分片所在的节点。在这个节点上,Elasticsearch 会先将文档写入到该分片的内存缓存和事务日志(Translog)中。事务日志用于保证在系统崩溃或者断电情况下的数据不会丢失。
- 一旦文档被写入到内存和事务日志中,Elasticsearch 就会异步地将数据刷新到磁盘中的 Lucene 索引中。
3. 同步到副本分片
- 在文档成功写入主分片后,Elasticsearch 会将写入请求并行发送到该主分片对应的所有副本分片(Replica Shard)。每个副本分片会重复主分片的写入过程,确保副本分片的数据与主分片保持一致。
- 在三节点集群中,假设有一个主分片和两个副本分片,主分片和两个副本分片会分别位于不同的节点上,这样就实现了高可用性。
4. 写入确认
- 当所有副本分片都成功写入文档后,它们会向主分片确认写入成功。主分片在收到所有副本的确认后,才会向客户端返回成功响应。
- 这种机制确保了即使某个节点出现故障,只要副本分片在其他节点上仍然可用,数据就不会丢失。
5. 刷入磁盘和持久化
- 文档写入后,数据会首先缓存在内存中,而不是立即写入磁盘。Elasticsearch 定期(默认每 1 秒)将内存中的数据刷新到磁盘上的 Lucene 索引文件中。这就是所谓的刷新操作(Flush)。在刷入磁盘后,文档才算是持久化存储。
- 此外,Elasticsearch 还会定期对事务日志进行合并和清理,确保日志不会无限制地增长。
6. 失败处理
- 如果在写入副本分片的过程中发生错误(如网络中断或节点故障),主分片会通知其他可用副本继续执行写入操作。如果所有副本分片都不可用,主分片仍然可以接受写入,并在副本分片恢复后进行同步。
- 当主分片发生故障时,Elasticsearch 会选举一个副本分片为新的主分片,继续处理后续的写入和查询请求。
文档的读取原理(指定文档id)
1. 路由查询请求
- 当客户端发送查询请求(如
GET
请求来检索某个文档)时,Elasticsearch 首先根据文档的_id
或者路由键来确定该请求应该被路由到哪个主分片或副本分片。 - Elasticsearch 使用与写入过程类似的哈希算法来定位分片。该算法确保特定文档总是会被路由到同一个主分片和副本分片集合中的某个分片。
2. 主分片和副本分片的读取选择
- 当路由确定了目标分片后,Elasticsearch 不仅仅从主分片读取数据,它也可以从副本分片中读取数据。实际上,为了分担读取负载、提升性能,Elasticsearch 会在主分片和副本分片之间随机选择一个分片来响应读取请求。
- 这意味着,查询请求可以被任何一个副本分片或主分片处理,从而充分利用集群中的计算资源。
3. 查询流程
- 集群接收到查询请求后,Elasticsearch 会将请求转发给对应的主分片或副本分片。
- 处理查询的分片会负责从索引中读取文档数据,通常是从 Lucene 的倒排索引中找到文档的位置信息,然后从磁盘或缓存中取出完整的文档。
- 一旦找到文档,该分片会将结果返回给协调节点,协调节点再将结果返回给客户端。
4. 多节点查询优化
- 如果查询涉及多个分片,Elasticsearch 会将查询请求并行发送到相关的多个主分片和副本分片。这种并行处理能够大大提高查询效率。
- 例如,如果一个索引有 3 个主分片,每个主分片都有 2 个副本分片(分布在三台节点上),那么在执行查询时,Elasticsearch 可以并行访问 9 个分片中的任何一个(主分片或副本分片),从而最大化利用集群的资源。
5. 读取的一致性
- Elasticsearch 默认的读取模式是最终一致性。这意味着当文档写入操作完成时,主分片和副本分片之间的数据同步可能会有些延迟。所以在极少数情况下,查询副本分片时可能会返回比主分片稍早的旧数据。
- 但如果用户需要更强的一致性,可以通过在查询时使用
preference
参数将查询路由到特定分片,或者强制读取主分片来避免这种延迟。例如,realtime
查询模式可以确保读取最新的数据。
6. 失败处理
- 如果查询的分片不可用(例如分片所在的节点宕机),Elasticsearch 会自动从其他可用的副本分片中读取数据。副本分片的存在保证了即使主分片或某个副本分片不可用,查询仍然能够继续。
- Elasticsearch 会在后台监控每个节点和分片的状态,并确保在节点恢复时,副本分片能快速同步数据,以保持数据的一致性。
7. 查询结果合并
- 对于复杂查询,Elasticsearch 会将查询请求分发给多个分片(主分片或副本分片),然后每个分片会独立处理查询并返回部分结果。
- 协调节点接收到各个分片的查询结果后,会对结果进行合并、排序、分页等处理,最终返回给客户端。
8. 缓存机制
- Elasticsearch 利用了缓存机制来优化查询性能。查询结果可以被缓存到内存中,以便后续的相同查询可以直接返回缓存结果,而不需要再次检索分片数据。这在处理频繁访问的数据时尤其高效。
- 分片级别的缓存可以提高数据读取效率,并减少对磁盘的访问,提升整个集群的查询响应速度。
文档的读取原理(不指定文档id)
全量查询的步骤
当你向集群发送一个全量查询(如 _search
请求)时,查询会按以下步骤执行:
1. 查询路由到协调节点
- 客户端发送的查询请求首先会被路由到集群中的一个节点,该节点称为协调节点(Coordinating Node)。协调节点的任务是负责接收客户端请求、分发查询任务、合并分片结果并返回最终的响应。
- 任何一个节点都可以作为协调节点,通常是随机选择,也可以通过配置指定。
2. 将查询分发到各个分片
- Elasticsearch 索引中的每个分片(无论是主分片还是副本分片)都可以独立处理查询请求。协调节点会将查询请求分发到所有与该索引相关的分片。对于每个分片,Elasticsearch 会随机选择一个可用的分片(主分片或副本分片)来处理查询请求。
- 例如,如果索引有 3 个主分片和 3 个副本分片,总共有 6 个分片(3 个主分片和 3 个副本分片)。协调节点会为每个逻辑分片(分片 0、分片 1、分片 2)选择一个实际的分片(主分片或副本分片)来执行查询。
3. 分片执行查询
- 每个选中的分片(主分片或副本分片)会独立执行查询操作。分片使用底层的 Lucene 索引和倒排索引来执行查询,找到匹配的文档。
- 查询操作可以包括多种类型,如关键词搜索、全文检索、范围查询等。这些查询会使用倒排索引高效检索到匹配的文档。
4. 分片返回查询结果
- 每个分片在完成查询后,会将查询结果(通常是部分结果集和相关信息,如评分、文档 ID)返回给协调节点。
- 分片返回的数据量可以通过
size
参数进行控制,通常只返回最匹配的文档,而不返回全部文档。
5. 协调节点合并结果
- 当协调节点从所有分片收到结果后,它会对所有分片的查询结果进行合并。合并过程包括对查询结果的排序、分页处理等。
- 如果查询使用了
from
和size
参数,协调节点会根据这些参数在全量结果中返回指定的分页结果,而不需要每个分片都返回完整数据集。
6. 返回最终结果
- 协调节点将合并后的结果返回给客户端,结果通常包括文档的
_source
数据以及其他元信息(如_score
、匹配度等)。