ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降
目前在绝对多数公司在使用 ElasticSearch 将其当做数据库使用,将多个数据库中的数据同步到 ElasticSearch 索引是非常常见的应用场景。那么自然而然就会涉及到数据频繁的新增和更新,而官方的文档并没有对 update 的底层机制做特别说明,而当我们从 2.x 版本升级到 5.x 发现反而比之前的性能差很多,那这到底是怎么回事呢?
问题描述
在 ElasticSearch5.x 里通过 bulk update 将数据从数据库同步到 ElasticSearch,如果短时间更新的一批数据里存在相同的 id,例如一个 bulk update 里大量写入下面类型的数据:
{id:1,name:aaa}
{id:1,name:bbb}
{id:1,name:ccc}
{id:2,name:aaa}
{id:2,name:bbb}
{id:2,name:ccc}
.......
则更新的速度会变的非常的慢。而在 ElasticSearch1.x 和 ElasticSearch2.x 里同样的操作会快的多。
根源追溯
update 操作时分为两个步骤进行的,即先根据文档 id 做一次 GET 操作,得到旧版本的文档,然后在其基础上做更新再写回去,问题就出在这个 GET 上面。
在 core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java 这个类里,get 函数会根据一个 realtime 参数(默认是 true),决定如何拿到文档。
public GetResult get(Get get, Function<String, Searcher> searcherFactory, LongConsumer onRefresh) throws EngineException {
assert Objects.equals(get.uid().field(), uidField) : get.uid().field();
try (ReleasableLock lock = readLock.acquire()) {
ensureOpen();
if (get.realtime()) {
VersionValue versionValue = versionMap.getUnderLock(get.uid());
if (versionValue != null) {
if (versionValue.isDelete()) {
return GetResult.NOT_EXISTS;
}
if (get.versionType().isVersionConflictForReads(versionValue.getVersion(), get.version())) {
throw new VersionConflictEngineException(shardId, get.type(), get.id(),
get.versionType().explainConflictForReads(versionValue.getVersion(), get.version()));
}
long time = System.nanoTime();
refresh("realtime_get");
onRefresh.accept(System.nanoTime() - time);
}
}
// no version, get the version from the index, we know that we refresh on flush
return getFromSearcher(get, searcherFactory);
}
可以看到 realtime 参数决定了 GET 到的数据的实时性。如果设置为 false,则直接从 searcher 里面拿,而 searcher 只能访问 refresh 过的数据,意味着刚写入的数据由于存在于 index writer buffer 里还未 refresh,暂时无法搜索到,所以这种方式拿到的数据是准实时的。而默认的 realtime=true,则决定了获取到的数据必须是实时的,也就是说 index writer buffer 里的数据也要能被检索到。从代码里可以看到,其中存在一个 refresh("realtime_get"); 的函数调用,这个函数会检查 GET 的 doc id 是否都是可以被搜索到的。如果已经写入了但是无法搜索到,也就是刚刚写入到 index writer buffer 里还未 refresh 这种情况,就会强制执行一次 refresh 操作,让数据对 searcher 可见,保证最后的 getFromSearcher 方法调用拿的是完全实时的数据。
实际上测试下来,也是这样的,关闭自动刷新,写入一条文档,然后对该文档 id 执行一次 GET 操作,就会看到有一个新的 segment 生成。
查了下文档,GET API 调用时,可以选择实时或者非实时,只需要在 url 里带上可选参数 realtime=[true/false]。
然而不幸的是,UPDATE API 的文档和源码都没有提供一个 “禁用” 实时性的参数。update 对 GET 的调用,传入的 realtime 参数是写死的即为 true,意味着 update 的时候,强制执行 realtime GET。
至于为什么 update 一定要需要实时 GET,想一下其实也可以理解的。因为 update 允许对文档进行局部更新,如果有两个请求分别更新了同一个文档的不同字段,可能先更新的数据还在 index writer buffer 里,没来得及 refresh,因为对 searcher 不可见。如果不做refresh,那后面的更新还是在老版本上做的,前面的更新就会丢失掉。
另外一个问题,为啥 ElasticSearch5.x 之前的版本没有这个性能问题呢?
看了 ElasticSearch2.4 的 GET 方法源码,其没有采用 refresh 的方式来保障数据的实时性,而是通过访问 translog 来达到同样的目的。ElasticSearch5.x 将机制从 translog 改为了 refresh,理由是之前 ElasticSearch 里有很多地方利用 translog 来位数数据的位置,使得很多操作变得很慢,去掉对 translog 的依赖可以全面提高性能。
很遗憾,这个更改对于短时间反复大量更新相同的 doc id 的操作,会因为过于频繁的强制 refresh,短时间生成很多小的 segment,继而不断触发 segment 合并,产生性能损耗。官方认为,在提升大多数应用场景性能的前提下,对于这种较少见的场景下的性能损耗是值得付出的。所以建议从应用层面去解决该问题。
因此,如果实际应用场景里遇到类似的数据更新问题,只能优化应用数据架构,在应用层面合并相同的 doc id 的数据更新后再写入到 ElasticSearch 索引中;或者 只能使用 ElasticSearch2.x 这样的老版本来间接的解决这类问题。
ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降的更多相关文章
- ElasticStack系列之十六 & ElasticSearch5.x index/create 和 update 源码分析
开篇 在ElasticSearch 系列十四中提到的问题即 ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降,继续这个问 ...
- ElasticStack系列之十八 & ElasticSearch5.x XPack 过期新 License 更新
摘要 当你某一天打开 Kibana 对应的 Monitoring 选项卡的时候,发现提示需要下载新的 license,旧的 license 已经过期了,试用期为30天,如果不是很需要其他的复杂监控.报 ...
- Chrome浏览器扩展开发系列之十四
Chrome浏览器扩展开发系列之十四:本地消息机制Native messaging 时间:2015-10-08 16:17:59 阅读:1361 评论:0 收藏:0 ...
- webpack4 系列教程(十四):Clean Plugin and Watch Mode
作者按:因为教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十四):Clean Plugin and Watch Mode>原文地址.更欢迎 ...
- OSGi 系列(十四)之 Event Admin Service
OSGi 系列(十四)之 Event Admin Service OSGi 的 Event Admin 服务规范提供了开发者基于发布/订阅模型,通过事件机制实现 Bundle 间协作的标准通讯方式. ...
- Java 设计模式系列(十四)命令模式(Command)
Java 设计模式系列(十四)命令模式(Command) 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复 ...
- Chrome浏览器扩展开发系列之十四:本地消息机制Native messagin
Chrome浏览器扩展开发系列之十四:本地消息机制Native messaging 2016-11-24 09:36 114人阅读 评论(0) 收藏 举报 分类: PPAPI(27) 通过将浏览器 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- “全栈2019”Java第二十四章:流程控制语句中决策语句switch下篇
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
随机推荐
- 20181023-11 Alpha发布
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2283 文案: Learning by Playing 界面清爽明快,UI ...
- D.王者荣耀交流协会——PSP Daily(测评人:贾男男)
D.王者荣耀交流协会——PSP Daily(测评人:贾男男) 一.基于NABCD评论作品,及改进建议 每个小组评论其他小组beta发布的作品.1.根据(不限于)NABCD评论作品的选题;2.评论作品对 ...
- 第26次Scrum会议(11/14)【欢迎来怼】
一.小组信息 队名:欢迎来怼小组成员队长:田继平成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/11/14 11:35~11:57,总计22min.地点:东北 ...
- Data truncation: Truncated incorrect DOUBLE value:
在写sql查询语句queryRunner.update(connection,"update account set balance=? where name=?",account ...
- Python开发【第五篇】迭代器、生成器、递归函数、二分法
阅读目录 一.迭代器 1. 迭代的概念 #迭代器即迭代的工具(自定义的函数),那什么是迭代呢? #迭代:指一个重复的过程,每次重复都可以称之为一次迭代,并且每一次重复的结果是下一个迭代的初始值(例如: ...
- Internet History, Technology and Security (Week 5-2)
Week 5 (续) Layer 2: Internet Protocol The InterNetwork (IP) 老师强调了一下不用去记住他介绍的人所说的每句话,而是记住要点,了解老师所做的PP ...
- qq飞车精灵家园里的背景音乐:Mysterious Town pooka 下载
一直都觉得Mysterious Town pooka特别好听,但是酷狗音乐和网上直接搜搜不到,于是我直接从源文件中找了出来.虽然是.ogg格式,但是在酷狗音乐里还是可以播放的.貌似是<奥丁领 ...
- 关于SVM数学细节逻辑的个人理解(一) :得到最大间隔分类器的基本形式
网上,书上有很多的关于SVM的资料,但是我觉得一些细节的地方并没有讲的太清楚,下面是我对SVM的整个数学原理的推导过程,其中逻辑的推导力求每一步都是有理有据.现在整理出来和大家讨论分享. 因为目前我的 ...
- 微信 小程序布局 scroll-view
//滚动触底事件 <scroll-view scroll-y lower-threshold="0" bindscrolltolower="scrollBott ...
- web.config文件详解[转]
一).Web.Config是以XML文件规范存储,配置文件分为以下格式1.配置节处理程序声明特点: 位于配置文件的顶部,包含在<configSections>标志中.2.特定应用程序配置特 ...