ES 18 - (底层原理) Elasticsearch写入索引数据的过程 以及优化写入过程
1 Lucene操作document的流程
Lucene将index数据分为segment(段)进行存储和管理.
Lucene中, 倒排索引一旦被创建就不可改变, 要添加或修改文档, 就需要重建整个倒排索引, 这就对一个index所能包含的数据量, 或index可以被更新的频率造成了很大的限制.
为了在保留不变性的前提下实现倒排索引的更新, Lucene引入了一个新思路: 使用更多的索引, 也就是通过增加新的补充索引来反映最新的修改, 而不是直接重写整个倒排索引.
—— 这样就能确保, 从最早的版本开始, 每一个倒排索引都会被查询到, 查询完之后再对结果进行合并.
1.1 添加document的流程
① 将数据写入buffer(内存缓冲区);
② 执行commit操作: buffer空间被占满, 其中的数据将作为新的 index segment 被commit到文件系统的cache(缓存)中;
③ cache中的index segment通过fsync强制flush到系统的磁盘上;
④ 写入磁盘的所有segment将被记录到commit point(提交点)中, 并写入磁盘;
④ 新的index segment被打开, 以备外部检索使用;
⑤ 清空当前buffer缓冲区, 等待接收新的文档.
说明:
(a)
fsync是一个Unix系统调用函数, 用来将内存缓冲区buffer中的数据存储到文件系统. 这里作了优化, 是指将文件缓存cache中的所有segment刷新到磁盘的操作.(b) 每个Shard都有一个提交点(commit point), 其中保存了当前Shard成功写入磁盘的所有segment.
1.2 删除document的流程
① 提交删除操作, 先查询要删除的文档所属的segment;
② commit point中包含一个.del文件, 记录哪些segment中的哪些document被标记为deleted了;
③ 当.del文件中存储的文档足够多时, ES将执行物理删除操作, 彻底清除这些文档.
在删除过程中进行搜索操作:
依次查询所有的segment, 取得结果后, 再根据
.del文件, 过滤掉标记为deleted的文档, 然后返回搜索结果. —— 也就是被标记为delete的文档, 依然可以被查询到.在删除过程中进行更新操作:
将旧文档标记为
deleted, 然后将新的文档写入新的index segment中. 执行查询请求时, 可能会匹配到旧版本的文档, 但由于.del文件的存在, 不恰当的文档将被过滤掉.
2 优化写入流程 - 实现近实时搜索
2.1 流程的改进思路
(1) 现有流程的问题:
插入的新文档必须等待fsync操作将segment强制写入磁盘后, 才可以提供搜索.而 fsync操作的代价很大, 使得搜索不够实时.
(2) 改进写入流程:
① 将数据写入buffer(内存缓冲区);
② 不等buffer空间被占满, 而是每隔一定时间(默认1s), 其中的数据就作为新的index segment被commit到文件系统的cache(缓存)中;
③ index segment 一旦被写入cache(缓存), 就立即打开该segment供搜索使用;
④ 清空当前buffer缓冲区, 等待接收新的文档.
—— 这里移除了fsync操作, 便于后续流程的优化.
优化的地方: 过程②和过程③:
segment进入操作系统的缓存中就可以提供搜索, 这个写入和打开新segment的轻量过程被称为
refresh.
2.2 设置refresh的间隔
Elasticsearch中, 每个Shard每秒都会自动refresh一次, 所以ES是近实时的, 数据插入到可以被搜索的间隔默认是1秒.
(1) 手动refresh —— 测试时使用, 正式生产中请减少使用:
# 刷新所有索引:
POST _refresh
# 刷新某一个索引:
POST employee/_refresh
(2) 手动设置refresh间隔 —— 若要优化索引速度, 而不注重实时性, 可以降低刷新频率:
# 创建索引时设置, 间隔1分钟:
PUT employee
{
"settings": {
"refresh_interval": "1m"
}
}
# 在已有索引中设置, 间隔10秒:
PUT employee/_settings
{
"refresh_interval": "10s"
}
(3) 当你在生产环境中建立一个大的新索引时, 可以先关闭自动刷新, 要开始使用该索引时再改回来:
# 关闭自动刷新:
PUT employee/_settings
{
"refresh_interval": -1
}
# 开启每秒刷新:
PUT employee/_settings
{
"refresh_interval": "1s"
}
3 优化写入流程 - 实现持久化变更
Elasticsearch通过事务日志(translog)来防止数据的丢失 —— durability持久化.
3.1 文档持久化到磁盘的流程
① 索引数据在写入内存buffer(缓冲区)的同时, 也写入到translog日志文件中;
② 每隔refresh_interval的时间就执行一次refresh:
(a) 将buffer中的数据作为新的 index segment, 刷到文件系统的cache(缓存)中;
(b) index segment一旦被写入文件cache(缓存), 就立即打开该segment供搜索使用;
③ 清空当前内存buffer(缓冲区), 等待接收新的文档;
④ 重复①~③, translog文件中的数据不断增加;
⑤ 每隔一定时间(默认30分钟), 或者当translog文件达到一定大小时, 发生flush操作, 并执行一次全量提交:
(a) 将此时内存buffer(缓冲区)中的所有数据写入一个新的segment, 并commit到文件系统的cache中;
(b) 打开这个新的segment, 供搜索使用;
(c) 清空当前的内存buffer(缓冲区);
(d) 将translog文件中的所有segment通过fsync强制刷到磁盘上;
(e) 将此次写入磁盘的所有segment记录到commit point中, 并写入磁盘;
(f) 删除当前translog, 创建新的translog接收下一波创建请求.
扩展: translog也可以被用来提供实时CRUD.
当通过id查询、更新、删除一个文档时, 从segment中检索之前, 先检查translog中的最新变化 —— ES总是能够实时地获取到文档的最新版本.
共计:3599 个字
3.2 基于translog和commit point的数据恢复
(1) 关于translog的配置:
flush操作 = 将translog中的记录刷到磁盘上 + 更新commit point信息 + 清空translog文件.
Elasticsearch默认: 每隔30分钟就flush一次;
或者: 当translog文件的大小达到上限(默认为512MB)时主动触发flush.
相关配置为:
# 发生多少次操作(累计多少条数据)后进行一次flush, 默认是unlimited:
index.translog.flush_threshold_ops
# 当translog的大小达到此预设值时, 执行一次flush操作, 默认是512MB:
index.translog.flush_threshold_size
# 每隔多长时间执行一次flush操作, 默认是30min:
index.translog.flush_threshold_period
# 检查translog、并执行一次flush操作的间隔. 默认是5s: ES会在5-10s之间进行一次操作:
index.translog.interval
(2) 数据的故障恢复:
① 增删改操作成功的标志: segment被成功刷新到Primary Shard和其对应的Replica Shard的磁盘上, 对应的操作才算成功.
② translog文件中存储了上一次flush(即上一个commit point)到当前时间的所有数据的变更记录. —— 即translog中存储的是还没有被刷到磁盘的所有最新变更记录.
③ ES发生故障, 或重启ES时, 将根据磁盘中的commit point去加载已经写入磁盘的segment, 并重做translog文件中的所有操作, 从而保证数据的一致性.
(3) 异步刷新translog:
为了保证不丢失数据, 就要保护translog文件的安全:
Elasticsearch 2.0之后, 每次写请求(如index、delete、update、bulk等)完成时, 都会触发
fsync将translog中的segment刷到磁盘, 然后才会返回200 OK的响应;或者: 默认每隔5s就将translog中的数据通过
fsync强制刷新到磁盘.
—— 提高数据安全性的同时, 降低了一点性能.
==> 频繁地执行fsync操作, 可能会产生阻塞导致部分操作耗时较久. 如果允许部分数据丢失, 可设置异步刷新translog来提高效率.
PUT employee/_settings
{
"index.translog.durability": "async",
"index.translog.sync_interval": "5s"
}
4 优化写入流程 - 实现海量segment文件的归并
4.1 存在的问题
由上述近实时性搜索的描述, 可知ES默认每秒都会产生一个新的segment文件, 而每次搜索时都要遍历所有的segment, 这非常影响搜索性能.
为解决这一问题, ES会对这些零散的segment进行merge(归并)操作, 尽量让索引中只保有少量的、体积较大的segment文件.
这个过程由独立的merge线程负责, 不会影响新segment的产生.
同时, 在merge段文件(segment)的过程中, 被标记为deleted的document也会被彻底物理删除.
4.2 merge操作的流程
① 选择一些有相似大小的segment, merge成一个大的segment;
② 将新的segment刷新到磁盘上;
③ 更新commit文件: 写一个新的commit point, 包括了新的segment, 并删除旧的segment;
④ 打开新的segment, 完成搜索请求的转移;
⑤ 删除旧的小segment.
4.3 优化merge的配置项
segment的归并是一个非常消耗系统CPU和磁盘IO资源的任务, 所以ES对归并线程提供了限速机制, 确保这个任务不会过分影响到其他任务.
(1) 归并线程的速度限制:
限速配置 indices.store.throttle.max_bytes_per_sec的默认值是20MB, 这对写入量较大、磁盘转速较高的服务器来说明显过低.
对ELK Stack应用, 建议将其调大到100MB或更高. 可以通过API设置, 也可以写在配置文件中:
PUT _cluster/settings
{
"persistent" : {
"indices.store.throttle.max_bytes_per_sec" : "100mb"
}
}
// 响应结果如下:
{
"acknowledged": true,
"persistent": {
"indices": {
"store": {
"throttle": {
"max_bytes_per_sec": "100mb"
}
}
}
},
"transient": {}
}
(2) 归并线程的数目:
推荐设置为CPU核心数的一半, 如果磁盘性能较差, 可以适当降低配置, 避免发生磁盘IO堵塞:
PUT employee/_settings
{
"index.merge.scheduler.max_thread_count" : 8
}
(3) 其他策略:
# 优先归并小于此值的segment, 默认是2MB:
index.merge.policy.floor_segment
# 一次最多归并多少个segment, 默认是10个:
index.merge.policy.max_merge_at_once
# 一次直接归并多少个segment, 默认是30个
index.merge.policy.max_merge_at_once_explicit
# 大于此值的segment不参与归并, 默认是5GB. optimize操作不受影响
index.merge.policy.max_merged_segment
4.4 optimize接口的使用
segment的默认大小是5GB, 在非常庞大的索引中, 仍然会存在很多segment, 这对文件句柄、内存等资源都是很大的浪费.
但由于归并任务非常消耗资源, 所以一般不会选择加大 index.merge.policy.max_merged_segment 配置, 而是在负载较低的时间段, 通过optimize接口强制归并segment:
# 强制将segment归并为1个大的segment:
POST employee/_optimize?max_num_segments=1
# 在终端中的操作方法:
curl -XPOST http://ip:5601/employee/_optimize?max_num_segments=1
optimize线程不会受到任何资源上的限制, 所以不建议对还在写入数据的热索引(动态索引)执行这个操作.
实战建议: 对一些很少发生变化的老索引, 如日志信息, 可以将每个Shard下的segment合并为一个单独的segment, 节约资源, 还能提高搜索效率.
参考资料
https://www.elastic.co/guide/cn/elasticsearch/guide/current/making-text-searchable.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/near-real-time.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/translog.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/merge-process.html
版权声明
作者: 马瘦风
出处: 博客园 马瘦风的博客
感谢阅读, 如果文章有帮助或启发到你, 点个[好文要顶
ES 18 - (底层原理) Elasticsearch写入索引数据的过程 以及优化写入过程的更多相关文章
- ES 14 - (底层原理) Elasticsearch内部如何处理不同type的数据
目录 1 type的作用 2 type的底层数据结构 3 探究type的存储结构 3.1 创建索引并配置映射 3.2 添加数据 3.3 查看存储结构 4 关于type的最佳实践 1 type的作用 在 ...
- ES 17 - (底层原理) Elasticsearch增删改查索引数据的过程
目录 1 增删改document的流程 1.1 协调节点 - Coordinating Node 1.2 增删改document的流程 2 查询document的流程 1 增删改document的流程 ...
- 记录一次MySQL两千万数据的大表优化解决过程,提供三种解决方案(转)
问题概述 使用阿里云rds for MySQL数据库(就是MySQL5.6版本),有个用户上网记录表6个月的数据量近2000万,保留最近一年的数据量达到4000万,查询速度极慢,日常卡死.严重影响业务 ...
- elasticsearch批量索引数据示例
示例数据文件document.json(index表示在索引中增加或替换现有文档,create表示如果文档不存在则添加文档,delete表示删除文档): { "index": { ...
- java架构之路-(mysql底层原理)Mysql索引和查询引擎
今天我们来说一下我们的mysql,个人认为现在的mysql能做到很好的优化处理,不比收费的oracle差,而且mysql确实好用. 当我们查询慢的时候,我会做一系列的优化处理,例如分库分表,加索引.那 ...
- ES的底层原理-倒排索引的概念
Elasticsearch底层使用的使用的lucene lucene使用的是倒排索引的方式来进行加快检索速度 倒排索引的原理 doc_1 The quick brown fox jumped ...
- 转载:记录一次MySQL两千万数据的大表优化解决过程
地址:https://database.51cto.com/art/201902/592522.htm 虽然是广告文,但整体可读性尚可.
- 使用Flink实现索引数据到Elasticsearch
使用Flink实现索引数据到Elasticsearch 2018-07-28 23:16:36 Yanjun 使用Flink处理数据时,可以基于Flink提供的批式处理(Batch Proce ...
- ES 12 - 配置使用Elasticsearch的动态映射 (dynamic mapping)
目录 1 动态映射(dynamic mapping) 1.1 什么是动态映射 1.2 体验动态映射 1.3 搜索结果不一致的原因分析 2 开启dynamic mapping策略 2.1 约束策略 2. ...
随机推荐
- python数据存储技巧
1.文本存储 比如我们现在有10篇文章,每篇文章由三部分组成,题目,作者,内容(title,author,content),然后要求这三个部分明确展示出来,并且每篇文章之间用=====分割. 大致思路 ...
- SOFA 源码分析 — 调用方式
前言 SOFARPC 提供了多种调用方式满足不同的场景. 例如,同步阻塞调用:异步 future 调用,Callback 回调调用,Oneway 调用. 每种调用模式都有对应的场景.类似于单进程中的调 ...
- 【转】javascript 分号问题
javascript的分号代表语句的结束符,但由于javascript具有分号自动插入规则,所以它是一个十分容易让人模糊的东西,在一般情况下,一个换行就会产生一个分号,但实际情况却不然,也就是说在ja ...
- 团队项目第二阶段个人进展——Day7
一.昨天工作总结 冲刺第七天,动手完成了一个demo来实现数据的上传与下载 二.遇到的问题 代码逻辑没看太懂 三.今日工作规划 对发布页面的数据进行处理,实现能够请求和响应,并学习如何实现图片的上传与 ...
- WebApiClient库支持AOT
1 库简介 WebApiClient是开源在github上的一个http客户端库,内部基于HttpClient开发,只需要定义c#接口(interface),并打上相关特性,即可异步调用http-ap ...
- TX2 安装 ROS 依赖库错误解决办法
一.更换ubuntu 16.04 更新源 1. 更新源 deb http://mirrors.ustc.edu.cn/ubuntu-ports/ xenial main multiverse rest ...
- 附近的人,附近的卖家(geohash+前缀树)
http://www.cnblogs.com/LBSer/p/3310455.html http://blog.csdn.net/shixiaoguo90/article/details/253137 ...
- centos6 安装配置ss笔记
2018-05-17 centos6 安装配置ss笔记 操作环境:Centos 6 x86_64 bbr 服务器地址:美国 1.准备VPS 在https://www.bwh1.net可购买,购买时已默 ...
- Deep Learning Enables You to Hide Screen when Your Boss is Approaching
https://github.com/Hironsan/BossSensor/ 背景介绍 学生时代,老师站在窗外的阴影挥之不去.大家在玩手机,看漫画,看小说的时候,总是会找同桌帮忙看着班主任有没有来. ...
- 玩转CSS3(一)----CSS3实现页面布局
请珍惜小编劳动成果,该文章为小编原创,转载请注明出处. 摘要: CSS3相对CSS2增加了一些新的布局方式:多栏布局和盒子布局.在这篇文章中,将对CSS2的布局进行简单的回忆,并总结CSS3的 ...