开篇

  在ElasticSearch 系列十四中提到的问题即 ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降,继续这个问题再继续查看更加多的源代码,看看底层在执行 index、create 和 update 操作到底有什么不同,有什么可以使得我们使用性能更加好的。

准备

  使用 Intellij IDEA 来阅读 ElasticSearch 源码,操作相对来说比较简单。具体操作步骤如下:

1. 下载 ElasticSearch 源码

git clone https://github.com/elastic/elasticsearch.git

2. 下载安装 gradle,确保版本在 3.3 及以上,我电脑是 macOS,自动下载最新版本为:4.2.1

brew update && brew install gradle

  在 mac 上按照上面的命令执行完毕之后它会自动安装到 /usr/local/bin/gradle , 源文件目录我到现在都没有找到,最后一怒之下直接下载对应最新版本的 zip 包,放到指定目录自己执行安装

  首先,先download最新版本的gradle,网址:http://www.gradle.org/get-started
  然后将下载下来的zip包放在你要安装的路径上,我安装在 /usr/myname/
  然后打开电脑上的.bash_profile文件,输入以下命令:

  export GRADLE_HOME=/Users/myname/gradle-4.2.1export PATH=$PATH:$GRADLE_HOME/bin

  然后执行命令:source ~/.bash_profile 使刚才配置生效。

3. 进入到 ElasticSearch 源码目录,在该目录下执行以下命令准备导入 IntelliJ IDEA 需要的文件

gradle idea

  以上命令执行完毕之后,打开 IntelliJ IDEA 工具它会自己进行编译,需要你配置对应的 gradle home 目录,即上面指定的 /Usrs/myname/gradle-4.2.1 ,配置完毕之后项目就算导入成功了。

  注意:

  1. 运行这个命令需要下载很多东西,有时候可能因为某一个包卡住,不要紧张,退出重新运行该命令,多尝试几次就好了。

  2. 各个版本代码上有一定的差别,但是核心代码整体是不会有大改动的,我查看的源码版本为当前最新版本:5.6.3

Index/Create 源码分析

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

  public IndexResult index(Index index) throws IOException

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

  

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

  private IndexingStrategy planIndexingAsPrimary(Index index) throws IOException

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

  • optimizedAppendOnly
  • skipDueToVersionConflict
  • processNormally
  • overrideExistingAsIfNotThere
  • skipAsStale

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

  

  这里的第一步判断:是否是自定义 doc id? 这一步就是 es 对于日志类非自定义 doc id 的优化,感兴趣的可以自己去看下代码,简单讲就是在非自定义 id 的情况下,直接将文档 add ,否则需要 update,而 update 比 add 成本高很多。
  而第二个判断:检查版本号是否冲突? 涉及到是如何根据 文档版本号(doc version) 来确认文档可写入,代码都在 index.versionType().isVersionConflictForWrites 方法里,逻辑也比较简单,不展开讲了,感兴趣的自己去看吧。

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

  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},true, request.version(), request.versionType(), FetchSourceContext.FETCH_SOURCE);return prepare(indexShard.shardId(), request, getResult, nowInMillis);
}

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

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

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

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

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

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

总结

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

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

转载于:https://www.cnblogs.com/liang1101/p/7661810.html

ElasticStack系列之十六 ElasticSearch5.x index/create 和 update 源码分析相关推荐

  1. 【vue-router源码】十二、useRoute、useRouter、useLink源码分析

    [vue-rouer源码]系列文章 [vue-router源码]一.router.install解析 [vue-router源码]二.createWebHistory.createWebHashHis ...

  2. Java Collection系列之HashMap、ConcurrentHashMap、LinkedHashMap的使用及源码分析

    文章目录 HashMap HashMap的存储结构 初始化 put & get put元素 get元素 扩容 遍历Map jdk1.8中的优化 ConcurrentHashMap jdk1.7 ...

  3. 深入理解Spark 2.1 Core (十二):TimSort 的原理与源码分析

    在博文<深入理解Spark 2.1 Core (十):Shuffle Map 端的原理与源码分析 >中我们提到了: 使用Sort等对数据进行排序,其中用到了TimSort 这篇博文我们就来 ...

  4. 并发编程(十六)——java7 深入并发包 ConcurrentHashMap 源码解析

    以前写过介绍HashMap的文章,文中提到过HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容 ...

  5. 人为什么活着系列之十六:具体的人具体的环境具体分析

    非常感谢阿懒-上海-PM分享! 今天下午抽空粗略地读完了十五贴 人为什么活着?依坛上前十五贴中的一贴有分两个问题来回答,以下是个人观点: 一是:人为什么活着? 为什么? 我的回答是:没有为什么.人的出 ...

  6. spring boot实战(第六篇)加载application资源文件源码分析

    前言 在上一篇中了解了spring配置资源的加载过程,本篇在此基础上学习spring boot如何默认加载application.xml等文件信息的. ConfigFileApplicationLis ...

  7. Spring 源码分析衍生篇十 :Last-Modified 缓存机制

    文章目录 一.前言 二.Last-Modify 三.实现方案 1. 实现 org.springframework.web.servlet.mvc.LastModified接口 1.1. 简单演示 1. ...

  8. Java源码详解六:ConcurrentHashMap源码分析--openjdk java 11源码

    文章目录 注释 类的继承与实现 数据的存储 构造函数 哈希 put get 扩容 本系列是Java详解,专栏地址:Java源码分析 ConcurrentHashMap 官方文档:ConcurrentH ...

  9. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

最新文章

  1. 小程序网络最大并发限制解决思路
  2. linux无法联网使用yum提示cannot find a valid baseurl for repobase7x86_64
  3. 数据库更新记录,但程序查不到新记录问题
  4. python打包安卓的方法_30个你想打包带走的Python技巧(下)
  5. 前端学习(221):字体属性
  6. Luogu P3975 [TJOI2015]弦论
  7. LeetCode 2206. 将数组划分成相等数对
  8. LeetCode OJ:Pascal's Triangle(帕斯卡三角)
  9. 华为商城抢手机软件_华为p40配置详细参数
  10. 远程登录工具 —— filezilla(FTP vs. SFTP)、xshell、secureCRT
  11. Shp数据批量导入Postgresql工具的原理和设计
  12. ElementUI:el-container实现高度占满
  13. 快易准粤语拼音输入法 绿色
  14. iOS证书(p12)获取解密公私钥
  15. oracle重建inventory,Oracle中Inventory目录作用以及如何重建此目录-Oracle
  16. Giving an effective oral presentation at university(The Hong Kong Polytechnic University 香港理工大学)
  17. Oracle项目管理系统之设计任务下达及成果交付
  18. H3C服务器带外默认账号和密码,H3C产品的默认密码是多少?
  19. 广告坑死人,这年头如何辨别互联网金融的可靠性?
  20. 使用WASAPI捕获声卡音频

热门文章

  1. 模拟电路技术之基础知识(五)
  2. 把二元查找树转变成排序的双向链表(树)
  3. FisherFace 进行人脸分裂
  4. matlab基础(0)
  5. php零拷贝,百万并发「零拷贝」技术系列之初探门径
  6. mysql忘记密码解决方法
  7. 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第五场)
  8. 吴裕雄--天生自然 高等数学学习:多元函数的概念
  9. python-pdf添加水印
  10. Day004_Linux基础命令之特殊符号与正则表达式通配符