亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

ElasticSearch寫入流程實例解析

 更新時間:2022年09月07日 11:02:17   作者:IT巔峰技術(shù)  
這篇文章主要為大家介紹了ElasticSearch寫入流程實例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

一、前言

介紹我們在前面已經(jīng)知道ElasticSearch底層的寫入是基于lucence依進行doc寫入的。ElasticSearch作為一款分布式系統(tǒng),在寫入數(shù)據(jù)時還需要考慮很多重要的事項,比如:可靠性、原子性、一致性、實時性、隔離性、性能等多個指標。

ElasticSearch是如何做到的呢?下面我們針對ElasticSearch的寫入進行分析。

二、lucence寫

2.1 增刪改

ElasticSearch拿到一個doc后調(diào)用lucence的api進行寫入的。

 public long addDocument();
 public long updateDocuments();
 public long deleteDocuments();

如上面的代碼所示,我們使用lucence的上面的接口就可以完成文檔的增刪改操作。在lucence中有一個核心的類IndexWriter負責數(shù)據(jù)寫入和索引相關(guān)的工作。

//1. 初始化indexwriter對象
IndexWriter writer = new IndexWriter(new Directory(Paths.get("/index")), new IndexWriterConfig());
//2. 創(chuàng)建文檔
Document doc = new Document();
doc.add(new StringField("empName", "王某某", Field.Store.YES));
doc.add(new TextField("content", "操作了某菜單", Field.Store.YES));
//3. 添加文檔
writer.addDocument(doc);
//4. 提交
writer.commit();

以上代碼演示了最基礎(chǔ)的lucence的寫入操作,主要涉及到幾個關(guān)鍵點: 初始化: Directory是負責持久化的,他的具體實現(xiàn)有很多,有本地文件系統(tǒng)、數(shù)據(jù)庫、分布式文件系統(tǒng)等待,ElasticSearch默認的實現(xiàn)是本地文件系統(tǒng)。 Document: Document就是es中的文檔,F(xiàn)iledType定義了很多索引類型。這里列舉幾個常見的類型:

  • stored: 字段原始內(nèi)容存儲 
  • indexOptions:(NONE/DOCS/DOCS_AND_FREQS/DOCS_AND_FREQS_AND_POSITIONS/DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS),倒排索引的選項,存儲詞頻、位置信息等。
  • docValuesType: 正排索引,建立一個docid到field的的一個列存儲。
  • 一些其它的類型

IndexWriter:IndexWriter在doc進行commit后,才會被持久化并且是可搜索的。IndexWriterConfig:IndexWriterConfig負責了一些整體的配置參數(shù),并提供了方便使用者進行功能定制的參數(shù): 

  • Similarity: 這個是搜索的核心參數(shù),實現(xiàn)了這個接口就能夠進行自定義算分。lucence默認實現(xiàn)了前面文章提到的TF-IDF、BM25算法。 
  • MergePolicy: 合并的策略。我們知道ElasticSearch會進行合并,從而減少段的數(shù)量。 
  • IndexerThreadPool: 線程池的管理。
  • FlushPolicy: flush的策略。
  • Analyzer: 定制分詞器。
  • IndexDeletionPolicy: 提交管理。

PS:在ElasticSearch中,為了支持分布式的功能,新增了一些系統(tǒng)默認字段:

  • _uid,主鍵,在寫入的時候,可以指定該Doc的ID值,如果不指定,則系統(tǒng)自動生成一個唯一的UUID值。
  • _version,版本字段,version來保證對文檔的變更正確的執(zhí)行,更新文檔時有用。 
  • _source,原始信息,如果后面維護不需要reindex索引可以關(guān)閉該字段,從而節(jié)省空間 
  • _routiong,路由字段。 
  • 其它的字段

2.2. 并發(fā)模型

上面我們知道indexwriter負責了ElasticSearch索引增刪改查。那它具體是如何管理的呢?

