Elasticsearch(二)Elasticsearch的原理

Elasticsearch索引的精髓:一切設計都是為了提高搜索的性能。


二、設計原理

2.1 一個空的集群

Figure1 Empty Cluster

如果我們啟動了一個節點,沒有索引沒有數據,那么看起來就像上圖一樣。 一個節點Node運行著ES的實例,一個集群由一個或多個使用著同樣名字(cluster.name)的節點組成,分享數據和負載。 當Node從集群中添加或刪除時,集群會重組自己,使數據平攤的更均勻。

集群中需要有一臺master節點。master的作用是掌管集群的管理工作: 例如創建和刪除索引。 從集群中添加或刪除一臺節點。 master節點無需掌管文檔級的變更和索引。這也意味著在只有一臺master的情況下,隨著負載的增加master不會成為瓶頸。 所有的節點都可能變成master。

作為user,我們可以與任何一臺節點通信,包括master。每一臺節點都知道每一個文檔的位置并且可以將user的請求路由到文檔所在的節點,并且這臺節點負責接收它路由到的node or nodes的響應,并且將數據組織起來返回給用戶。這些對用戶都是透明的。

2.2 創建一個索引—index,shard,cluster

將數據添加到ES的前提是,我們需要一個索引(名詞):index——一個存儲與這個索引相對應數據的地方。實際上,index僅僅只是一個命名空間來指向一個或多個實際的物理分片(shard)。

一個分片(shard)是一個比較低層的工作單元來處理這個索引(index)的所有數據的一個切片(slice)。一個shard實際上是一個Lucene實例,在它的能力范圍內擁有完整的搜索功能(在處理它自己擁有的數據時有所有的功能)。我們所有文檔的索引indexed(動詞)和存儲工作都是在shard上,但這是透明的,我們不需要直接和shard通信,而是和我們創建的index(名詞)通信。

shards是ES將數據分布式在你的集群的關鍵。想象下shards是數據的容器,文檔存儲在shards里,而shards被分配在集群的每一個節點Node里。當你的集群規模增長和降低時,ES會自動的在Nodes間遷移shards以保持集群的負載均衡。

shard的分類與作用:

shard可分為primary shard和replica shard。 在一個index里的每一個文檔都屬于一個單獨的primary shard,所以primary shard的數量決定了你最大能存儲的數據量(對應于一個index)。

注意:shard是歸屬與index的,而不是cluster的。

replica shard是primary shard的拷貝。replica有兩個作用: 1.冗余容災 2.提供讀請求服務,例如搜索或讀取文檔

primary shard的數量在索引創建時確定后不能修改,replica可以在任何時候修改。 例: 見Figure2,在2.1的集群上創建一個index,擁有3個primary shards以及1個replica shards。

由于只有一臺Node,而Primary shard的Replicas與其在同一臺節點上毫無意義,所以集群沒有初始化replicas,這時添加另外一臺Node。見Figure3,每一個primary shard初始化了一個replica。

2.3 水平擴容

當我們繼續添加一臺節點時,Node1和Node2中的各取一個shard移動到了Node3.見Figure4

這樣,我們每一臺Node上只有兩個shard。這就意味著每一臺Node的硬件資源(CPU,RAM,I/O)將會被更少的shards共享,提高了每一個shard的性能。在這個案例中,6個shards最多可使用6臺Node,這樣每個shard就可以使用100%的node硬件資源。

現在我們修改replica的數量到2,如Figure5

這樣我們就有了一個3primary shards,6replica shards的Cluster。我們可將Node提高到9臺。水平擴容了集群性能。

2.4 容災

ES可以容下當節點宕機情況下的異常。例如現在我們殺掉Node1節點。見Figure6

