https://segmentfault.com/a/1190000011272749

社区里面有人问了如下一个问题:

执行 bulk 索引文档的时候,用 index 或者 create 类型并且自定义 doc id 的情况下,是否会像 update 一样每次都要去 get 一遍原始文档? 比如下面的这条命令:

POST _bulk{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } } { "field1" : "value1" } { "create" : { "_index" : "test", "_type" : "type1", "_id" : "3" } } { "field1" : "value3" }

问题出现的原因是他们在 bulk 测试的时候遇到了写性能的问题,而正巧社区里面前几天有这么一个类似的帖子,说的是 es 5.x 版本里面做 update 操作的性能问题。虽然和这个问题不完全一致,但都涉及到 es 索引数据的部分。

侯捷老师说:“源码面前,了无秘密”,那我们就来简单看下 es 这部分的相关代码,以便回答开篇提出的问题。

准备工作

我是用 IntelliJ IDEA 来阅读 elasticsearch 源码的,操作也简单。操作步骤如下:

  1. 下载 es 源码,由于 es 的commit信息比较多,可以增加 --depth=1 只下载最近的commit,减少下载时间。

    git clone https://github.com/elastic/elasticsearch.git --depth=1
  2. 安装 gradle,确保版本在 3.3 及以上,然后在源码目录下执行以下命令准备导入 IntelliJ IDEA 需要的文件

    gradle idea
  3. 下载安装 IntelliJ IDEA,确保版本为 2017.2 及以上版本。安装完成后,将 elasticsearch 以 gradle 形式导入即可。

大家可以参考 elasticsearch 文档说明 和 Elasticsearch源码分析—环境准备 这两篇文章,细节我这里就不赘述了。

另外我是分析的 5.5.0 分支,大家记得 checkout,防止行数对应不起来。另外由于 es 代码结构有些复杂,先不在这篇文章里面梳理整个流程了,直接说核心代码。

Index/Create 源码分析

es index 和 create 最终都会调用 org/elasticsearch/index/engine/InternalEngine.java 中下面的方法:

457 public IndexResult index(Index index) throws IOException

注意这里的 index 中包含有要写入的 doc, 简单画下该方法的执行流程图,代码这里就不贴了,刚兴趣的自己去看。

请结合上面的流程图来看相应的代码,整个逻辑应该还是很清晰的,接下来我们看 planIndexingAsPrimary 的逻辑。