2.2.1. 基本操作

關(guān)鍵點:  

  • DocumentsWriter處理寫請求,并分配具體的線程DocumentsWriterPerThread
  • DocumentsWriterPerThread具有獨立內(nèi)存空間,對文檔進行處理DocumentsWriter觸發(fā)一些flush的操作。
  • DocumentsWriterPerThread中的內(nèi)存In-memory buffer會被flush成獨立的segement文件。 
  • 對于這種設(shè)計,多線程的寫入,針對純新增文檔的場景,所有數(shù)據(jù)都不會有沖突,非常適合隔離的數(shù)據(jù)寫入方式

2.2.2 更新

Lucene的update和數(shù)據(jù)庫的update不太一樣,Lucene的更新是查詢后刪除再新增。  

  • 分配一個操作線程 
  • 在線程里執(zhí)行刪除 
  • 在線程里執(zhí)行新增

2.2.3 刪除

上面已經(jīng)說了,在update中會刪除,普通的也會刪除,lucence維護了一個全局的刪除表,每個線程也會維護一個刪除表,他們雙向同步數(shù)據(jù)

  • update的刪除會先在內(nèi)部記錄刪除的數(shù)據(jù),然后同步到全局表中。
  • delete的刪除會作用在Global級別,后異步同步到線程中。
  • Lucene Segment內(nèi)部,數(shù)據(jù)實際上其實并不會被真正刪除,Segment內(nèi)部會維持一個文件記錄,哪些是docid是刪除的,在merge時,相應的doc文檔會被真正的刪除。

2.2.4 flush和commit

每一個WriterPerThread線程會根據(jù)flush策略將文檔形成segment文件,此時segment的文件還是不可見的,需要indexWriter進行commit后才能被搜索。 這里需要注意:ElasticSearch的refresh對應于lucene的flush,ElasticSearch的flush對應于lucene的commit,ElasticSearch在refresh時通過其它方式使得segment變得可讀。

2.2.5 merge

merge是對segment文件合并的動作,這樣可以提升查詢的效率并且可以真正的刪除的文檔。

小結(jié)

在這里我們稍微總結(jié)一下,一個ElasticSearch索引的一個分片對應一個完整的lucene索引, 而一個lucene索引對應多個segment。我們在構(gòu)建同一個lucene索引的時候, 可能有多個線程在并發(fā)構(gòu)建同一個lucene索引, 這個時候每個線程會對應一個DocumentsWriterPerThread, 而每個 DocumentsWriterPerThread會對應一個index buffer. 在執(zhí)行了flush以后, 一個 DocumentsWriterPerThread會生成一個segment。

三、 ElasticSearch的寫

3.1. 宏觀看ElasticSearch請求

在前面的文章已經(jīng)討論了寫入的流程ElasticSearch

圖片來自官網(wǎng) 當寫入文檔的時候,根據(jù)routing規(guī)則,會將文檔發(fā)送至特定的Shard中建立lucence。

  • 介紹在Primary Shard上執(zhí)行成功后,再從Primary Shard上將請求同時發(fā)送給多個Replica Shardgit 
  • 請求在多個Replica Shard上執(zhí)行成功并返回給Primary Shard后,寫入請求執(zhí)行成功,返回結(jié)果給客戶端

注意上面的寫入延時=主分片延時+max(Replicas Write),即寫入性能如果有副本分片在,就至少是寫入兩個分片的延時延時之和。

3.2. 詳細流程

3.2.1 協(xié)調(diào)節(jié)點內(nèi)部流程