我們殺掉的是master節點。一個Cluster必須要有master以保證集群的功能正常。所以集群要做的第一件事是選擇一個新的master:Node2. 當我們殺掉1節點時,Primary shards 1和2丟失了。如果丟失了primary shard,index(名詞)將不能正常的工作。此時P1和P2的拷貝存在Node2和Node3上。所以此時新升級的master(Node2)將做的第一件事就是將NODE2和NODE3上的replica shard1和replica shard2升級為primary shard。此時如果我們殺掉NODE2,整個集群的容災過程同理,還是可以正常運行。

這時,如果我們重啟了NODE1,cluster將會重新分配缺少的兩個replica shards(現在每個primary shard只有2個replicas,配置是3個,缺少2個)。如果NODE1的數據是舊的,那么它將會繼續利用它們,NODE1只會從現在的Primary Shards拷貝這期間更改的數據。

2.5 分布式文檔存儲

2.5.1 Shards文檔路由

當你對一個文檔建立索引時,它僅存儲在一個primary shard上。ES是怎么知道一個文檔應該屬于哪個shard?當你創建一個新的文檔時,ES是怎么知道應該把它存儲至shard1還是shard2? 這個過程不能隨機無規律的,因為以后我們還要將它取出來。它的路由算法是:

shard = hash(routing) % numberofprimary_shards

routing的值可以是文檔的id,也可以是用戶自己設置的一個值。hash將會根據routing算出一個數值然后%primaryshards的數量。這也是為什么primary_shards在index創建時就不能修改的原因。

問題:當看到這里時,產生了一個問題:ES為什么要這樣設計路由算法,這樣就強制使primaryshards不可變,不利于以后index的擴容,除非事前就要對數據規模有所評估來設計可擴展的index。為什么不能使用一致性hash解決primaryshards改變時的情況呢?

2.5.2 Primary/Replica Shards的交互

假如我們有Figure8的集群。我們可以向這個集群的任何一臺NODE發送請求,每一個NODE都有能力處理請求。每一個NODE都知道每一個文檔所在的位置所以可以直接將請求路由過去。下面的例子,我們將所有的請求都發送到NODE1。

注:最好的實踐方式是輪詢所有的NODE來發送請求,以達到請求負載均衡。

寫操作

創建、索引、刪除文檔都是寫操作,這些操作必須在primary shard完全成功后才能拷貝至其對應的replicas上。見Figure9。

下面是Figure9的步驟:

1.客戶端向Node1發送寫操作的請求。

2.Node1使用文檔的_id來決定這個文檔屬于shard0,然后將請求路由至NODE3,P0所在的位置。

3.Node3在P0上執行了請求。如果請求成功,則將請求并行的路由至NODE1 NODE2的R0上。當所有的replicas報告成功后,NODE3向請求的node(NODE1)發送成功報告,NODE1再報告至Client。

當客戶端收到執行成功后,操作已經在Primary shard和所有的replica shards上執行成功了。

當然,有一些請求參數可以修改這個邏輯。見原文。

讀操作

一個文檔可以在primary shard和所有的replica shard上讀取。見Figure10

讀操作步驟:

1.客戶端發送Get請求到NODE1。

2.NODE1使用文檔的_id決定文檔屬于shard 0.shard 0的所有拷貝存在于所有3個節點上。這次,它將請求路由至NODE2。

3.NODE2將文檔返回給NODE1,NODE1將文檔返回給客戶端。 對于讀請求,請求節點(NODE1)將在每次請求到來時都選擇一個不同的replica。

shard來達到負載均衡。使用輪詢策略輪詢所有的replica shards。

更新操作

更新操作,結合了以上的兩個操作:讀、寫。見Figure11

步驟:

1.客戶端發送更新操作請求至NODE1

2.NODE1將請求路由至NODE3,Primary shard所在的位置

3.NODE3從P0讀取文檔,改變source字段的JSON內容,然后試圖重新對修改后的數據在P0做索引。如果此時這個文檔已經被其他的進程修改了,那么它將重新執行3步驟,這個過程如果超過了retryon_conflict設置的次數,就放棄。

