1. 程式人生 > >【ElasticSearch學習】之一圖讀懂文件索引全過程

【ElasticSearch學習】之一圖讀懂文件索引全過程

ES索引過程詳解:

1.客戶端傳送索引請求。

  客戶端向ES節點發送索引請求,以RestClient客戶端發起請求為例:

  ES提供了Java High Level REST Client,使用者可以通過RestClient傳送請求:

  RestClient restClient = RestClient.builder(
            new HttpHost("127.0.0.1", 9200, "http"),
            new HttpHost("127.0.0.2", 9200, "http")
           ).build();

  其中127.0.0.1,127.0.0.1是ES中的節點,ES在接受請求時,充當coordinate node節點的角色,如果設定有專用coorinate node則應該將接受客戶端請求的節點設定為該專用節點,負責請求的接受和轉發。在RestClient中使用round-robin輪詢演算法,進行傳送節點的選取。

2.引數檢查。

  對請求中的引數進行檢查,檢查引數是否合法,不合法的引數直接返回失敗給客戶端。

3.資料預處理

  如果請求指定了pipeline引數,則對資料進行預處理,資料預處理的節點為Ingest Node,如果接受請求的節點不具有資料處理能力,則轉發給其他能處理的節點。

  在Ingest Node上有定義好的處理資料的Pipeline,Pipeline中有一組定義好的Processor,每個Processor分別具有不同的處理功能,ES提供了一些內建的Processor,如:split、join、set 、script等,同時也支援通過外掛的方式,實現自定義的Processor。資料經過Pipeline處理完畢後繼續進行下一步操作。

4.判斷索引是否存在

  判斷索引是否存在。如果索引不存在,則判斷是否能夠自動建立,如果節點支援Dynamic Mapping,寫入文件時,如果索引不存在,會自動建立索引,並會根據索引文件資訊推算欄位的型別,但並不能完全推算正確。

  配置:Dynamic:true時,文件有新增欄位的時候,索引的mapping也會同步更新。

     Dynamic:false時,索引的mapping不會被更新,新增欄位無法被索引到。

     Dynamic:strict時,索引有新增欄位時,將會報錯。

  注:生產環境儘量避免使用Dynamic mapping,以免過多欄位導致cluster state佔用過多。

5.建立索引

  建立索引請求被髮送到Master節點,由Master節點負責進行索引的建立,索引建立成功後,Master節點會更新叢集狀態clusterstate,更新完畢後將索引建立的情況返回給Coordinate節點,收到Master節點返回的所有建立索引的響應後,進入下一流程。

6.請求預處理

  1)獲取叢集狀態資訊,判斷叢集是否正常;   2)從叢集狀態中獲取對應索引的元資訊,從元資訊中獲取索引的mapping、version、等資訊,從請求中解析routing、id資訊,如果請求沒有指定文件的id,則會生成一個UUID作為文件的id。 7.路由計算   根據請求的routing、id資訊計算文件應該被索引到哪個分配,計算公式:
shard_num = hash(_routing) % num_primary_shards
  _routing預設值為文件id,num_primary_shards是主分片個數,所以從演算法中即可以看出索引的主分片個數一旦指定便無法修改,因為文件利用主分片的個數來進行定位。當使用自定義_routing或者id時,按照上面的公式計算,資料可能會大量聚集於某些分配,造成資料分佈不均衡,所以ES提供了routing_partition_size引數,routing_partition_size越大,資料的分佈越均勻。分片的計算公式變為:
shard_num = (hash(_routing) + hash(_id) % routing_partition_size) % num_primary_shards
  定位到shard序號後,還需要定位shard所屬的資料節點;從叢集狀態的內容路由表獲取主分片所在的節點,並將請求轉發至節點。需要注意的是shard到資料節點的對映關係不是固定的,當檢測到資料分佈不均勻、新節點加入或者節點宕掉等會進行shard重新分配。 8.主分片索引文件   當主分片所在節點接受到請求後,節點開始進行本節點的文件寫入,文件寫入過程如下:   1)文件寫入時,不會直接寫入到磁碟中,而是先將文件寫入到Index Buffer記憶體空間中,到一定的時間,Index Buffer會Refresh把記憶體中的文件寫入Segment中。當文件在Index Buffer中時,是無法被查詢到的,這就是ES不是實時搜尋,而是近實時搜尋的原因。   2)因為文件寫入時,先寫入到記憶體中,當文件落盤之前,節點出現故障重啟、宕機等,會造成記憶體中的資料丟失,所以索引寫入的同時會同步向Transaction Log寫入操作內容。   3)每隔固定的時間間隔ES會將Index Buffer中的文件寫入到Segment中,這個寫入的過程叫做Refresh,Refresh的時間可以通過index.refresh_interval,預設情況下為1秒。   4)寫入到Segment中並不代表文件已經落盤,因為Segment寫入磁碟的過程相對耗時,Refresh時會先將Segment寫入快取,開放查詢,也就是說當文件寫入Segment後就可以被查詢到。每次refresh的時候都會生成一個新的segment,太多的Segment會佔用過多的資源,而且每個搜尋請求都會遍歷所有的Segment,Segment過多會導致搜尋變慢,所以ES會定期合併Segment,減少Segment的個數,並將Segment和併為一個大的Segment;在操作Segment時,會維護一個Commit Point檔案,其中記錄了所有Segment的資訊;同時維護.del檔案用於記錄所有刪除的Segment資訊。   單個倒排索引檔案被稱為Segment。多個Segment彙總在一起,就是Lucene的索引,對應的就是ES中的shard。   Lucene倒排索引由單詞詞典及倒排列表組成:   單詞詞典:記錄所有文件的單詞,記錄單詞到倒排列表的關係,資料量比較大,一般採用B+樹,雜湊拉鍊法實現。   倒排列表:記錄單詞對應的文件集合,由倒排索引項組成。倒排索引項結構如表所示:其中,文件ID:記錄單詞所在文件的ID;詞頻:記錄單詞在文件中出現的次數;位置:記錄單詞在文件中的位置;偏移:記錄單詞的開始位置,結束位置。

    

  5)每隔一定的時間(預設30分鐘),ES會呼叫Flush操作,Flush操作會呼叫Refresh將Index Buffer清空;然後呼叫fsync將快取中的Segments寫入磁碟;隨後清空Transaction Log。同時當Transaction Log空間(預設512M)後也會觸發Flush操作。

9.副本分片索引文件

  當主分片完成索引操作後,會迴圈處理要寫的所有副本分片,向副本分片所在的節點發送請求。副本分片執行和主分片一樣的文件寫入流程,然後返回寫入結果給主分片節點。

10.請求返回

  主分片收到副本分片的響應後,會執行finish()操作,將收到的響應資訊返回給Coordinate節點,告知Coordinate節點文件寫入分片成功、失敗的情況;coordinate節點收到響應後,將索引執行情況返回給客戶端。當文件寫入失敗時,主分片節點會向Master節點返送shardFieled請求,因為主副本分片未同步,Master會更新叢集的狀態,將寫失敗的副本分片從in-sync-allocation中去除;同時在路由表中將該分片的狀態改為unassigned,即未分配狀態。 

 

學習來源:

  阮一鳴《Elasticsearch核心技術與實戰》

  張超《Elasticsearch 原始碼解析與優化實戰