558 private IndexingStrategy planIndexingAsPrimary(Index index) throws IOException {

这个方法最终返回一个 IndexingStrategy,即一个索引的策略,总共有如下几个策略:

  • optimizedAppendOnly
  • skipDueToVersionConflict
  • processNormally
  • overrideExistingAsIfNotThere
  • skipAsStale

不同的策略对应了不同的处理逻辑,前面3个是常用的,我们来看下流程图。

这里的第一步判断 是否是自定义 doc id?这一步就是 es 对于日志类非自定义 doc id的优化,感兴趣的可以自己去看下代码,简单讲就是在非自定义 id 的情况下,直接将文档 add ,否则需要 update,而 update 比 add 成本高很多。

而第二个判断 检查版本号是否冲突? 涉及到是如何根据文档版本号来确认文档可写入,代码都在index.versionType().isVersionConflictForWrites方法里,逻辑也比较简单,不展开讲了,感兴趣的自己去看吧。

上面的流程图也比较清晰地列出了策略选择的逻辑,除去 optimizedAppendOnly 策略,其他都需要根据待写入文档的版本号来做出决策。接下来我们就看下获取文档版本号的方法。

389 private VersionValue resolveDocVersion(final Operation op) throws IOException {

该方法逻辑比较简单,主要分为2步:

  1. 尝试从 versionMap 中读取待写入文档的 version,也即从内存中读取。versionMap 会暂存还没有 commit 到磁盘的文档版本信息。
  2. 如果第 1 步中没有读到,则从 index 中读取,也即从文件中读取。

看到这里,开篇问题便有了答案。es 在 index 或者 create 的时候并不会 get 整个文档,而是只会获取文档的版本号做对比,而这个开销不会很大。

Update 源码分析

es update 的核心代码在 org/elasticsearch/action/update/UpdateHelper.java 中,具体方法如下:

    public Result prepare(UpdateRequest request, IndexShard indexShard, LongSupplier nowInMillis) { final GetResult getResult = indexShard.getService().get(request.type(), request.id(), new String[]{RoutingFieldMapper.NAME, ParentFieldMapper.NAME, TTLFieldMapper.NAME, TimestampFieldMapper.NAME}, true, request.version(), request.versionType(), FetchSourceContext.FETCH_SOURCE); return prepare(indexShard.shardId(), request, getResult, nowInMillis); }

代码逻辑很清晰,分两步走:

  1. 获取待更新文档的数据
  2. 执行更新文档的操作

第 1 步最终会调用 InternalEngine 中的 get 方法,如下:

350 public GetResult get(Get get, Function<String, Searcher> searcherFactory, LongConsumer onRefresh) throws EngineException {

这里就接上开篇提到的社区问题中的源码分析了。代码就不展开讲了,感兴趣的自己去看吧。

update 操作需要先获取原始文档的原因也很简单,因为这里是允许用户做部分更新的,而 es 底层每次更新时要求必须是完整的文档(因为 lucene 的更新实际是删除老文档,新增新文档),如果不拿到原始数据的话,就不能组装出更新后的完整文档了。

因此,比较看重效率的业务,最好还是不要用 update 这种操作,直接用上面的 index 会更好一些。

总结

本文通过源码分析的方式解决了开篇提到的问题,答案简单总结在下面。

es 在 index 和 create 操作的时候,如果没有自定义 doc id,那么会使用 append 优化模式,否则会获取待写入文档的版本号,进行版本检查后再决定是否写入lucene。所以这里不会去做一个 get 操作,即获取完整的文档信息。

最后,记住侯捷老师的话:

源码面前,了无秘密!

转载于:https://www.cnblogs.com/davidwang456/articles/9923885.html

elasticsearch index、create和update的源码分析相关推荐

  1. 一、后台首页index.php【dedecms后台源码分析】

    后台目录dede目录的所有问价的源码分析 使用的dedecms的版本5.7(2012-04-01更新) 后台登陆之后的首页分析dede/index.php <?php /*** 管理后台首页** ...

  2. ElasticStack系列之十六 ElasticSearch5.x index/create 和 update 源码分析

    开篇 在ElasticSearch 系列十四中提到的问题即 ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降,继续这个问 ...

  3. Elasticsearch CCR源码分析

    本文基于Elasticsearch6.8.5版本 ES使用的是Guice框架,依赖注入和暴露接口的方式和Spring差距较大,可先查看guice框架 节点启动过程: org/elasticsearch ...

  4. PG数据库内核源码分析——UPDATE

    PG中UPDATE源码分析 本文主要描述SQL中UPDATE语句的源码分析,代码为PG13.3版本. 整体流程分析 以 update dtea set id = 1;这条最简单的Update语句进行源 ...

  5. Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求

    Elasticsearch源码分析-线程池(十一) 转自:https://www.felayman.com/articles/2017/11/10/1510291570687.html 线程池 每个节 ...

  6. elasticsearch源码分析之search模块(server端)

    elasticsearch源码分析之search模块(server端) 继续接着上一篇的来说啊,当client端将search的请求发送到某一个node之后,剩下的事情就是server端来处理了,具体 ...

  7. elasticsearch源码分析之search模块(client端)

    elasticsearch源码分析之search模块(client端) 注意,我这里所说的都是通过rest api来做的搜索,所以对于接收到请求的节点,我姑且将之称之为client端,其主要的功能我们 ...

  8. 【Elasticsearch源码】CCR源码分析(一)

    1 CCR的基本概念 什么是CCR? CCR( cross-cluster replication):跨集群复制是ES 6.5发布的一个新的特性:可以将两个集群中的数据进行远程复制. 集群复制类似于数 ...

  9. 【Elasticsearch源码】CCR源码分析(二)

    接上一篇:[Elasticsearch源码]CCR源码分析(一). sendShardChangesRequest方法最终进入到ShardChangesAction.TransportAction#s ...

最新文章

  1. 鱼眼图像的unwarping过程
  2. .net程序员使用Oracle新手上路指南
  3. DeepLearning——CNN
  4. AWS EC2 Run Command特性新增多重云脚本
  5. c/c++反混淆方法
  6. Wordpress插件
  7. linux命令-- 抓包
  8. oracle配置的监听文件,配置oracle监听文件
  9. CVPR 2021 底层视觉最新进展分享
  10. ArrayList 一个面试题
  11. 【旅行】1月17日镇江自驾游
  12. 表单和ajax中的post请求后台获取数据方法(深度好文)
  13. 几何画板椭圆九种画法_几何画板中椭圆的几种构造方法
  14. 定能解决No instances available for provider
  15. AI产品经理视角下的AI翻译机 in 旅游场景
  16. 解决Windows无法访问指定设备路径或文件,您可能没有合适的权限访问这个项目
  17. win10 如何修改 C:\Users\用户名文件夹
  18. 与Lucene 4.10配合的中文分词比较(转)
  19. iOS开发笔记 -- 动态切换APP的logo
  20. 电源设计经验谈1-9

热门文章

  1. ip6tables 无法基于端口过滤IPv6 分片报文问题解决
  2. js 到 jsp 传输数据
  3. linux lvs公网ip,Linux集群架构(2)LVS介绍、LVS的调度算法、NAT模式搭建、 DR模式、keepalive...
  4. hive 时间转字符串_07697.0.3如何在Kerberos环境下用Ranger完成对Hive的行过滤及列脱敏...
  5. java编程pig编码_Pig编程指南.pdf
  6. 驱动进化之路:总线设备驱动模型
  7. idea卸载不干净怎么办_挡风玻璃总是洗不干净,该怎么办
  8. android库项目管理,一个android工程代码多个差异化项目管理方法探讨
  9. 我的世界java版forge放在哪_我的世界1.9forge安装 怎么装forge教程
  10. Oralcle存储过程书写规范