ES 5.x Bulk update重復(fù)的文檔id性能低下

本文原載于Elastic中文社區(qū): https://elasticsearch.cn/publish/article/273
本文是針對(duì)社區(qū)問題question#2352的分析和總結(jié)

現(xiàn)在很多公司(包括我們自己)將ES用作數(shù)據(jù)庫數(shù)據(jù)的索引,將多個(gè)數(shù)據(jù)庫的數(shù)據(jù)同步到ES是非常常見的應(yīng)用場(chǎng)景。所以感覺這個(gè)問題可能會(huì)困擾不止一個(gè)用戶闸英,而官方的文檔也沒有對(duì)update的底層機(jī)制及局限做特別說明,特將該問題的討論和結(jié)論整理成文连锯,供社區(qū)用戶參考。


問題描述

在ES5.x里通過bulk update將數(shù)據(jù)從數(shù)據(jù)庫同步到ES,如果短時(shí)間更新的一批數(shù)據(jù)里存在相同的文檔ID画舌,例如一個(gè)bulk update里大量寫入下面類型的數(shù)據(jù):

{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操作是分為兩個(gè)步驟進(jìn)行介褥,即先根據(jù)文檔ID做一次GET,得到最新版本的文檔递惋,然后在內(nèi)存里做好更新后呻顽,再寫回去。問題就出在這個(gè)GET操作上面丹墨。

core/src/main/java/org/elasticsearch/index/engine/InternalEngine.java 這個(gè)類里面廊遍,get函數(shù)會(huì)根據(jù)一個(gè)realtime參數(shù)(默認(rèn)是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 flush
            return getFromSearcher(get, searcherFactory);
        }

可以看到realtime參數(shù)決定了是否以實(shí)時(shí)的方式獲取數(shù)據(jù)喉前。 如果設(shè)置為false,意味著不關(guān)心實(shí)時(shí)性王财,此時(shí)直接從searcher對(duì)象里面拿數(shù)據(jù)卵迂。因?yàn)?code>searcher只能訪問refresh過的數(shù)據(jù),那些剛寫入到indexing writter buffer里绒净,還未經(jīng)歷過refresh的數(shù)據(jù)不會(huì)被訪問到见咒,故而該讀取方式是準(zhǔn)實(shí)時(shí)(Near Real Time)。 而這個(gè)realtime參數(shù)默認(rèn)設(shè)置是true挂疆,說明需要以實(shí)時(shí)的方式訪問數(shù)據(jù)改览,也就是說writter buffer里未經(jīng)refresh的數(shù)據(jù)也要能被檢索到,如何保證這塊數(shù)據(jù)也能被實(shí)時(shí)訪問呢缤言?

從代碼里可以看到宝当,其中存在一個(gè)refresh("realtime_get") 的函數(shù)調(diào)用。這個(gè)函數(shù)調(diào)用會(huì)檢查胆萧,GET的doc id是否都是可以被搜索到庆揩。 如果已經(jīng)寫入了但無法搜索到,也就是剛剛寫入到writter buffer里還未refresh這種情況跌穗,就會(huì)強(qiáng)制執(zhí)行一次refresh操作订晌,讓數(shù)據(jù)對(duì)searcher可見,保證getFromSearcher調(diào)用拿的是完全實(shí)時(shí)的數(shù)據(jù)蚌吸。

實(shí)際上測(cè)試下來锈拨,正是這樣的結(jié)果: 在關(guān)閉索引的自動(dòng)刷新的情況下(設(shè)置refresh_interval: -1,只寫入一條文檔套利,然后對(duì)該文檔ID執(zhí)行一個(gè)GET操作推励,就會(huì)看到有一個(gè)新的segment生成鹤耍。 說明GET的過程觸發(fā)了refresh。

查了下文檔验辞,如果僅僅是做GET API調(diào)用稿黄,這個(gè)實(shí)時(shí)性可以認(rèn)為控制,只需要在url里帶可選參數(shù)realtime=[true/|false]跌造。 參考: reference/5.6/docs-get.html#realtime杆怕。

然而,不幸的是壳贪,update API的文檔和源碼都沒有提供一個(gè)禁用實(shí)時(shí)性的參數(shù)陵珍。 update對(duì)GET的調(diào)用,傳入的realtime參數(shù)是在代碼里寫死為true的违施,意味著update的時(shí)候互纯,必須強(qiáng)制執(zhí)行一次realtime GET.

為什么是這樣的代碼邏輯,仔細(xì)想一下就也就了然了磕蒲。因?yàn)閡pdate允許對(duì)文檔做部分字段更新留潦,如果有2個(gè)請(qǐng)求分別更新了同一個(gè)文檔的不同字段, 可能先更新的數(shù)據(jù)還在writter buffer里辣往,沒來得及refresh兔院,因而對(duì)searcher不可見。如果后續(xù)更新不做一次refresh站削,前面的更新可能就丟失了坊萝。