如上圖所示:

  • 協(xié)調(diào)節(jié)點會對請求檢查放在第一位,如果如果有問題就直接拒絕。主要有長度校驗、必傳參數(shù)、類型、版本、id等等。
  • pipeline,用戶可以自定義設(shè)置處理器,比如可以對字段切割或者新增字段,還支持一些腳本語言,可以查看官方文檔編寫。
  • 如果允許自動創(chuàng)建索引(默認是允許的),會先創(chuàng)建索引,創(chuàng)建索引會發(fā)送到主節(jié)點上,必須等待master成功響應后,才會進入下一流程。
  • 請求預處理,比如是否會自動生成id、路由,獲取到整個集群的信息了,并檢查集群狀態(tài),比如集群master不存在,都會被拒絕。
  • 構(gòu)建sharding請求,比如這一批有5個文檔, 如果都是屬于同一個分片的,那么就會合并到一個請求里,會根據(jù)路由算法將文檔分類放到一個map里 Map> requestsByShard = new HashMap<>();路由算法默認是文檔id%分片數(shù)。
  • 轉(zhuǎn)發(fā)請求,有了分片會根據(jù)前面的集群狀態(tài)來確定具體的ElasticSearch節(jié)點ip,然后并行去請求它們。

3.2.2 主分片節(jié)點流程*

 寫入(index)

該部分是elasticsarch的核心寫入流程,在前面的文章也介紹了,請求到該節(jié)點會最終調(diào)用lucence的方法,建立lucence索引。其中主要的關(guān)鍵點:

  • ElasticSearch節(jié)點接收index請求,存入index buffer,同步存入磁盤translog后返回索引結(jié)果
  • Refresh定時將lucence數(shù)據(jù)生成segment,存入到操作系統(tǒng)緩存,此時沒有fsync,清空lucence,此時就可以被ElasticSearch查詢了,如果index buffer占滿時,也會觸發(fā)refresh,默認為jvm的10%。
  • Flush定時將緩存中的segments寫入到磁盤,刪除translog。如果translog滿時(512m),也會觸發(fā)flush。
  • 如果數(shù)據(jù)很多,segment的也很多,同時也可能由刪除的文檔,ElasticSearch會定期將它們合并。

update

  • 讀取同id的完整Doc, 記錄版本為version1。
  • 將version1的doc和update請求的Doc合并成一個Doc,更新內(nèi)存中的VersionMap。獲取到完整Doc后。進入后續(xù)的操作。
  • 后面的操作會加鎖。
  • 第二次從versionMap中讀取該doc的的最大版本號version2,這里基本都會從versionMap中獲取到。
  • 檢查版本是否沖突,判斷版本是否一致(沖突),如果發(fā)生沖突,則回到第一步,重新執(zhí)行查詢doc合并操作。如果不沖突,則執(zhí)行最新的添加doc請求。
  • 介紹在add Doc時,首先將Version + 1得到V3,再將Doc加入到Lucene中去,Lucene中會先刪同id下的已存在doc id,然后再增加新Doc。寫入Lucene成功后,將當前V3更新到versionMap中。
  • 釋放鎖,更新流程就結(jié)束了。

介紹其實就是樂觀鎖的機制,每次更新一次版本號加 1 ,不像關(guān)系式數(shù)據(jù)庫有事物,你在更新數(shù)據(jù),可能別人也在更新的話,就把你的給覆蓋了。你要更新的時候,先查詢出來,記住版本號,在更新的時候最新的版本號和你查詢的時候不一樣,說明別人先更新了。你應該讀取最新的數(shù)據(jù)之后再更新。寫成功后,會轉(zhuǎn)發(fā)寫副本分片,等待響應,并最后返回數(shù)據(jù)給協(xié)調(diào)節(jié)點。具體的流程:

  • 校驗,校驗寫的分片是否存在、索引的狀態(tài)是否正常等等。
  • 是否需要延遲執(zhí)行,如果是則會放入到隊列里等待。
  • 校驗活躍的分片數(shù)是否存在,不足則拒絕寫入。
public boolean enoughShardsActive(final int activeShardCount) {
  if (this.value < 0) {
    throw new IllegalStateException("not enough information to resolve to shard count");
  }
  if (activeShardCount < 0) {
    throw new IllegalArgumentException("activeShardCount cannot be negative");
  }
  return this.value <= activeShardCount;
}

