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