现在很多公司(包括我们自己)将ES用作数据库数据的索引,将多个数据库的数据同步到ES是非常常见的应用场景。所以感觉这个问题可能会困扰不止一个用户,而官方的文档也没有对update的底层机制及局限做特别说明,特将该问题的讨论和结论整理成文,供社区用户参考。


问题描述

在ES5.x里通过bulk update将数据从数据库同步到ES,如果短时间更新的一批数据里存在相同的文档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}.......

则更新的速度非常慢。  而在ES 1.x和2.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 flushreturn getFromSearcher(get, searcherFactory);}

可以看到realtime参数决定了是否以实时的方式获取数据。 如果设置为false,意味着不关心实时性,此时直接从searcher对象里面拿数据。因为searcher只能访问refresh过的数据,那些刚写入到indexing writter buffer里,还未经历过refresh的数据不会被访问到,故而该读取方式是准实时(Near Real Time)。 而这个realtime参数默认设置是true,说明需要以实时的方式访问数据,也就是说writter buffer里未经refresh的数据也要能被检索到,如何保证这块数据也能被实时访问呢?

从代码里可以看到,其中存在一个refresh("realtime_get") 的函数调用。这个函数调用会检查,GET的doc id是否都是可以被搜索到。 如果已经写入了但无法搜索到,也就是刚刚写入到writter buffer里还未refresh这种情况,就会强制执行一次refresh操作,让数据对searcher可见,保证getFromSearcher调用拿的是完全实时的数据。