為什么會要校驗這個活躍的分片數(shù)呢?

  • ElasticSearch的索引層有個一waitforactiveshards參數(shù)代表寫入的時候必須的分片數(shù),默認是1。如果一個索引是每個分片3個副本的話,那么一共有4個分片,請求時至少需要校驗存活的分片數(shù)至少為1,相當于提前校驗了。如果對數(shù)據(jù)的可靠性要求很高,就可以調(diào)高這個值,必須要達到這個數(shù)量才會寫入。
  • 調(diào)用lucence寫入doc.
  • 寫入translog日志。
  • 寫入副本分片,循環(huán)處理副本請求,會傳遞一些信息。在這里需要注意,它們是異步發(fā)送到副本分片上的,并且需要全部等待響應結(jié)果,直至超時。
  • 接著上一步,如果有副本分片失敗的情況,會把這個失敗的分片發(fā)送給master,master會更新集群狀態(tài),這個副本分片會從可分配列表中移除。 

發(fā)送請求至副本

@Override
public void tryAction(ActionListener<ReplicaResponse> listener) {
  replicasProxy.performOn(shard, replicaRequest, primaryTerm, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, listener);
}

等待結(jié)果

privatevoid decPendingAndFinishIfNeeded() {
  assert pendingActions.get() > 0 : "pending action count goes below 0 for request [" + request + "]";
  if (pendingActions.decrementAndGet() == 0) {
    finish();
  }
}

在以前的版本中,其實是異步請求副本分片的,后來覺得丟失數(shù)據(jù)的風險很大,就改成同步發(fā)送了,即Primary等Replica返回后再返回給客戶端。如果副本有寫入失敗的,ElasticSearch會進行一些重試,但最終并不強求一定要在多少個節(jié)點寫入成功。在返回的結(jié)果中,會包含數(shù)據(jù)在多少個shard中寫入成功了,多少個失敗了,如果有副本上傳失敗,會將失敗的副本上報至Master。

PS:ElasticSearch的數(shù)據(jù)副本模型和kafka副本很相似,都是采用的是ISR機制。即:ES里面有一個:in-sync copies概念,主分片會在索引的時候會同步數(shù)據(jù)至in-sync copies里面所有的節(jié)點,然后再返回ACK給client。而in-sync copies里面的節(jié)點是動態(tài)變化的,如果出現(xiàn)極端情況,在in-sync copies列表中只有主分片一個的話,這里很容易出現(xiàn)SPOF問題,這個是在ElasticSearch中是如何解決的呢?

就是依靠上面我們分析的wait_for_active_shards參數(shù)來防止SPOF,如果配置index的wait_for_active_shards=3就會提前校驗必須要有三個活躍的分片才會進行同步,否則拒絕請求。對于可靠性要求高的索引可以提升這個值。

PS:為什么是先寫lucence再寫入translog呢,這是因為寫入lucence寫入時會有數(shù)據(jù)檢查,有可能會寫入失敗,這個是發(fā)生在內(nèi)存之中的,如果先寫入磁盤的translog的話,還需要回退日志,比較麻煩

3.2.3 副本分片節(jié)點流程8

這個過程和主分片節(jié)點的流程基本一樣,有些校驗可能略微不同,最終都會寫入lucence索引。

四、總結(jié)

