剖析 Elasticsearch 集羣系列第二篇 分佈式的三個 C、translog 和 Lucene 段

最近在學習ES,發現《剖析 Elasticsearch 集羣系列》文章寫得挺好,轉載過來記錄下。

原文:https://www.infoq.cn/article/anatomy-of-an-elasticsearch-cluster-part02/

 

剖析 Elasticsearch 集羣系列涵蓋了當今最流行的分佈式搜索引擎 Elasticsearch 的底層架構和原型實例。

本文是這個系列的第二篇,我們將討論 Elasticsearch 如何處理分佈式的三個 C((共識 (consensus)、併發 (concurrency) 和一致 (consistency)) 的問題、Elasticsearch 分片的內部概念,比如 translog(預寫日誌,WAL(Write Ahead Log)),以及 Lucene 中的段。

在本系列的前一篇中,我們討論了 Elasticsearch 的底層存儲模型及 CRUD(創建、讀取、更新和刪除)操作的工作原理。在本文中,我將分享 Elasticsearch 是如何應對分佈式系統中的一些基本挑戰的,以及分片的內部概念。這其中包括了一些操作方面的事情,Insight Data 的工程師們已經在使用 Elasticsearch 構建的數據平臺之上成功地實踐並真正理解。我將在本文中主要講述:

共識——裂腦問題及法定票數的重要性

共識是分佈式系統的一項基本挑戰。它要求系統中的所有進程/ 節點必須對給定數據的值/ 狀態達成共識。已經有很多共識算法諸如 Raft  Paxos 等,從數學上的證明了是行得通的。但是,Elasticsearch 卻實現了自己的共識系統 (zen discovery),Elasticsearch 之父 Shay Banon 在這篇文章中解釋了其中的原因。zen discovery 模塊包含兩個部分:

  • Ping: 執行節點使用 ping 來發現彼此
  • 單播 (Unicast): 該模塊包含一個主機名列表,用以控制哪些節點需要 ping 通

Elasticsearch 是端對端的系統,其中的所有節點彼此相連,有一個 master 節點保持活躍,它會更新和控制集羣內的狀態和操作。建立一個新的 Elasticsearch 集羣要經過一次選舉,選舉是 ping 過程的一部分,在所有符合條件的節點中選取一個 master,其他節點將加入這個 master 節點。ping 間隔參數ping_interval的默認值是 1 秒,ping 超時參數ping_timeout的默認值是 3 秒。因爲節點要加入,它們會發送一個請求給 master 節點,加入超時參數join_timeout的默認值是ping_timeout值的 20 倍。如果 master 出現問題,那麼羣集中的其他節點開始重新 ping 以啓動另一次選舉。這個 ping 的過程還可以幫助一個節點在忽然失去 master 時,通過其他節點發現 master。

注意:默認情況下,client 節點和 data 節點不參與這個選舉過程。可以在elasticsearch.yml配置文件中,通過設置discovery.zen.master_election.filter_client屬性和discovery.zen.master_election.filter_data屬性爲false來改變這種默認行爲。

故障檢測的原理是這樣的,master 節點會 ping 所有其他節點,以檢查它們是否還活着;然後所有節點 ping 回去,告訴 master 他們還活着。

如果使用默認的設置,Elasticsearch 有可能遭到裂腦問題的困擾。在網絡分區的情況下,一個節點可以認爲 master 死了,然後選自己作爲 master,這就導致了一個集羣內出現多個 master。這可能會導致數據丟失,也可能無法正確合併數據。可以按照如下公式,根據有資格參加選舉的節點數,設置法定票數屬性的值,來避免爆裂的發生。

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

åæElasticsearché群系å第äºç¯ åå¸å¼çä¸ä¸ªCãtranslogåLucene段

這個屬性要求法定票數的節點加入新當選的 master 節點,來完成並獲得新 master 節點接受的 master 身份。對於確保羣集穩定性和在羣集大小變化時動態地更新,這個屬性是非常重要的。圖 a 和 b 演示了在網絡分區的情況下,設置或不設置minimum_master_nodes屬性時,分別發生的現象。

注意:對於一個生產集羣來說,建議使用 3 個節點專門做 master,這 3 個節點將不服務於任何客戶端請求,而且在任何給定時間內總是隻有 1 個活躍。

我們已經搞清楚了 Elasticsearch 中共識的處理,現在讓我們來看看它是如何處理併發的。

併發

Elasticsearch 是一個分佈式系統,支持併發請求。當創建 / 更新 / 刪除請求到達主分片時,它也會被平行地發送到分片副本上。但是,這些請求到達的順序可能是亂序的。在這種情況下,Elasticsearch 使用樂觀併發控制,來確保文檔的較新版本不會被舊版本覆蓋。

每個被索引的文檔都擁有一個版本號,版本號在每次文檔變更時遞增並應用到文檔中。這些版本號用來確保有序接受變更。爲了確保在我們的應用中更新不會導致數據丟失,Elasticsearch 的 API 允許我們指定文件的當前版本號,以使變更被接受。如果在請求中指定的版本號比分片上存在的版本號舊,請求失敗,這意味着文檔已經被另一個進程更新了。如何處理失敗的請求,可以在應用層面來控制。Elasticsearch 還提供了其他的鎖選項,可以通過這篇來閱讀。

當我們發送併發請求到 Elasticsearch 後,接下來面對的問題是——如何保證這些請求的讀寫一致?現在,還無法清楚回答,Elasticsearch 應落在 CAP 三角形的哪條邊上,我不打算在這篇文章裏解決這個素來已久的爭辯。

dc716c148db61cd80449f23de0608b32.jpeguploading.gif正在上傳…重新上傳取消

但是,我們要一起看下如何使用 Elasticsearch 實現寫讀一致。

一致——確保讀寫一致

對於寫操作而言,Elasticsearch 支持的一致性級別,與大多數其他的數據庫不同,允許預檢查,來查看有多少允許寫入的可用分片。可選的值有quorumoneall。默認的設置爲quorum,也就是說只有當大多數分片可用時才允許寫操作。即使大多數分片可用,還是會因爲某種原因發生寫入副本失敗,在這種情況下,副本被認爲故障,分片將在一個不同的節點上重建。

對於讀操作而言,新的文檔只有在刷新時間間隔之後,才能被搜索到。爲了確保搜索請求的返回結果包含文檔的最新版本,可設置 replication 爲sync(默認),這將使操作在主分片和副本碎片都完成後才返回寫請求。在這種情況下,搜索請求從任何分片得到的返回結果都包含的是文檔的最新版本。即使我們的應用爲了更高的索引率而設置了replication=async,我們依然可以爲搜索請求設置參數_preferenceprimary。這樣,搜索請求將查詢主分片,並確保結果中的文檔是最新版本。

我們已經瞭解了 Elasticsearch 如何處理共識、併發和一致,讓我們來看看分片內部的一些主要概念,正是這些特點讓 Elasticsearch 成爲一個分佈式搜索引擎。

Translog(預寫日誌)

因爲關係數據庫的發展,預寫日誌 (WAL) 或者事務日誌 (translog) 的概念早已遍及數據庫領域。在發生故障的時候,translog 能確保數據的完整性。translog 的基本原理是,變更必須在數據實際的改變提交到磁盤上之前,被記錄下來並提交。

當新的文檔被索引或者舊的文檔被更新時,Lucene 索引將發生變更,這些變更將被提交到磁盤以持久化。這是一個很昂貴的操作,如果在每個請求之後都被執行。因此,這個操作在多個變更持久化到磁盤時被執行一次。正如我們在上一篇文章中描述的那樣,Lucene 提交的沖洗 (flush) 操作默認每 30 分鐘執行一次或者當 translog 變得太大 (默認 512MB) 時執行。在這樣的情況下,有可能失去 2 個 Lucene 提交之間的所有變更。爲了避免這種問題,Elasticsearch 採用了 translog。所有索引 / 刪除 / 更新操作被寫入到 translog,在每個索引 / 刪除 / 更新操作執行之後(默認情況下是每 5 秒),translog 會被同步以確保變更被持久化。translog 被同步到主分片和副本之後,客戶端纔會收到寫請求的確認。

在兩次 Lucene 提交之間發生硬件故障的情況下,可以通過重放 translog 來恢復自最後一次 Lucene 提交前的任何丟失的變更,所有的變更將會被索引所接受。

注意:建議在重啓 Elasticsearch 實例之前顯式地執行沖洗 translog,這樣啓動會更快,因爲要重放的 translog 被清空。POST /_all/_flush命令可用於沖洗集羣中的所有索引。

使用 translog 的沖洗操作,在文件系統緩存中的段被提交到磁盤,使索引中的變更持久化。現在讓我們來看看 Lucene 的段。

Lucene 的段

Lucene 索引是由多個段組成,段本身是一個功能齊全的倒排索引。段是不可變的,允許 Lucene 將新的文檔增量地添加到索引中,而不用從頭重建索引。對於每一個搜索請求而言,索引中的所有段都會被搜索,並且每個段會消耗 CPU 的時鐘周、文件句柄和內存。這意味着段的數量越多,搜索性能會越低。

爲了解決這個問題,Elasticsearch 會合並小段到一個較大的段(如下圖所示),提交新的合併段到磁盤,並刪除那些舊的小段。

åæElasticsearché群系å第äºç¯ åå¸å¼çä¸ä¸ªCãtranslogåLucene段

這會在後臺自動執行而不中斷索引或者搜索。由於段合併會耗盡資源,影響搜索性能,Elasticsearch 會節制合併過程,爲搜索提供足夠的可用資源。

接下來有什麼?

從搜索請求角度來說,一個 Elasticsearch 索引中給定分片內的所有 Lucene 段都會被搜索,但是,從 Elasticsearch 集羣角度而言,獲取所有匹配的文檔或者深入有序結果文檔是有害的。在本系列的後續文章中我們將揭曉原因,讓我們來看一下接下來的主題,內容包括了一些在 Elasticsearch 中爲相關性搜索結果的低延遲所做的權衡。

  • Elasticsearch 準實時性方面的內容
  • 爲什麼搜索中的深層分頁是有害的?
  • 搜索相關性計算中的權衡之道

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章