分片、路由、数据写入过程
分片和路由
在Elasticsearch中,若要进行分片和副本配置,则需要尽早配置
因为 当 在一个多分片 的 索引中 写入数据时,需要 通过路由 来确定 具体写入 哪一个分片中
所以 在创建索引时 需要指定 分片的数量,并且分片的数量 一旦确定 就不能修改
分片的数量 和 副本数量 都可以通过 创建索引时的Settings
来配置
Elasticsearch默认 为一个索引 创建5个主分片,并分别为 每个分片 创建一个副本
配置的参数如下所示:
对文档的新建、索引和删除请求等写操作,必须在主分片上面完成之后 才能被复制到 相关的副本分片
Elasticsearch为了加快写入的速度,写入过程往往是并发实施的
为了解决 在并发写的过程中 出现的 数据冲突的问题,Elasticsearch通过乐观锁进行控制,每个文档都有一个version (版本号),当文档被修改时版本号递增
分片如何使用?(和数据写入过程有关)
当向Elasticsearch写入数据时,Elasticsearch 根据 文档标识符ID 将文档 分配到 多个分片上
当查询数据时,Elasticsearch会查询 所有的分片 并 汇总结果
对用户而言,这个过程是透明的,用户并不知道数据到底存在哪个分片上
为了避免在查询时 部分分片 查询失败 影响结果的准确性,Elasticsearch引入了路由功能
即 数据在写入时,通过 路由 将数据 写入 指定分片
在查询时,可以通过 相同的路由 指明 在哪个分片 将数据查出来
在默认情况下,索引数据的分片算法如下所示:
routing字段的取值 默认是 id字段 或者 是parent字段
routing字段 在Hash分片之后 再与 有分片的数量 取模,最终得到 这条数据 应该被分配在 哪一个分片上
这样做的目的 是通过 Hash分片 来保证 在每个分片上 数据量的 均匀分布,避免 各个分片的存储负载不均衡
在做数据检索时,Elasticsearch默认会搜索 所有分片上的数据,最后 在主节点上 汇总 各个分片数据 并进行 排序处理后,返回最终的结果数据
数据写入过程(重要!!!)
数据写入操作是在Elasticsearch的内存中执行的,数据会被分配到特定的分片和副本上,但最终数据是需要存储到磁盘上持久化的
在Elasticsearch中,数据的存储路径在配置文件../config/elasticsearch.yml
中进行设置,具体设置如下:
注:建议不要使用默认值,主要是考虑到当Elasticsearch升级时数据的安全性问题,防止因升级Elasticsearch而导致数据部分甚至全部丢失
1.分段存储
索引数据在磁盘上的 是以 分段形式存储的
“段”是Elasticsearch从Lucene中继承的概念
在索引中,索引文件被拆分为多个子文件,其中每个子文件就叫作段,每个段 都是一个 倒排索引的小单元
段具有不变性,一旦索引的数据被写入硬盘,就不能再修改
为什么要引入分段?
可以试想一下,如果全部的文档集合仅构建在一个很大的倒排索引文件中,且数据量还在不断增加,当进行修改时,需要全量更新当前的倒排索引文件
这会使得数据更新时效性很差、且耗费大量资源
其实在Lucene中,分段的存储模式 可以避免 在读写操作时 使用锁,从而大大提升Elasticsearch的读写性能
这有点类似于CurrentHashMap中“分段锁”的概念,二者有异曲同工之妙,都是为了减少锁的使用,提高并发
当分段被写入磁盘后 会生成 一个提交点,提交点 意味着 一个用来记录 所有段信息的文件 已经生成
因此,一个段 一旦拥有了 提交点,就表示 从此 该段 仅有读的权限,永远失去了写的权限
当段在内存中时,此时分段 拥有 只写的权限,数据还会不断写入,而不具备 读数据的权限,意味着这部分数据 不能被 Elasticsearch用户检索到
既然索引文件分段存储并且不可修改,那么新增、更新和删除如何处理?
新增是比较容易处理的,既然数据是新的,那么只需在当前文档新增一个段即可
删除数据时,由于分段不可修改的特性,Elasticsearch不会把文档 从 旧的段中 移除,而是新增一个.del文件,.del文件中 会记录 这些 被删除文档 的 段信息
被标记删除的文档 仍然可以 被查询匹配到,但它会在 最终结果 被返回前 通过.del文件 将其 从结果集中移除
当更新数据时,由于分段不可修改的特性,Elasticsearch无法通过 修改旧的段 来反映 文档的更新,于是,更新操作 变成了 两个操作的结合,即先删除、后新增
Elasticsearch会将旧的文档从.del文件中标记删除,然后 将文档的新版本 索引到 一个新的段中
在查询数据时,两个版本的文档 都会被 一个查询匹配到,但 被删除的旧版本文档 在结果集返回前 就会被移除
综上所述,段作为不可修改是具有一定优势的,段的优势主要表现在:不需要锁,从而提升Elasticsearch的读写性能
分段不变性的主要缺点 是 存储空间占用量大——当删除旧数据时,旧数据不会被马上删除,而是在.del文件中被标记为删除
而旧数据 只能等到 段更新时 才能被移除,这样就会导致存储空间的浪费
倘若频繁更新数据,则每次更新都是新增新的数据到新分段,并标记旧的分段中的数据,存储空间的浪费会更多
在删除和更新数据时,存储空间会浪费
在检索数据时,依然有局限——在查询得到的结果集中会包含所有的结果集,因此 主节点 需要排除 被标记删除的旧数据,随之带来的是查询的负担
2.延迟写策略
在Elasticsearch中,索引写入磁盘的过程是异步的
为了提升写的性能,Elasticsearch 并没有 每新增一条数据 就 增加一个段 到 磁盘上,而是采用 延迟写策略
延迟写策略的执行过程如下:
每当有新的数据写入时,就将其 先写入 JVM的内存中
在 JVM内存 和 磁盘之间 是 文件系统缓存,文件缓存空间 使用的是 操作系统的空间
当达到默认的时间 或者 JVM内存的数据达到一定量时,会触发一次刷新(Refresh)操作
刷新操作 将 JVM内存中的数据 生成到 一个新的分段上 并缓存到 文件缓存系统,稍后 再被刷新到 磁盘中 并生成 提交点
需要指出的是,由于 新的数据 会继续写入内存,而 JVM内存中的数据 并不是以段的形式 存储的,因此不能提供检索功能
只有当 数据 经由JVM内存 刷新到 文件缓存系统,并生成新的段后,新的段 才能供 搜索使用,而不需要等到 被刷新到 磁盘才可以搜索
在Elasticsearch中,写入和打开一个新段的过程 叫作 刷新
在默认情况下,每个分片 会 每秒自动刷新一次
这就是Elasticsearch能做到近实时搜索的原因,因为 文档的变化 并不是 立即 对搜索可见的,但会在一秒之内变为可见
当然,除自动刷新外,也可以手动触发刷新
还可以在创建索引时,在Settings中通过配置refresh_interval
的值,来调整 索引的刷新频率
在设置值时需要注意后面带上时间单位,否则默认是毫秒
当refresh_interval=-1
时,表示关闭索引的自动刷新
虽然 延迟写策略 可以减少 数据 往 磁盘上写的次数,提升Elasticsearch的整体写入能力,但 文件缓存系统的引入 同时也带来了 数据丢失的风险,如机房断电等
为此,Elasticsearch引入事务日志(Translog)机制
事务日志 用于记录 所有 还没有持持久化到磁盘 的 数据
于是,在添加了事务日志机制后,数据写入索引的流程如下所示:
- 新文档 被 索引之后,先被写入 JVM内存中。为了防止数据丢失,Elasticsearch 会 追加一份数据 到 事务日志中
- 新的文档 持续在被 写入JVM内存时,同时 也会记录到 事务日志中。当然,此时的新数据 还不能被检索和查询
- 当 达到 默认的刷新时间 或 JVM内存中的数据达到一定量后,Elasticsearch会触发一次刷新,将JVM内存中的数据 以一个新段形式 刷新到 文件缓存系统中 并 清空JVM内存。这时新段虽未被提交到磁盘,但已经可以对外提供文档的检索功能 且 不被修改
- 随着 新文档索引 不断被写入,当日志数据大小超过某个值,或者超过一定时间时,Elasticsearch会触发一次Flush
此时,JVM内存中的数据 被写入 一个新段,同时被写入 文件缓存系统,文件缓存系统中的数据 通过Fsync
刷新到磁盘中,生成提交点。而日志文件被删除,创建一个空的新日志
3.段合并
在Elasticsearch自动刷新流程中,每秒都会创建一个新的段
这自然会导致短时间内段的数量猛增,而当段数量太多时会带来较大的资源消耗,如对文件句柄、内存和CPU的消耗
而在内容搜索阶段,由于搜索请求要检查到每个段,然后合并查询结果,因此段越多,搜索速度越慢
为此,Elasticsearch引入段合并机制
段合并机制在后台定期进行,从而小的段被合并到大的段,然后这些大的段再被合并到更大的段
在段合并过程中,Elasticsearch会将那些 旧的已删除文档 从文件系统中 清除
被删除的文档 不会被 拷贝到新的大段中,当然,在合并的过程中不会中断索引和搜索
段合并 是 自动进行索引和搜索的,在合并进程中,会选择一小部分大小相似的段,在后台将它们合并到更大的段中,这些段既可以是未提交的,也可以是已提交的
在合并结束后,老的段会被删除,新的段被Flush到磁盘,同时 写入一个 包含新段 且 排除旧的 和 较小的段 的 新提交点。打开新的段之后,可以用来搜索。
由于段合并的计算量较大,对磁盘I/O的消耗也较大,因此 段合并 会影响 正常的数据写入速率,因此Elasticsearch不会放任自流,让段合并影响搜索性能
Elasticsearch在默认情况下 会对 合并流程 进行 资源限制,这就是搜索服务仍然有足够的资源仍然可以执行的原因