本文介紹了ElasticSearch的寫入流程和一些比較詳細的機制,最后我們總結(jié)下開頭我們提出的問題,一個分布式系統(tǒng)需要滿足很多特性,大部分特性都能夠在ElasticSearch中得到滿足。

  • 可靠性:lucence只是個工具,ElasticSearch中通過自己設(shè)計的副本來保證了節(jié)點的容錯,通過translog日志保證宕機后能夠恢復。通過這兩套機制提供了可靠性保障。
  • 一致性:ElasticSearch實現(xiàn)的是最終一致性,副本和主分片在同一時刻讀取的數(shù)據(jù)可能不一致。比如副本的refresh頻率和主分片的頻率可能不一樣。
  • 高性能:ElasticSearch通過多種手段來提升性能,具體包括:
  • lucence自身獨立線程維護各自的Segment,多線程需要競爭的資源更少,性能更好。 
  • update等操作使用versionMap緩存,減少io.
  • refresh至操作系統(tǒng)緩存。
  • 原子性、隔離性:使用版本的樂觀鎖機制保證的。
  • 實時性:ElasticSearch設(shè)計的是近實時的,如果同步進行refresh、flush將大幅降低性能,所以是”攢一部分數(shù)據(jù)“再刷入磁盤,不過實時寫入的tranlog日志還是可以實時通過id查到的。

以上就是ElasticSearch寫入流程實例解析的詳細內(nèi)容,更多關(guān)于ElasticSearch寫入流程的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Git 教程之基本操作詳解

    Git 教程之基本操作詳解

    本文主要主要介紹Git 基本操作,這里整理了詳細的基本操作資料,包括基本命令使用方法,有需要的朋友可以參考下
    2016-09-09
  • 真?zhèn)戊o態(tài)區(qū)別方法分析

    真?zhèn)戊o態(tài)區(qū)別方法分析

    有些用戶覺得,偽靜態(tài)和真靜態(tài)實際被收錄量會相差非常大,其實不然,從你個人角度,你去判斷一下一個帖子到底是真靜態(tài)還是偽靜態(tài)?
    2010-01-01
  • vscode擴展代碼定位實現(xiàn)步驟詳解

    vscode擴展代碼定位實現(xiàn)步驟詳解

    這篇文章主要為大家介紹了vscode擴展代碼定位實現(xiàn)步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • fiddler抓包小技巧之自動保存抓包數(shù)據(jù)的實現(xiàn)方法分析【可根據(jù)需求過濾】

    fiddler抓包小技巧之自動保存抓包數(shù)據(jù)的實現(xiàn)方法分析【可根據(jù)需求過濾】

    這篇文章主要介紹了fiddler抓包小技巧之自動保存抓包數(shù)據(jù)的實現(xiàn)方法,較為詳細的分析了fiddler自動保存抓包數(shù)據(jù)及根據(jù)需求過濾相關(guān)操作技巧,需要的朋友可以參考下
    2020-01-01
  • 解決idea git切換多個分支后maven不生效的問題

    解決idea git切換多個分支后maven不生效的問題

    這篇文章主要介紹了解決idea git切換多個分支后maven不生效的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Mac如何給應用單獨設(shè)置語言

    Mac如何給應用單獨設(shè)置語言

    這篇文章主要介紹了Mac如何給應用單獨設(shè)置語言,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-10-10
  • git之遠程代碼回滾master問題

    git之遠程代碼回滾master問題

    這篇文章主要介紹了git之遠程代碼回滾master問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • win7/win10+vs2015+pcl1.8.0配置方案詳解

    win7/win10+vs2015+pcl1.8.0配置方案詳解

    這篇文章主要介紹了win7/win10+vs2015+pcl1.8.0詳細配置方案,本文通過圖文并茂的形式給大家講解的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 值得推薦的Idea十幾大優(yōu)秀插件(小結(jié))

    值得推薦的Idea十幾大優(yōu)秀插件(小結(jié))

    這篇文章主要介紹了值得推薦的Idea十幾大優(yōu)秀插件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-04-04
  • Systemd?入門實戰(zhàn)教程

    Systemd?入門實戰(zhàn)教程

    這篇文章主要介紹了Systemd?入門實戰(zhàn)教程,介紹如何使用它完成一些基本的任務,本文結(jié)合示例代碼給大家講解的非常詳細,需要的朋友可以參考下
    2022-12-12

最新評論