无论才能、知识多么卓著,如果缺乏热情,则无异纸上画饼充饥,无补于事。

社区日报 第47期 (2017-09-14)

社区日报白衬衣 发表了文章 • 0 个评论 • 2349 次浏览 • 2017-09-14 09:54 • 来自相关话题

1.喜大普奔,elasticsearch的java高层级rest client发布了!
http://t.cn/RpWnkWI
2.干货:携程wood叔告诉你,ES 5.x Bulk update重复的文档id为什么性能低下。
https://elasticsearch.cn/article/273
3.详解elasticsearch中的乐观并发控制。
http://t.cn/RpWnrDX

编辑:金桥
归档:https://elasticsearch.cn/article/274
订阅:https://tinyletter.com/elastic-daily

有10台机器构成一个集群,其中一个index,需要指定在第1-5台创建shard

Elasticsearchkennywu76 回复了问题 • 4 人关注 • 2 个回复 • 3346 次浏览 • 2017-09-14 11:45 • 来自相关话题

elasticsearch5提升副本复制速率

回复

Elasticsearchminsa 发起了问题 • 1 人关注 • 0 个回复 • 2003 次浏览 • 2017-09-14 09:19 • 来自相关话题

菜鸟一只,请教下各位老司机,es的评分规则是什么,有没有相关文档给介绍下

Elasticsearchnovia 回复了问题 • 2 人关注 • 1 个回复 • 4882 次浏览 • 2017-09-14 09:46 • 来自相关话题

elk怎么做数据迁移

Elasticsearch匿名用户 回复了问题 • 3 人关注 • 2 个回复 • 5182 次浏览 • 2017-09-14 09:11 • 来自相关话题

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

Elasticsearchkennywu76 发表了文章 • 16 个评论 • 10608 次浏览 • 2017-09-13 17:10 • 来自相关话题

【携程旅行网  吴晓刚】


更新 @2018/07/20: ES 6.3解决了这个问题,对应的pull request: [#29264](https://github.com/elastic/elasticsearch/pull/29264)

本文是针对社区问题[question#2352](https://elasticsearch.cn/question/2352)的分析和总结

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

---

问题描述


在ES5.x里通过bulk update将数据从数据库同步到ES,如果短时间更新的一批数据里存在相同的文档ID,例如一个bulk update里大量写入下面类型的数据:
<br /> {id:1,name:aaa} <br /> {id:1,name:bbb}<br /> {id:1,name:ccc}<br /> {id:2,name:aaa}<br /> {id:2,name:bbb}<br /> {id:2,name:ccc}<br /> .......<br />
则更新的速度非常慢。  而在ES 1.x和2.x里同样的操作快得多

---

根源追溯


update操作是分为两个步骤进行,即先根据文档ID做一次GET,得到最新版本的文档,然后在内存里做好更新后,再写回去。问题就出在这个GET操作上面。

core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java 这个类里面,get函数会根据一个realtime参数(默认是true),决定如何获取原始文档。 
<br /> public GetResult get(Get get, Function<String, Searcher> searcherFactory, LongConsumer onRefresh) throws EngineException {<br /> assert Objects.equals(get.uid().field(), uidField) : get.uid().field();<br /> try (ReleasableLock lock = readLock.acquire()) {<br /> ensureOpen();<br /> if (get.realtime()) {<br /> VersionValue versionValue = versionMap.getUnderLock(get.uid());<br /> if (versionValue != null) {<br /> if (versionValue.isDelete()) {<br /> return GetResult.NOT_EXISTS;<br /> }<br /> if (get.versionType().isVersionConflictForReads(versionValue.getVersion(), get.version())) {<br /> throw new VersionConflictEngineException(shardId, get.type(), get.id(),<br /> get.versionType().explainConflictForReads(versionValue.getVersion(), get.version()));<br /> }<br /> long time = System.nanoTime();<br /> refresh("realtime_get");<br /> onRefresh.accept(System.nanoTime() - time);<br /> }<br /> }<br /> <br /> // no version, get the version from the index, we know that we refresh on flush<br /> return getFromSearcher(get, searcherFactory);<br /> }<br />

可以看到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](https://www.elastic.co/guide/e ... altime)。

然而,不幸的是,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](https://github.com/elastic/elasticsearch/pull/20102)将机制从访问translog改为了refresh。理由是之前ES里有很多地方利用translog来维护数据的位置,使得很多操作变得很慢,去掉对translog的依赖可以全面提高性能。

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

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

关于java里使用Script的问题

回复

Elasticsearchjianwwei 回复了问题 • 1 人关注 • 1 个回复 • 4878 次浏览 • 2017-09-13 17:04 • 来自相关话题

使用kibana中的graph功能时报了fielddata的错误,需要将fielddata改为ture,不知道怎么操作?

KibanaJonRambo 回复了问题 • 4 人关注 • 3 个回复 • 7116 次浏览 • 2020-06-04 10:54 • 来自相关话题

IK中文分词匹配,匹配度不符理想值

Elasticsearch夏李俊 回复了问题 • 3 人关注 • 3 个回复 • 5233 次浏览 • 2017-09-13 18:02 • 来自相关话题

es 多type 数据结合查询

回复

Elasticsearchsaga 发起了问题 • 1 人关注 • 0 个回复 • 3516 次浏览 • 2017-09-13 14:22 • 来自相关话题

search guard运行quick start中的demo,如何验证配置成功?

回复

ElasticsearchJustRun 回复了问题 • 1 人关注 • 1 个回复 • 4014 次浏览 • 2017-09-13 10:29 • 来自相关话题

社区日报 第46期 (2017-09-13)

社区日报千夜 发表了文章 • 0 个评论 • 4463 次浏览 • 2017-09-13 08:20 • 来自相关话题

1. 来自哔哩哔哩的日志系统
http://t.cn/RpNq18p 
2.亚马逊自研的存储检索系统与Elasticsearch的全方位对比
http://t.cn/RpN578C 
3.用Elasticsearch存储Kubernetes监控数据并用Kibana展示
http://t.cn/RpN9Piz 
 
编辑:江水
归档:https://elasticsearch.cn/article/272 
订阅:https://tinyletter.com/elastic-daily
 

Elasticsearch 聚合时,如何获取非聚合字段的值

Elasticsearchwkdx 回复了问题 • 10 人关注 • 7 个回复 • 7184 次浏览 • 2020-10-10 18:40 • 来自相关话题

logstash安装logstash-input-mongodb 写.conf es获取到索引没有数据 可是启动logstash一直有数据写入的

Logstashfantesycjt 回复了问题 • 4 人关注 • 5 个回复 • 5487 次浏览 • 2018-01-25 11:18 • 来自相关话题

select a, b, sum(c) fromt table group by a, b order by sum(c); 这种sql 用es怎么实现

Elasticsearchzhangli 回复了问题 • 2 人关注 • 4 个回复 • 6158 次浏览 • 2017-09-12 18:42 • 来自相关话题