1. 程式人生 > >Elasticsearch實現原理分析-2

Elasticsearch實現原理分析-2

介紹

    第1部分分析了Elasticsearch基本的讀、寫、更新、儲存等方面的實現原理,本文件主要介紹Elasticsearch如何實現分散式系統的三個特性(consensus, concurrency和consistency),以及分片的內部概念,例如:translog(Write Ahead Log - WAL)和Lucene segments。
本章主要包括以下內容:

  • Consensus: 腦裂(split-brain)和quorum機制
  • Concurrency(併發)
  • Consistency(一致性):確保一致的寫入和讀取
  • Translog (Write Ahead Log — WAL)
  • Lucene segments

Consensus(腦裂和quorum機制)

    Consensus是一個分散式系統的基本挑戰。它要求分散式系統中的所有的程序/節點,對給定的資料的值/狀態達成一致。有很多Consensus的演算法,如RaftPaxos等。這些演算法在數學上被證明是有效的,但Elasticsearch實現了它自己的consensus系統(zen discovery),是因為Shay Banon(Elasticsearch的建立者)所描述的原因
    zen discovery模組有兩個部分:

  • Ping:執行該過程的節點,用來發現對方。
  • Unicast:該模組包含一個主機名列表,用於控制要ping哪個節點。

    Elasticsearch是一種點對點(peer-to-peer)系統,其中所有節點彼此通訊,並且只有一個活動的主節點,該節點可以更新和控制群集狀態和操作。新的Elasticsearch叢集將會進行一次選舉,該選舉作為ping程序的一部分在節點中執行。在所有有資格選舉的節點中,其中有一個節點被選舉為主節點(master node),其他節點加入該主節點。
    預設的ping_interval為1秒,ping_timeout為3秒。當節點加入時,他們會發生一個join的請求給master主機,該請求有一個join_timeout時間,預設是引數ping_timeout的20倍。
    若主節點失敗,叢集中的幾點再次開始ping,開始另一次新的選舉。如果節點意外地認為主節點發生故障,並且通過其他節點發現主節點,則該ping過程也將有所幫助。
    注意:預設情況下,客戶端(client)和資料節點不會對選舉過程做出貢獻。可以通過改變以下引數的設定,這些引數在elasticsearch.yml 檔案中:

 discovery.zen.master_election.filter_client:False
 discovery.zen.master_election.filter_data: False

    故障檢測的過程是這樣的:主節點ping所有的節點來檢查這些節點是否存活,而所有的節點回ping主節點來彙報他們是存活的。

    若使用預設配置,Elasticsearch會遇到腦裂的問題,如果是網路分割槽,則節點可以認為主節點已經宕機,並將其自身選為主節點,從而導致叢集具有多個主節點。這樣可能導致資料丟失,可能無法正確合併資料。這種情況可以通過將以下屬性設定為主合格節點的法定數量來避免:

discovery.zen.minimum_master_nodes = int(# of master eligible nodes/2)+1

這裡寫圖片描述

    該引數要求法定數量的可參加選舉的節點加入新的選舉過程來完成新的選舉過程,並接受新節點作為新的master節點。這是確保叢集穩定性的極其重要的引數,如果叢集大小發生變化,可以進行動態更新。圖a和b分別顯示了在minimum_master_nodes屬性被設定,和未被設定的兩種情況。
    注意:對於生產環境的叢集,建議有3個專用主節點,在任何給定的時間點只有一個處於活動狀態,其他的節點不服務任何客戶請求。

Concurrency(併發控制)

    Elasticsearch是一個分散式系統,支援併發請求。當建立/更新/刪除請求命中主分片時,它同時傳送到副本分片,但是這些請求到達的順序是不確定的。在這種情況下,Elasticsearch使用樂觀併發控制(optimistic concurrency control)來確保文件的較新版本不會被舊版本覆蓋。
    每個被索引的文件都會有一個版本號,每次文件改變時,該版本號就會增加。使用版本號可以確保對文件的改變是順序進行的。為確保我們的應用程式中的更新不會導致資料丟失,Elasticsearch的API允許您指定,更改應該應用到目前哪一個版本號。如果請求中指定的版本號,早於分片中存在的版本號,則請求失敗,這意味著該文件已被其他程序更新。
    如何處理失敗的請求?可以在應用程式級別進行控制。還有其他鎖定選項可用,您可以在這裡閱讀

    當我們向Elasticsearch傳送併發請求時,下一個問題是 - 我們如何使這些請求保持一致?現在要回答 CAP原則,還不是太清晰,這是接下來要討論的問題。
這裡寫圖片描述
但是,我們將回顧如何使用Elasticsearch實現一致的寫入和讀取。

Consistency (確保一致的寫和讀)

    對於寫操作,Elasticsearch支援與大多數其他資料庫不同的一致性級別,它允許初步檢查以檢視有多少個分片可用於允許寫入。可用的選項是:quorum的可設定的值為:one和all。預設情況下,它被設定為:quorum,這意味著只有當大多數分片可用時,才允許寫入操作。在大部分分片可用的情況下,由於某種原因,寫入複製副本分片失敗仍然可能發生,在這種情況下,副本被認為是錯誤的,該分片將在不同的節點上進行重建。

    對於讀操作,新文件在重新整理間隔時間後才能用於搜尋。為了確保搜尋結果來自最新版本的文件,可以將複製(replication)設定為sync(預設值),當在主分片和副本分片的寫操作都完成後,寫操作才返回。在這種情況下,來自任何分片的搜尋請求將從文件的最新版本返回結果。
    即使你的應用程式為了更快的indexing而設定:replication=async,也可以使用_preference引數,可以為了搜素請求把它設定為primary。這樣,查詢主要分片就是搜尋請求,並確保結果將來自最新版本的文件。
    當我們瞭解Elasticsearch如何處理consensus,併發性和一致性時,我們來看看分片內部的一些重要概念,這些概念導致了Elasticsearch作為分散式搜尋引擎的某些特徵。

Translog

    自從開發關係資料庫以來,資料庫世界中一直存在著write ahead log(WAL)或事務日誌(translog)的概念。Translog在發生故障的情況下確保資料的完整性,其基本原則是,在資料的實際更改提交到磁碟之前必須先記錄並提交預期的更改。
    當新文件被索引或舊文件被更新時,Lucene索引會更改,這些更改將被提交到磁碟以進行持久化。每次寫請求之後進行持久化操作是一個非常消耗效能的操作,它通過一次持久化多個修改到磁碟的方式執行(譯註:也就是批量寫入)。正如我們在之前的一篇部落格中描述的那樣,預設情況下每30分鐘執行一次flush操作(Lucene commit),或者當translog變得太大(預設為512MB)時)。在這種情況下,有可能失去兩次Lucene提交之間的所有變化。為了避免這個問題,Elasticsearch使用translog。所有的索引/刪除/更新操作都先被寫入translog,並且在每個索引/刪除/更新操作之後(或者每預設預設為5秒),translog都被fsync’s,以確保更改被持久化。在主檔案和副本分片上的translog被fsync’ed後,客戶端都會收到寫入確認。

    在兩次Lucene提交或重新啟動之間出現硬體故障的情況下,會在最後一次Lucene提交之前重播Translog以從任何丟失的更改中恢復,並將所有更改應用於索引。
    建議在重新啟動Elasticsearch例項之前顯式重新整理translog,因為啟動將更快,因為要重播的translog將為空。POST / _all / _flush命令可用於重新整理叢集中的所有索引。
使用translog重新整理操作,檔案系統快取中的段將提交到磁碟,以使索引持續更改。
    現在我們來看看Lucene segment:

Lucene Segments

    Lucene索引由多個片段(segment)組成,片段本身是完全功能的倒排索引。片段是不可變的,這允許Lucene增量地向索引新增新文件,而無需從頭開始重建索引。對於每個搜尋請求,搜所有段都會被搜素,每個段消耗CPU週期,檔案控制代碼和記憶體。這意味著分段數越多,搜尋效能就越低。
    為了解決這個問題,Elasticsearch將小段合併成一個更大的段(如下圖所示),將新的合併段提交到磁碟,並刪除舊的較小的段。
這裡寫圖片描述
合併操作將會自動發生在後臺,而不會中斷索引或搜尋。由於分段合併可能會浪費資源並影響搜尋效能,因此Elasticsearch會限制合併過程的資源使用,以獲得足夠的資源進行搜尋。

參考文件