4.如果NODE3成功更新了文檔,它將并行的將新版本的文檔同步到NODE1和NODE2的replica shards重新建立索引。一旦所有的replica

shards報告成功,NODE3向被請求的節點(NODE1)返回成功,然后NODE1向客戶端返回成功。

2.6 Shard

本節將解決以下問題:

為什么搜索是實時的

為什么文檔的CRUD操作是實時的

ES怎么保障你的更新在宕機的時候不會丟失

為什么刪除文檔不會立即釋放空間

2.6.1不變性

寫到磁盤的倒序索引是不變的:自從寫到磁盤就再也不變。 這會有很多好處:

不需要添加鎖。如果你從來不用更新索引,那么你就不用擔心多個進程在同一時間改變索引。

一旦索引被內核的文件系統做了Cache,它就會待在那因為它不會改變。只要內核有足夠的緩沖空間,絕大多數的讀操作會直接從內存而不需要經過磁盤。這大大提升了性能。

其他的緩存(例如fiter cache)在索引的生命周期內保持有效,它們不需要每次數據修改時重構,因為數據不變。

寫一個單一的大的倒序索引可以讓數據壓縮,減少了磁盤I/O的消耗以及緩存索引所需的RAM。

當然,索引的不變性也有缺點。如果你想讓新修改過的文檔可以被搜索到,你必須重新構建整個索引。這在一個index可以容納的數據量和一個索引可以更新的頻率上都是一個限制。

2.6.2動態更新索引

如何在不丟失不變形的好處下讓倒序索引可以更改?答案是:使用不只一個的索引。 新添額外的索引來反映新的更改來替代重寫所有倒序索引的方案。 Lucene引進了per-segment搜索的概念。一個segment是一個完整的倒序索引的子集,所以現在index在Lucene中的含義就是一個segments的集合,每個segment都包含一些提交點(commit point)。見Figure16。新的文檔建立時首先在內存建立索引buffer,見Figure17。然后再被寫入到磁盤的segment,見Figure18。

一個per-segment的工作流程如下:

1.新的文檔在內存中組織,見Figure17。

2.每隔一段時間,buffer將會被提交: 一個新的segment(一個額外的新的倒序索引)將被寫到磁盤 一個新的提交點(commit point)被寫入磁盤,將包含新的segment的名稱。 磁盤fsync,所有在內核文件系統中的數據等待被寫入到磁盤,來保障它們被物理寫入。

3.新的segment被打開,使它包含的文檔可以被索引。

4.內存中的buffer將被清理,準備接收新的文檔。

當一個新的請求來時,會遍歷所有的segments。詞條分析程序會聚合所有的segments來保障每個文檔和詞條相關性的準確。通過這種方式,新的文檔輕量的可以被添加到對應的索引中。

刪除和更新

segments是不變的,所以文檔不能從舊的segments中刪除,也不能在舊的segments中更新來映射一個新的文檔版本。取之的是,每一個提交點都會包含一個.del文件,列舉了哪一個segmen的哪一個文檔已經被刪除了。 當一個文檔被”刪除”了,它僅僅是在.del文件里被標記了一下。被”刪除”的文檔依舊可以被索引到,但是它將會在最終結果返回時被移除掉。

文檔的更新同理:當文檔更新時,舊版本的文檔將會被標記為刪除,新版本的文檔在新的segment中建立索引。也許新舊版本的文檔都會本檢索到,但是舊版本的文檔會在最終結果返回時被移除。

2.6.3實時索引

