Elasticsearch写入原理深入详解
1. ES相关问题
引用官方文档地址:分片内部原理 | Elasticsearch: 权威指南 | Elastic
为什么Elasticsarch是近实时,而不是准实时?
为什么文档的CRUD (创建-读取-更新-删除) 操作是实时的?
Elasticsearch 是怎样保证更新被持久化在断电时也不丢失数据?
Refresh、flush的作用是什么? 什么时候使用?
Elasticsearch存储怎么让数据保存在磁盘上,而不是在内存上?
为什么删除文档不会立刻释放空间?
2. ES索引、分片、分段的概念
2.1 索引 index
索引是 Elasticsearch 存储、组织和搜索数据的逻辑容器。它类似于 MySQL 中的数据表,一个 Elasticsearch 集群可以包含多个索引。从 Elasticsearch 7.x 开始,Elasticsearch 不再支持多个 type且默认为_doc,并在之后的版本中完全移除,因此索引可以认为是一个数据表而非数据库
2.2 分片 shard
在Elasticsearch中,分片是对索引数据的水平划分和分布。索引被分成多个分片,每个分片可以在集群的不同节点上存储。这种分片的设计提供了一种水平扩展的能力,允许将大量数据分布到多个节点上,从而提高性能和可伸缩性。每个分片就是一个Lucene的实例,具有完整的功能。
ES使用数据**分片(shard)**来提高服务的可用性,将数据分散保存在不同的节点上以降低当单个节点发生故障时对数据完整性的影响,同时使用副本(repiica)来保证数据的完整性。关于分片的默认分配策略,在7.x之前,默认5个primary shard
,每个primary shard
默认分配一个replica
,即5主1副,而7.x之后,默认1主1副
2.3 分段 segment
一个段(segment)是有完整功能的倒排索引,Lucene中的索引指的是段的集合和提交点(commit point,即记录所有分段的文件)。当在这个commit point上进行搜索,就是在这个提交点下面的所有的segment文件中搜索,每个segment返回结果,然后汇总返回给用户。
每当创建新文档时,它们就会被写入新的 Segment 中。 每当创建新文档时,它们都属于一个新的 Segment,并且无需修改前一个 Segment。 如果必须删除文档,则在其原始 Segment 中将其标记为已删除。 这意味着它永远不会从 Segement 中物理删除。
与更新相同:文档的先前版本在上一个 Segment 中被标记为已删除,更新后的版本保留在当前 Segment 中的同一文档 ID下
-
分段内的doc数量上限是2的31次方
-
默认每秒都会生成一个segment文件
-
在分片中搜索将依次搜索每个片段,然后将其结果合并到该分片的最终结果中
查看索引中分段信息的方法:
GET /nlc_works/_segments
shards
: 包含了关于索引各个分片的详细信息。
0
,1
,2
: 这些是分片的编号,表示各个分片的信息。routing
: 分片的路由信息,表示该分片所在的节点、状态等。num_committed_segments
: 分片中已提交的段(segments)数量。num_search_segments
: 分片中用于搜索的段数量。segments
: 分片中的段信息。_2
,_3
,_a
,_b
,_c
,_3
,_4
: 这些是段的标识,表示各个段的信息。generation
: 段的代数,表示段的生成次数。num_docs
: 段中文档的数量。deleted_docs
: 已删除的文档数量。size_in_bytes
: 段的大小(以字节为单位)。memory_in_bytes
: 段占用的内存大小。committed
: 表示该段是否已提交。search
: 表示该段是否用于搜索。version
: Lucene 版本。compound
: 表示该段是否是复合段。attributes
: 段的属性信息。
2.4 索引、分片、分段的关系图示
Lucene 索引就是我们所说的Elasticsearch分片 ,而 Elasticsearch 中的索引是分片的集合。
3. es写入操作
3.1 refresh操作
refresh 操作是指手动或自动刷新索引从in-memory buffer(内存缓冲区)到 filesystem cache(文件系统缓存) 的过程。在 ES中写入的文档首先被索引到内存中的缓冲区(in-memory buffer)创建一个新的segment,执行refresh 操作会使该segment写入filesystem cache(文件系统缓存),同时可以立即被搜索到。
可以通过两种方式触发 refresh 操作:
-
显式刷新(Explicit Refresh):通过发送 refresh 请求到 Elasticsearch API 来手动执行刷新操作。这样可以确保在需要时立即刷新数据,但过于频繁的手动刷新可能会影响性能。
POST /{index}/_refresh
-
自动刷新(Automatic Refresh):Elasticsearch 也支持自动刷新机制。可以配置索引的刷新间隔(refresh interval),使得索引在一定时间内自动刷新。这样可以减少手动操作的需求,但需要权衡刷新频率与性能之间的关系。
PUT /my_index/_settings { "index.refresh_interval": "1s" }
关闭自动刷新
PUT /my_index/_settings { "refresh_interval": -1 }
3.2 flush操作
Elasticsearch 中的 flush 操作是指将 filesystem cache(文件系统缓存) 中的数据写入到磁盘,并清空事务日志(transaction log)文件的过程。这个过程确保了在节点重启或者故障发生时,已经提交但尚未持久化到磁盘上的数据不会丢失。
具体来说,flush 操作包含以下步骤:
-
写入磁盘(Write to Disk):将filesystem cache(文件系统缓存)的 segment 数据刷入磁盘,确保数据持久化。
-
清空事务日志(Clear Transaction Log):清空事务日志文件,以删除已经被持久化的数据。事务日志是用来记录索引操作的,包括写入、更新和删除操作。一旦数据被成功写入到磁盘,相关的事务日志就会被清空,以释放磁盘空间并减少对日志文件的依赖。
flush 操作默认情况下不会经常执行,而是由 Elasticsearch 自动管理。通常,Elasticsearch 会根据一定的条件来触发 flush 操作,例如:
- translog大小达到阈值
- 默认配置下固定时间间隔
- 手动触发 flush
在默认配置下,Elasticsearch 会每隔一段时间(默认是 30 分钟)执行一次 flush 操作。这个时间间隔可以通过配置文件进行调整以满足特定需求。flush 操作的执行频率和时机会受到硬件性能、索引写入频率等因素的影响。
手动触发 flush 操作 API:
POST /{index}/_flush
3.3 commit 提交
commit point 是指在内存中的数据被成功写入到磁盘上并持久化的时间点。当 Elasticsearch 成功将数据写入到磁盘上后,会生成一个新的 commit point,这个点标志着数据的持久化已经完成,之后的操作可以基于这个新的 commit point。
commit 操作流程:
-
写入新的分段(segment):首先,Elasticsearch 将内存中的新文档数据写入到磁盘上,形成一个新的分段。这个新的分段包含了最近添加的文档数据。
-
更新提交点(commit point):随后,Elasticsearch 更新分片的提交点,记录新创建的分段以及相关的元数据信息。。
-
同步磁盘(fsync):为了确保数据的持久化,Elasticsearch 执行磁盘同步操作,将所有在文件系统缓存中等待的写入操作刷新到磁盘上。
-
打开新分段:完成同步后,新创建的分段被打开,使得其中包含的文档数据可以被搜索。新添加的文档就可以立即被查询到。
-
清空内存缓冲区:最后内存中的索引缓冲区被清空,准备接受新的文档数据。下一次的提交操作就可以将新的文档写入到一个新的分段中
commit point 的生成通常发生在以下情况下:
-
显式提交(Explicit Commit):通过 API 或命令向 Elasticsearch 发送显式提交请求,要求将内存中的数据刷新到磁盘上。这种情况下,commit point 会被立即生成。
-
自动提交(Automatic Commit):Elasticsearch 会定期检查内存中的缓冲区,并在达到一定条件时自动触发数据的刷新。这些条件可能包括缓冲区已满、一定的时间间隔已过等。
每个 commit point 记录了当前 Elasticsearch 中所有可用的段(segments)。对于每个 commit point,都会有一个与之对应的 .del 文件,用于记录删除操作。在 Elasticsearch 中,删除操作并不会直接从磁盘上物理移除文档,而是将其标记为已删除状态。这些删除操作会被准确地记录在 .del 文件中,包括了在某个段内哪些文档已被删除。当进行查询操作时,尽管在段中已经标记为删除的文档可以被查询到,但在返回结果时,Elasticsearch 会根据对应 commit point 维护的 .del 文件,将已被删除的文档过滤掉,确保返回的结果是准确的。这一机制保证了即使执行了删除操作,查询结果也会反映出文档的最新状态。
4. Elasticsearch写入步骤拆解
在Elasticsearch中,当数据被写入到一个节点的主分片之后,副本分片会被复制到其他节点。这个过程是由Elasticsearch内部的分片复制机制完成的,通常在写入数据到主分片之后,Elasticsearch会异步地将主分片的数据复制到其他节点上的相应副本分片
当前有三个节点,并且每个索引有一个主分片和一个副本分片,那么数据写入的过程如下:
-
客户端发送写入请求到Nginx负载均衡器
-
Nginx负载均衡器根据负载均衡算法选择一个节点作为协调节点
-
协调节点通过哈希取模算法确定数据要写入的目标节点,并将写入请求转发到目标节点
-
目标节点接收到写入请求后,将数据写入到对应的主分片
-
目标节点开始将主分片的数据异步复制到其他节点上的副本分片
-
其他节点接收到复制请求后,开始接收并存储副本分片的数据
-
primary和replica节点数据写入完成后,协调节点返回结果响应,nginx转发响应结果
Elasticsearch写入步骤图示
5. 相关参考
官网参考:分片内部原理 | Elasticsearch: 权威指南 | Elastic
官网参考:[Inside a Shard | Elasticsearch: The Definitive Guide master] | Elastic
参考链接: Elasticsearch写入原理深入详解-阿里云开发者社区 (aliyun.com)
参考:Elasticsearch:Elasticsearch 中的 refresh 和 flush 操作指南_elasticsearch flush-CSDN博客
本文依照官网和其他资料整理完成,如果有错误之处烦请指出