ElasticSearch探索之路(五)集群与分片:选举、动态更新、近实时搜索、事务日志、段合并
文章目錄
- 集群內部原理
- 集群與節點
- 分片
- 選舉
- 分片內部原理
- 索引不變性
- 動態更新索引
- 近實時搜索
- 事務日志
- 段合并
集群內部原理
集群與節點
一個運行中的Elasticsearch實例稱為一個節點,而集群是由一個或者多個擁有相同 cluster.name 配置的節點組成, 它們共同承擔數據和負載的壓力。當有節點加入集群中或者從集群中移除節點時,集群將會重新平均分布所有的數據。
由于Elasticsearch采用了主從模式,所以當一個節點被選舉成為主節點時, 它將負責管理集群范圍內的所有變更,例如增加、刪除索引,或者增加、刪除節點等。 因為主節點并不需要涉及到文檔級別的變更和搜索等操作,所以當集群只擁有一個主節點的情況下,即使流量的增加它也不會成為瓶頸。 任何節點都可以成為主節點。
作為用戶,我們可以將請求發送到集群中的任何節點(這個處理請求的節點也叫做協調節點)。 每個節點都知道任意文檔所處的位置,并且能夠將我們的請求直接轉發到存儲我們所需文檔的節點。 無論我們將請求發送到哪個節點,它都能負責從各個包含我們所需文檔的節點收集回數據,并將最終結果返回給客戶端。
分片
在分布式系統中,單機無法存儲規模巨大的數據,要依靠大規模集群處理和存儲這些數據,一般通過增加機器數量來提高系統水平擴展能力。因此,需要將數據分成若干小塊分配到各個機器上。然后通過某種路由策略找到某個數據塊所在的位置。
分片(shard)是底層的基本讀寫單元,分片的目的是分割巨大索引,讓讀寫可以并行操作,由多臺機器共同完成。讀寫請求最終落到某個分片上,分片可以獨立執行讀寫工作。Elasticsearch利用分片將數據分發到集群內各處。分片是數據的容器,文檔保存在分片內,不會跨分片存儲。分片又被分配到集群內的各個節點里。當集群規模擴大或縮小時,Elasticsearch會自動在各節點中遷移分片,使數據仍然均勻分布在集群里。
為了應對并發更新問題,Elasticsearch將分片分為兩部分,即主分片(primary shard)和副本分片(replica shard)。主數據作為權威數據,寫過程中先寫主分片,成功后再寫副分片,恢復階段以主分片為準。
一個副本分片只是一個主分片的拷貝。副本分片作為硬件故障時保護數據不丟失的冗余備份,并為搜索和返回文檔等讀操作提供服務。
那索引與分片之間又有什么關系呢?
一個Elasticsearch索引包含了很多個分片,每個分片又是一個Lucene的索引,它本身就是一個完整的搜索引擎,可以獨立執行建立索引和搜索任務。Lucene索引又由很多分段組成,每個分段都是一個倒排索引。Elasticsearch每次refresh都會生成一個新的分段,其中包含若干文檔的數據。在每個分段內部,文檔的不同字段被單獨建立索引。每個字段的值由若干詞(Term)組成,Term是原文本內容經過分詞器處理和語言處理后的最終結果。
索引、分片、段、字段、詞之間的關系選舉
在主節點選舉算法的選擇上,基本原則是不重復造輪子。最好實現一個眾所周知的算法,這樣的好處是其中的優點和缺陷是已知的。Elasticsearch的選舉算法的選擇上主要考慮下面兩種。
- Bully算法:Leader選舉的基本算法之一。它假定所有節點都有一個唯一的ID,使用該ID對節點進行排序。任何時候的當前Leader都是參與集群的最高ID節點。該算法的優點是易于實現。但是,當擁有最大ID的節點處于不穩定狀態的場景下會有問題。例如,Master負載過重而假死,集群擁有第二大ID的節點被選為新主,這時原來的Master恢復,再次被選為新主,然后又假死……
- Paxos算法:Paxos非常強大,尤其在什么時機,以及如何進行選舉方面的靈活性比簡單的Bully算法有很大的優勢,因為在現實生活中,存在比網絡連接異常更多的故障模式。但Paxos實現起來非常復雜。
Elasticsearch的選主算法是基于Bully算法的改進,主要思路是對節點ID排序,取ID值最大的節點作為Master,每個節點都運行這個流程。同時,為了解決Bully算法的缺陷,其通過推遲選舉,直到當前的Master失效來解決上述問題,只要當前主節點不掛掉,就不重新選主。但是容易產生腦裂(雙主),為此,再通過法定得票人數過半解決腦裂問題。
Elasticsearch對Bully附加的三個約定條件
流程如下圖
節點失效檢測會監控節點是否離線,然后處理其中的異常。失效檢測是選主流程之后不可或缺的步驟,不執行失效檢測可能會產生腦裂(雙主或多主)。在此我們需要啟動兩種失效探測器:
- 在Master節點,啟動NodesFaultDetection,簡稱NodesFD。定期探測加入集群的節點是否活躍。
- 非Master節點啟動MasterFaultDetection,簡稱MasterFD。定期探測Master節點是否活躍。
分片內部原理
索引不變性
早期的全文檢索會為整個文檔集合建立一個很大的倒排索引并將其寫入到磁盤。 一旦新的索引就緒,舊的就會被其替換,這樣最近的變化便可以被檢索到。
倒排索引被寫入磁盤后是不可改變的,索引的不變性具有以下好處:
- 不需要鎖。如果你從來不更新索引,你就不需要擔心多進程同時修改數據的問題。
- 一旦索引被讀入內核的文件系統緩存,便會留在哪里。由于其不變性,只要文件系統緩存中還有足夠的空間,那么大部分讀請求會直接請求內存,而不會命中磁盤。這提供了很大的性能提升。
- 緩存(像過濾器緩存)在索引的生命周期內始終有效。它們不需要在每次數據改變時被重建,因為數據不會變化。
- 寫入單個大的倒排索引允許數據被壓縮,減少磁盤 I/O 和 需要被緩存到內存的索引的使用量。
當然,一個不變的索引也有不好的地方。最大的缺點就是它是不可變的,我們無法對其進行修改。如果我們需要讓一個新的文檔可被搜索,就需要重建整個索引。這不僅對一個索引所能包含的數據量造成了巨大的限制,而且對索引可被更新的頻率同樣造成了影響。
動態更新索引
那么我們如何能在保留不變性的前提下實現倒排索引的動態更新呢?
答案就是使用更多的索引,即新增內容并寫到一個新的倒排索引中,查詢時,每個倒排索引都被輪流查詢,查詢完再對結果進行合并。
Elasticsearch基于Lucene引入了按段寫入的概念——每次內存緩沖的數據被寫入文件時,會產生一個新的Lucene段,每個段都是一個倒排索引。同時,在提交點中描述了當前Lucene索引都含有哪些分段。
按段寫入的流程如下:
- 一個新的段(倒排索引)被寫入磁盤。
- 一個新的提交點被寫入磁盤。
- 所有在文件系統緩存中等待的寫入都刷新到磁盤,以確保它們被寫入物理文件。
在一次提交后,一個新的段被添加到提交點而且緩存被清空。
當一個查詢被觸發,所有已知的段按順序被查詢。詞項統計會對所有段的結果進行聚合,以保證每個詞和每個文檔的關聯都被準確計算。 這種方式可以用相對較低的成本將新文檔添加到索引。
那插入和更新又如何實現呢?
段是不可改變的,所以既不能從把文檔從舊的段中移除,也不能修改舊的段來進行反映文檔的更新。 取而代之的是,每個提交點會包含一個 .del 文件,文件中會列出這些被刪除文檔的段信息。
- 當一個文檔被刪除時,它實際上只是在 .del 文件中被標記刪除。一個被標記刪除的文檔仍然可以被查詢匹配到, 但它會在最終結果被返回前從結果集中移除。
- 當一個文檔被更新時,舊版本文檔被標記刪除,文檔的新版本被索引到一個新的段中。 可能兩個版本的文檔都會被一個查詢匹配到,但被刪除的那個舊版本文檔在結果集返回前就已經被移除。
近實時搜索
Elasticsearch和磁盤之間是文件系統緩存,在執行寫操作時,為了降低從索引到可被搜索的延遲,一般新段會被先寫入到文件系統緩存,再將這些數據寫入硬盤(磁盤I/O是性能瓶頸)。
在寫操作中,一般會先在內存中緩沖一段數據,再將這些數據寫入硬盤,每次寫入硬盤的這批數據稱為一個分段。如同任何寫操作一樣,通過操作系統的write接口寫到磁盤的數據會先到達系統緩存(內存)。write函數返回成功時,數據未必被刷到磁盤。通過手工調用flush,或者操作系統通過一定策略將文件系統緩存刷到磁盤。
這種策略大幅提升了寫入效率。從write函數返回成功開始,無論數據有沒有被刷到磁盤,只要文件已經在緩存中, 就可以像其它文件一樣被打開和讀取了。
在內存緩沖區中包含了新文檔的 Lucene 索引Lucene允許新段被寫入和打開——使其包含的文檔在未進行一次完整提交時便對搜索可見。 這種方式比進行一次提交代價要小得多,并且在不影響性能的前提下可以被頻繁地執行。
Elasticsearch中將寫入和打開一個新段的過程叫做refresh(刷新) 。 默認情況下每個分片會每秒自動刷新一次。這就是為什么我們說Elasticsearch是近實時搜索——文檔的變化并不是立即對搜索可見,但會在一秒之內變為可見。
事務日志
由于系統先緩沖一段數據才寫,且新段不會立即刷入磁盤,這兩個過程中如果出現某些意外情況(如主機斷電),則會存在丟失數據的風險。
為了解決這個問題,Elasticsearch增加了一個translog(事務日志),在每一次對Elasticsearch進行操作時均進行了日志記錄,當Elasticsearch啟動的時候,重放translog中所有在最后一次提交后發生的變更操作。
其執行流程如下:
- 新的文檔被添加到內存緩沖區并且被追加到了事務日志,如下圖
- 刷新完成后, 緩存被清空但是事務日志不會,同時新段寫入文件系統緩沖區
- 事務日志不斷積累文檔
- 在刷新(flush)之后,段被全量提交,并且事務日志被清空
除此之外,translog還有下面這些功能
- translog提供所有還沒有被刷到磁盤的操作的一個持久化紀錄。當Elasticsearch啟動的時候, 它會從磁盤中使用最后一個提交點去恢復已知的段,并且會重放translog中所有在最后一次提交后發生的變更操作。
- translog也被用來提供實時CRUD 。當你試著通過ID查詢、更新、刪除一個文檔,在從相應的段中檢索之前, 首先檢查translog任何最近的變更。這意味著它總是能夠實時地獲取到文檔的最新版本。
段合并
由于自動刷新流程每秒會創建一個新的段 ,這樣會導致短時間內的段數量暴增。而段數目太多會帶來較大的麻煩。 每一個段都會消耗文件句柄、內存和cpu運行周期。更重要的是,每個搜索請求都必須輪流檢查每個段,所以段越多,搜索也就越慢。
Elasticsearch通過在后臺進行段合并來解決這個問題,其會選擇大小相似的分段進行合并。在合并過程中,標記為刪除(更新)的數據不會寫入新分段,當合并過程結束,舊的分段數據被刪除,標記刪除的數據才從磁盤刪除。
流程如下圖
一旦合并結束,老的段被刪除
合并大的段需要消耗大量的I/O和CPU資源,如果任其發展會影響搜索性能。Elasticsearch在默認情況下會對合并流程進行資源限制,所以搜索仍然有足夠的資源很好地執行。
整體寫入流程如下圖
總結
以上是生活随笔為你收集整理的ElasticSearch探索之路(五)集群与分片:选举、动态更新、近实时搜索、事务日志、段合并的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ElasticSearch探索之路(四)
- 下一篇: ElasticSearch探索之路(六)