在上述的per-segment搜索的機制下,新的文檔會在分鐘級內被索引,但是還不夠快。 瓶頸在磁盤。將新的segment提交到磁盤需要fsync來保障物理寫入。但是fsync是很耗時的。它不能在每次文檔更新時就被調用,否則性能會很低。 現在需要一種輕便的方式能使新的文檔可以被索引,這就意味著不能使用fsync來保障。 在ES和物理磁盤之間是內核的文件系統緩存。之前的描述中,Figure19,Figure20,在內存中索引的文檔會被寫入到一個新的segment。但是現在我們將segment首先寫入到內核的文件系統緩存,這個過程很輕量,然后再flush到磁盤,這個過程很耗時。但是一旦一個segment文件在內核的緩存中,它可以被打開被讀取。

2.6.4更新持久化

不使用fsync將數據flush到磁盤,我們不能保障在斷電后或者進程死掉后數據不丟失。ES是可靠的,它可以保障數據被持久化到磁盤。 在2.6.2中,一個完全的提交會將segments寫入到磁盤,并且寫一個提交點,列出所有已知的segments。當ES啟動或者重新打開一個index時,它會利用這個提交點來決定哪些segments屬于當前的shard。 如果在提交點時,文檔被修改會怎么樣?不希望丟失這些修改:

1.當一個文檔被索引時,它會被添加到in-memory buffer,并且添加到Translog日志中,見Figure21.

2.refresh操作會讓shard處于Figure22的狀態:每秒中,shard都會被refreshed:

在in-memory buffer中的文檔會被寫入到一個新的segment,但沒有fsync。

in-memory buffer被清空

3.這個過程將會持續進行:新的文檔將被添加到in-memory buffer和translog日志中,見Figure23

4.一段時間后,當translog變得非常大時,索引將會被flush,新的translog將會建立,一個完全的提交進行完畢。見Figure24

在in-memory中的所有文檔將被寫入到新的segment

內核文件系統會被fsync到磁盤。

舊的translog日志被刪除

translog日志提供了一個所有還未被flush到磁盤的操作的持久化記錄。當ES啟動的時候,它會使用最新的commit point從磁盤恢復所有已有的segments,然后將重現所有在translog里面的操作來添加更新,這些更新發生在最新的一次commit的記錄之后還未被fsync。

translog日志也可以用來提供實時的CRUD。當你試圖通過文檔ID來讀取、更新、刪除一個文檔時,它會首先檢查translog日志看看有沒有最新的更新,然后再從響應的segment中獲得文檔。這意味著它每次都會對最新版本的文檔做操作,并且是實時的。

2.6.5 Segment合并

通過每隔一秒的自動刷新機制會創建一個新的segment,用不了多久就會有很多的segment。segment會消耗系統的文件句柄,內存,CPU時鐘。最重要的是,每一次請求都會依次檢查所有的segment。segment越多,檢索就會越慢。

ES通過在后臺merge這些segment的方式解決這個問題。小的segment merge到大的,大的merge到更大的。。。

這個過程也是那些被”刪除”的文檔真正被清除出文件系統的過程,因為被標記為刪除的文檔不會被拷貝到大的segment中。

合并過程如Figure25:

1.當在建立索引過程中,refresh進程會創建新的segments然后打開他們以供索引。

2.merge進程會選擇一些小的segments然后merge到一個大的segment中。這個過程不會打斷檢索和創建索引。

3.Figure26,一旦merge完成,舊的segments將被刪除

新的segment被flush到磁盤

一個新的提交點被寫入,包括新的segment,排除舊的小的segments

新的segment打開以供索引

舊的segments被刪除

merge大的segments會消耗大量的I/O和CPU,嚴重影響索引性能。默認,ES會節制merge過程來給留下足夠多的系統資源。

核心概念

集群(Cluster):?ES集群是一個或多個節點的集合,它們共同存儲了整個數據集,并提供了聯合索引以及可跨所有節點的搜索能力。多節點組成的集群擁有冗余能力,它可以在一個或幾個節點出現故障時保證服務的整體可用性。

集群靠其獨有的名稱進行標識,默認名稱為“elasticsearch”。節點靠其集群名稱來決定加入哪個ES集群,一個節點只能屬一個集群。