另外一個(gè)問題,為啥5.x之前的版本沒有這個(gè)性能問題许起? 看了下2.4的GET方法源碼十偶,其的確沒有采用refresh的方式來保障數(shù)據(jù)的實(shí)時(shí)性,而是通過訪問translog來達(dá)到同樣的目的街氢。官方在這個(gè)變更里pull#20102將機(jī)制從訪問translog改為了refresh哨啃。理由是之前ES里有很多地方利用translog來維護(hù)數(shù)據(jù)的位置界酒,使得很多操作變得很慢,去掉對(duì)translog的依賴可以全面提高性能绒极。

很遺憾馅笙,這個(gè)更改對(duì)于短時(shí)間反復(fù)大量更新相同doc id的操作伦乔,會(huì)因?yàn)檫^于頻繁的強(qiáng)制refresh,短時(shí)間生成很多小segment董习,繼而不斷觸發(fā)segment合并烈和,產(chǎn)生顯著的性能損耗。 從上面鏈接里的討論看皿淋,官方認(rèn)為招刹,在提升大多數(shù)應(yīng)用場(chǎng)景性能的前提下恬试,對(duì)于這種較少見的場(chǎng)景下的性能損失是值得付出的。所以疯暑,建議從應(yīng)用層面去解決训柴。

因此,如果實(shí)際應(yīng)用場(chǎng)景里遇到類似的數(shù)據(jù)更新問題妇拯, 只能是優(yōu)化應(yīng)用數(shù)據(jù)架構(gòu)幻馁,在應(yīng)用層面合并相同doc id的數(shù)據(jù)更新后再寫入ES,或者只能使用ES 2.x這樣的老版本了越锈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仗嗦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子甘凭,更是在濱河造成了極大的恐慌稀拐,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丹弱,死亡現(xiàn)場(chǎng)離奇詭異钩蚊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹈矮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門砰逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泛鸟,你說我怎么就攤上這事蝠咆。” “怎么了北滥?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵刚操,是天一觀的道長。 經(jīng)常有香客問我再芋,道長菊霜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任济赎,我火速辦了婚禮鉴逞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘司训。我一直安慰自己构捡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布壳猜。 她就那樣靜靜地躺著勾徽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪统扳。 梳的紋絲不亂的頭發(fā)上喘帚,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天畅姊,我揣著相機(jī)與錄音,去河邊找鬼吹由。 笑死涡匀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溉知。 我是一名探鬼主播陨瘩,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼级乍!你這毒婦竟也來了舌劳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤玫荣,失蹤者是張志新(化名)和其女友劉穎甚淡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捅厂,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贯卦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焙贷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撵割。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辙芍,靈堂內(nèi)的尸體忽然破棺而出啡彬,到底是詐尸還是另有隱情,我是刑警寧澤故硅,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布庶灿,位于F島的核電站,受9級(jí)特大地震影響吃衅,放射性物質(zhì)發(fā)生泄漏往踢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一徘层、第九天 我趴在偏房一處隱蔽的房頂上張望峻呕。 院中可真熱鬧,春花似錦惑灵、人聲如沸山上。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哮伟,卻和暖如春干花,著一層夾襖步出監(jiān)牢的瞬間妄帘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工池凄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抡驼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓肿仑,卻偏偏與公主長得像致盟,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尤慰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理馏锡,服務(wù)發(fā)現(xiàn),斷路器伟端,智...
    卡卡羅2017閱讀 134,696評(píng)論 18 139
  • 很少寫技術(shù)博客,一是覺得自己技術(shù)有限霜医,怕誤人子弟齿拂;二是文筆較差,比較排斥寫作之類的工作肴敛。話說不鍛煉创肥,哪會(huì)有進(jìn)步呢,...
    奔跑的番茄醬閱讀 42,333評(píng)論 7 132
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,280評(píng)論 25 707
  • INDEX API 示例: 說明:1.索引文檔使用PUT方法值朋,需要指定index(test)叹侄、type(user)...
    imsilence閱讀 3,064評(píng)論 0 0
  • 定義 ThreadThread 是程序執(zhí)行的最小單元,它是分配CPU的基本單位昨登≈捍可以用 Thread 來執(zhí)行一些異...
    learning_lx閱讀 240評(píng)論 0 1