ElasticStack系列之十六 ElasticSearch5.x index/create 和 update 源码分析
開篇
在ElasticSearch 系列十四中提到的問題即 ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重復(fù) id 性能驟降,繼續(xù)這個(gè)問題再繼續(xù)查看更加多的源代碼,看看底層在執(zhí)行 index、create 和 update 操作到底有什么不同,有什么可以使得我們使用性能更加好的。
準(zhǔn)備
使用 Intellij IDEA 來閱讀 ElasticSearch 源碼,操作相對(duì)來說比較簡(jiǎn)單。具體操作步驟如下:
1. 下載 ElasticSearch 源碼
git clone https://github.com/elastic/elasticsearch.git2. 下載安裝 gradle,確保版本在 3.3 及以上,我電腦是 macOS,自動(dòng)下載最新版本為:4.2.1
brew update && brew install gradle在 mac 上按照上面的命令執(zhí)行完畢之后它會(huì)自動(dòng)安裝到 /usr/local/bin/gradle , 源文件目錄我到現(xiàn)在都沒有找到,最后一怒之下直接下載對(duì)應(yīng)最新版本的 zip 包,放到指定目錄自己執(zhí)行安裝
首先,先download最新版本的gradle,網(wǎng)址:http://www.gradle.org/get-started
然后將下載下來的zip包放在你要安裝的路徑上,我安裝在 /usr/myname/
然后打開電腦上的.bash_profile文件,輸入以下命令:
然后執(zhí)行命令:source ~/.bash_profile 使剛才配置生效。
3. 進(jìn)入到 ElasticSearch 源碼目錄,在該目錄下執(zhí)行以下命令準(zhǔn)備導(dǎo)入 IntelliJ IDEA 需要的文件
gradle idea以上命令執(zhí)行完畢之后,打開 IntelliJ IDEA 工具它會(huì)自己進(jìn)行編譯,需要你配置對(duì)應(yīng)的 gradle home 目錄,即上面指定的 /Usrs/myname/gradle-4.2.1 ,配置完畢之后項(xiàng)目就算導(dǎo)入成功了。
注意:
1. 運(yùn)行這個(gè)命令需要下載很多東西,有時(shí)候可能因?yàn)槟骋粋€(gè)包卡住,不要緊張,退出重新運(yùn)行該命令,多嘗試幾次就好了。
2. 各個(gè)版本代碼上有一定的差別,但是核心代碼整體是不會(huì)有大改動(dòng)的,我查看的源碼版本為當(dāng)前最新版本:5.6.3
Index/Create 源碼分析
es index 和 create 最終都會(huì)調(diào)用 org/elasticsearch/index/engine/InternalEngine.java 中下面的方法:
public IndexResult index(Index index) throws IOException注意這里的 index 中包含有要寫入的 doc, 簡(jiǎn)單畫下該方法的執(zhí)行流程圖,代碼這里就不貼了
請(qǐng)結(jié)合上面的流程圖來看相應(yīng)的代碼,整個(gè)邏輯應(yīng)該還是很清晰的,接下來我們看 planIndexingAsPrimary 的邏輯。
private IndexingStrategy planIndexingAsPrimary(Index index) throws IOException? 這個(gè)方法最終返回一個(gè) IndexingStrategy,即一個(gè)索引的策略,總共有如下幾個(gè)策略:
- optimizedAppendOnly
- skipDueToVersionConflict
- processNormally
- overrideExistingAsIfNotThere
- skipAsStale
不同的策略對(duì)應(yīng)了不同的處理邏輯,前面3個(gè)是常用的,我們來看下流程圖。
?
這里的第一步判斷:是否是自定義 doc id? 這一步就是 es 對(duì)于日志類非自定義 doc id 的優(yōu)化,感興趣的可以自己去看下代碼,簡(jiǎn)單講就是在非自定義 id 的情況下,直接將文檔 add ,否則需要 update,而 update 比 add 成本高很多。
而第二個(gè)判斷:檢查版本號(hào)是否沖突? 涉及到是如何根據(jù) 文檔版本號(hào)(doc version) 來確認(rèn)文檔可寫入,代碼都在 index.versionType().isVersionConflictForWrites 方法里,邏輯也比較簡(jiǎn)單,不展開講了,感興趣的自己去看吧。
上面的流程圖也比較清晰地列出了策略選擇的邏輯,除去 optimizedAppendOnly 策略,其他都需要根據(jù)待寫入文檔的版本號(hào)來做出決策。接下來我們就看下獲取文檔版本號(hào)的方法。
private VersionValue resolveDocVersion(final Operation op) throws IOException?該方法邏輯比較簡(jiǎn)單,主要分為2步:
看到這里,開篇問題便有了答案。es 在 index 或者 create 的時(shí)候并不會(huì) get 整個(gè)文檔,而是只會(huì)獲取文檔的版本號(hào)做對(duì)比,而這個(gè)開銷不會(huì)很大。
?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 步最終會(huì)調(diào)用 InternalEngine 中的 get 方法,如下:
public GetResult get(Get get, Function<String, Searcher> searcherFactory, LongConsumer onRefresh) throws EngineExceptioupdate 操作需要先獲取原始文檔的原因也很簡(jiǎn)單,因?yàn)檫@里是允許用戶做部分更新的,而 es 底層每次更新時(shí)要求必須是完整的文檔(因?yàn)?lucene 的更新實(shí)際是刪除老文檔,新增新文檔),如果不拿到原始數(shù)據(jù)的話,就不能組裝出更新后的完整文檔了。
因此,比較看重效率的業(yè)務(wù),最好還是不要用 update 這種操作,直接用上面的 index 會(huì)更好一些。
總結(jié)
本文通過源碼分析的方式解決了開篇提到的問題,答案簡(jiǎn)單總結(jié)在下面。
es 在 index 和 create 操作的時(shí)候,如果沒有自定義 doc id,那么會(huì)使用 append 優(yōu)化模式,否則會(huì)獲取待寫入文檔的版本號(hào)(doc version),進(jìn)行版本檢查后再?zèng)Q定是否寫入 lucene。所以這里不會(huì)去做一個(gè) get 操作,即獲取完整的文檔信息。
?
轉(zhuǎn)載于:https://www.cnblogs.com/liang1101/p/7661810.html
總結(jié)
以上是生活随笔為你收集整理的ElasticStack系列之十六 ElasticSearch5.x index/create 和 update 源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Communications link
- 下一篇: 201621123024《JAVA程序设