实际上测试下来,正是这样的结果: 在关闭索引的自动刷新的情况下(设置refresh_interval: -1,只写入一条文档,然后对该文档ID执行一个GET操作,就会看到有一个新的segment生成。 说明GET的过程触发了refresh。

查了下文档,如果仅仅是做GET API调用,这个实时性可以人为控制,只需要在url里带可选参数realtime=[true/|false]。 参考: reference/5.6/docs-get.html#realtime。

然而,不幸的是,update API的文档和源码都没有提供一个禁用实时性的参数。 update对GET的调用,传入的realtime参数是在代码里写死为true的,意味着update的时候,必须强制执行一次realtime GET.

为什么是这样的代码逻辑,仔细想一下就也就了然了。因为update允许对文档做部分字段更新,如果有2个请求分别更新了同一个文档的不同字段, 可能先更新的数据还在writter buffer里,没来得及refresh,因而对searcher不可见。如果后续更新不做一次refresh,前面的更新可能就丢失了。

另外一个问题,为啥5.x之前的版本没有这个性能问题?  看了下2.4的GET方法源码,其的确没有采用refresh的方式来保障数据的实时性,而是通过访问translog来达到同样的目的。官方在这个变更里pull#20102将机制从访问translog改为了refresh。理由是之前ES里有很多地方利用translog来维护数据的位置,使得很多操作变得很慢,去掉对translog的依赖可以全面提高性能。

很遗憾,这个更改对于短时间反复大量更新相同doc id的操作,会因为过于频繁的强制refresh,短时间生成很多小segment,继而不断触发segment合并,产生显著的性能损耗。 从上面链接里的讨论看,官方认为,在提升大多数应用场景性能的前提下,对于这种较少见的场景下的性能损失是值得付出的。所以,建议从应用层面去解决。

因此,如果实际应用场景里遇到类似的数据更新问题, 只能是优化应用数据架构,在应用层面合并相同doc id的数据更新后再写入ES,或者只能使用ES 2.x这样的老版本了。

注意:此问题在es6.3以上的版本已经解决

文章来源:https://elasticsearch.cn/article/273

ES 5.x Bulk update重复的文档id性能低下相关推荐

  1. ES 5.x bulk update重复的文档ID性能低下分析

    目前很多公司将ES作为数据库数据的索引,将多个数据库的数据同步到ES是非常常见的应用场景.所以感觉问题可能会困扰不止一个用户,而官方的文档没有对update底层机制作了详细的说明,特将该问题整理成文章 ...

  2. es springboot 不设置id_springboot整合ES_文档ID删除

    1.本课程涵盖**SpringBoot2.x版本10个常用技术点 适应企业开发要求,学习IDEA开发工具下的SpringBoot2.x开发 学习SpringBoot2.x - 基于Restful接口开 ...

  3. Elasticsearch——使用Java API实现ES中的索引、映射、文档操作

    文章目录: 1.开篇 2.案例详解 2.1 创建ES客户端:完成与ES服务端的连接 2.2 创建索引 2.3 查看索引 2.4 删除索引 2.5 创建文档 2.6 修改文档 2.7 查看文档 2.8 ...

  4. java spark es_JavaSpark写RDD到ES时指定文档ID的

    spark版本:2.3 ES版本:7.1 问题描述: 在通过JavaSpark写入数据到ES过程中,需要对文档id进行手动指定. 在查询ES官网之后,返现官网介绍中是有次方法的: 可以看到,JavaE ...

  5. Logstash:运用 fingerprint 过滤器处理重复的文档

    这篇文章介绍了使用 Logstash 在 Elasticsearch中 对数据进行重复数据删除的方法. 根据你的用例,Elasticsearch中 的重复内容可能不被接受. 例如,如果你要处理指标,则 ...

  6. 索引重建的必要性与影响 (文档 ID 1525787.1)

    索引重建的必要性与影响 (文档 ID 1525787.1) Index Rebuild, the Need vs the Implications (文档 ID 989093.1) 索引重建的必要性与 ...

  7. 如何修改集群的公网信息(包括 VIP) (文档 ID 1674442.1)

    如何修改集群的公网信息(包括 VIP) (文档 ID 1674442.1) 文档内容 用途 适用范围 详细信息   情况1.   修改公网对应的主机名   情况2.  只修改公网 IP或者VIP, 但 ...

  8. RAC 和 Oracle Clusterware 最佳实践和初学者指南(平台无关部分) (文档 ID 1526083.1)

      RAC 和 Oracle Clusterware 最佳实践和初学者指南(平台无关部分) (文档 ID 1526083.1) 转到底部 文档内容 用途   适用范围   详细信息   RAC 平台特 ...

  9. Pre-Upgrade Utility---下载并运行Oracle数据库预升级实用程序 (文档 ID 1577379.1)

    如何下载并运行Oracle数据库预升级实用程序 (文档 ID 1577379.1) 文档内容 目标 脚本来源 解决方案 脚本指导 下载并安装说明 请参阅下表以确定您需要的pre-upgrade脚本的版 ...

最新文章

  1. HTML Window.document
  2. 8086逻辑运算和移位
  3. pip安装软件时出现Command python setup.py egg_info failed with error code 1 in /tmp/pip-build*的解决方案
  4. SAP UI5 初学者教程之十 - 什么是 SAP UI5 应用的描述符 Descriptor 试读版
  5. IDE-Ecplise-代码注释 模版 编码规范 配色
  6. Homebrew命令具体解释
  7. 敏捷开发“松结对编程”系列之七:问题集之一
  8. 计算机视觉基础:图像处理Task01-图像插值算法
  9. python编程入门-Python编程:从入门到实践 PDF 中文扫描版
  10. 枚举算法典型的三个例子
  11. 基于springboot助学贷款管理毕业设计源码061528
  12. 11月 第4周 GitChat 话题排行榜
  13. FATAL Exited too quickly (process log may have details)
  14. C语言中用零作为终止标记,求出一批非零整数中的偶数.奇数的平均值,用零作为终止标记的程序...
  15. 搭建简单windows版NAS
  16. Laravel执行定时任务
  17. Polar SC的C++实现
  18. SeNet || 注意力机制——源代码+注释
  19. 寒假学习打卡第一篇文章-----numpy的学习
  20. matlab画图中颜色渐变

热门文章

  1. 微软一年从中国挣走8亿美金 - 老杳 - 网易博客
  2. 车速表 html 效果,车速表速度显示的问题,终于弄明白了!
  3. php生成密码及密码检验
  4. 西门子200恒压供水梯形图_西门子plc如何做恒压供水【实例】
  5. 计算机考研407,407分考入985高校,考研经验分享给大家!
  6. mysql查询姓名第二个字_Mysql(2)查询实例
  7. Xiyou Linux Group 2017,2018,2019面试题
  8. MOF (Meta Object Facility) 规范
  9. 在哪下载应聘岗位通用求职简历Word模板
  10. Android 一个应用启动另外一个应用