節點(node):?一個節點是一個邏輯上獨立的服務,可以存儲數據,并參與集群的索引和搜索功能, 一個節點也有唯一的名字,群集通過節點名稱進行管理和通信.

主節點:主節點的主要職責是和集群操作相關的內容,如創建或刪除索引,跟蹤哪些節點是群集的一部分,并決定哪些分片分配給相關的節點。穩定的主節點對集群的健康是非常重要的。雖然主節點也可以協調節點,路由搜索和從客戶端新增數據到數據節點,但最好不要使用這些專用的主節點。一個重要的原則是,盡可能做盡量少的工作。

對于大型的生產集群來說,推薦使用一個專門的主節點來控制集群,該節點將不處理任何用戶請求。

數據節點:持有數據和倒排索引。

客戶端節點:它既不能保持數據也不能成為主節點,該節點可以響應用戶的情況,把相關操作發送到其他節點;客戶端節點會將客戶端請求路由到集群中合適的分片上。對于讀請求來說,協調節點每次會選擇不同的分片處理請求,以實現負載均衡。

部落節點:部落節點可以跨越多個集群,它可以接收每個集群的狀態,然后合并成一個全局集群的狀態,它可以讀寫所有節點上的數據。

索引(Index):?ES將數據存儲于一個或多個索引中,索引是具有類似特性的文檔的集合。類比傳統的關系型數據庫領域來說,索引相當于SQL中的一個數據庫,或者一個數據存儲方案(schema)。索引由其名稱(必須為全小寫字符)進行標識,并通過引用此名稱完成文檔的創建、搜索、更新及刪除操作。一個ES集群中可以按需創建任意數目的索引。

文檔類型(Type):類型是索引內部的邏輯分區(category/partition),然而其意義完全取決于用戶需求。因此,一個索引內部可定義一個或多個類型(type)。一般來說,類型就是為那些擁有相同的域的文檔做的預定義。例如,在索引中,可以定義一個用于存儲用戶數據的類型,一個存儲日志數據的類型,以及一個存儲評論數據的類型。類比傳統的關系型數據庫領域來說,類型相當于“表”。

文檔(Document):文檔是Lucene索引和搜索的原子單位,它是包含了一個或多個域的容器,基于JSON格式進行表示。文檔由一個或多個域組成,每個域擁有一個名字及一個或多個值,有多個值的域通常稱為“多值域”。每個文檔可以存儲不同的域集,但同一類型下的文檔至應該有某種程度上的相似之處。相當于數據庫的“記錄”

Mapping:?相當于數據庫中的schema,用來約束字段的類型,不過 Elasticsearch 的 mapping 可以自動根據數據創建

ES中,所有的文檔在存儲之前都要首先進行分析。用戶可根據需要定義如何將文本分割成token、哪些token應該被過濾掉,以及哪些文本需要進行額外處理等等。

分片(shard) :ES的“分片(shard)”機制可將一個索引內部的數據分布地存儲于多個節點,它通過將一個索引切分為多個底層物理的Lucene索引完成索引數據的分割存儲功能,這每一個物理的Lucene索引稱為一個分片(shard)。

每個分片其內部都是一個全功能且獨立的索引,因此可由集群中的任何主機存儲。創建索引時,用戶可指定其分片的數量,默認數量為5個。

Shard有兩種類型:primary和replica,即主shard及副本shard。

Primary shard用于文檔存儲,每個新的索引會自動創建5個Primary shard,當然此數量可在索引創建之前通過配置自行定義,不過,一旦創建完成,其Primary shard的數量將不可更改。

Replica shard是Primary Shard的副本,用于冗余數據及提高搜索性能。

每個Primary shard默認配置了一個Replica shard,但也可以配置多個,且其數量可動態更改。ES會根據需要自動增加或減少這些Replica shard的數量。

ES集群可由多個節點組成,各Shard分布式地存儲于這些節點上。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容