1. 節點
節點按照職責可以分為 master節點
、資料節點
和 協調節點
,每個節點型別可以進行單獨配置。預設情況下,叢集不會對節點角色進行劃分,所有節點都是平等的,可以擔任所有的職責。但是在生產環境中需要對這些節點的角色進行最優劃分,否則在高併發請求的情況下,叢集容易出現服務阻塞超時甚至服務崩潰的隱患。
1. master節點
負責維護整個叢集的相關工作,管理叢集的變更,如建立/刪除索引、節點健康狀態監測、節點上/下線等。
master節點是由叢集節點透過選舉演演算法選舉出來的,一個叢集中只有一個節點可以成為master節點,但是可以有一個或多個節點參與master節點的選舉。在預設情況下,任意節點都可以作為master的候選節點,可以透過配置項node.master對當前節點是否作為master的候選節點進行控制。
為了避免選舉時產生腦裂,建議master的候選節點數量是 2n+1。
2. 資料節點
主要負責索引資料的儲存工作,此外也執行資料的其他操作,如檔案的刪除、修改和查詢操作。
資料節點的很多工作是呼叫Lucene庫進行Lucene索引操作,因此這種節點對於記憶體和I/O的消耗比較大,生產環境中應多注意資料節點的計算機負載情況。
3. 協調節點
客戶端可以向ES叢集的節點發起請求,這個節點叫作協調節點。
在預設情況下,協調節點可以是叢集中的任意節點,此時它的生命週期是和一個單獨的請求相關的。也就是說,當客戶端向叢集中的某個節點發起請求時,此時該節點被稱為當前請求的協調節點;當它將響應結果返回給客戶端後,該協調節點的生命週期就結束了。
當然,為了降低叢集的負載,可以設定某些節點作為單獨的協調節點。在節點的配置檔案中設定 node.master 和 node.data 配置項為 false,此時,這個節點就不會被選中為master節點並且不再擔任資料節點,而客戶端就可以把這類節點作為協調節點來使用,把所有的請求都分發到這些節點上。
2. 分片
一個分片(shard)是一個最小級別“工作單元(worker unit)”,它只是儲存了索引中所有資料的一部分。
分片可以是主分片(primary shard)
或者是 複製分片(replica shard)
(或副分片)。
比如,一個具有10億檔案的索引佔據1TB的磁碟空間,而任一節點都沒有這樣大的磁碟空間;或者單個節點處理搜尋請求,響應太慢。為瞭解決這個問題,ES 提供了將索引劃分成多份的能力,這些份就叫做 分片
。當你建立一個索引的時候,你可以指定你想要的分片的數量。每個分片本身也是一個功能完善並且獨立的“索引”,這個“索引”可以被放置到叢集中的任何節點上。分片很重要,主要有兩方面的原因:
- 允許你水平分割/擴充套件你的內容容量。
- 允許你在分片(潛在地,位於多個節點上)之上進行分散式的、並行的操作,進而提高效能/吞吐量。
至於一個分片怎樣分佈,它的檔案怎樣聚合回搜尋請求,是完全由 ES 管理的,對於作為使用者的你來說,這些都是透明的。
分片還有 副本
replica shard,在一個網路/雲的環境裡,失敗隨時都可能發生,在某個分片/節點不知怎麼的就處於離線狀態,或者由於任何原因消失了,這種情況下,有一個故障轉移機制是非常有用並且是強烈推薦的。為此目的,ES 允許你建立分片的一份或多份複製,這些複製叫做 複製分片
,或者直接叫複製。
2.1. 主分片與副分片
1. 是否後期可修改
- 主分片數量,在建立索引時指定。建立索引後不能修改。
- 副分片數量,在建立索引後,依然能修改。
因為資料儲存時,會拿hash值,根據索引中主分片數量取餘,定位所在分片的地址。如果後續修改了主分片數量,會導致資料的地址錯誤。
2. 數量
- 主分片:預設數量為5,單個分片的大小建議在20G~50G。主分片的數量決定了索引最多能儲存多少資料(實際的數量取決於資料、硬體和應用場景)。
- 副分片:預設數量為1。副分片的數量保證資料的高可靠性,防止資料丟失。
每個主分片都有一個或多個副本分片,當主分片異常時,副本可以提供資料的查詢等操作。
主分片和對應的副本分片是不會在同一個節點上的,所以副本分片數的最大值是 n -1(其中 n 為節點數)。
3. 意義
- 主分片:可水平分割/擴充套件的內容容量;可在分片之上進行分散式的、並行的操作,進而提供效能/吞吐量。
- 副分片:在分片/節點失敗的情況下,提供了高可用性。(注意:副本不能與對應的主分片置於同一節點上);擴充套件搜尋量/吞吐量,因為搜尋可以在所有的副本上並行執行。
4. 副分片選舉生成主分片
es為了更好地穩定性和容災,除了進行必要的索引備份外,副本的新增可以更好地維持叢集資料完整性。
當出現某個節點從叢集脫離,在叢集其他節點的副本,此時會選舉出主分片,所以這裡就有主分片和副本之間的資料同步問題。
5. 主從複製
當有資料寫入時,請求節點收到請求,先轉給主分片所在節點上執行請求,如果成功,它轉發請求到相應的幾個複製分片所在的節點上。當所有的複製分片報告成功,主分片所在節點報告成功到請求的節點,請求的節點再報告給客戶端。
6. 增減節點時,分片自動負載均衡
假設有6臺機器總共7個分片, 肯定就會有1臺機器有2個分片, 此時給 es 叢集新增1臺機器進來,那麼有2個分片的那臺機器會自動給1個分片分配給新加入的機器上去.
2.2. 叢集狀態
ES 的叢集監控資訊中包含了許多的統計資料,其中最為重要的一項就是叢集健康。叢集健康儲存在status欄位中,主要包括 green、yellow、red 三種狀態。它的三種顏色含義如下:
green
:所有的主分片和副本分片都正常執行。yellow
:所有的主分片都正常執行,但不是所有的副本分片都正常執行。red
:有主分片沒能正常執行。
3. 索引
3.1. 倒排索引
1. 正排索引
在說倒排索引之前我們先說說什麼是正排索引。
正排索引也稱為"前向索引",它是建立倒排索引的基礎。這種組織方法在建立索引的時候結構比較簡單,建立比較方便且易於維護;因為索引是基於檔案建立的,若是有新的檔案加入,直接為該檔案建立一個新的索引塊,掛接在原來索引檔案的後面。若是有檔案刪除,則直接找到該檔案號檔案對應的索引資訊,將其直接刪除。
他適合根據檔案ID來查詢對應的內容。但是在查詢一個 keyword 在哪些檔案裡包含的時候需對所有的檔案進行掃描以確保沒有遺漏,這樣就使得檢索時間大大延長,檢索效率低下。
檔案ID | 檔案內容 |
---|---|
1 | elasticsearch是最流行的搜尋引擎 |
2 | php是世界上最好的語言 |
3 | 搜尋引擎是如何誕生的 |
只能在一起簡單的場景下使用,如果透過 keyword 來挨個檢索,效能太低。
2. 倒排索引
根據字面意思可以知道他和正序索引是反的。在搜尋引擎中每個檔案都對應一個檔案ID,檔案內容被表示為一系列關鍵詞的集合。例如“檔案1”經過分詞,提取了3個關鍵詞,每個關鍵詞都會記錄它所在在檔案中的出現頻率及出現位置。
那麼上面的檔案及內容構建的倒排索引結果會如下:
單詞 | 檔案ID列表 |
---|---|
elasticsearch | 1 |
流行 | 1 |
搜尋引擎 | 1,3 |
php | 2 |
世界 | 2 |
最好 | 2 |
語言 | 2 |
如何 | 3 |
誕生 | 3 |
比如我們要查詢‘搜尋引擎’這個關鍵詞在哪些檔案中出現過。
- 首先,我們透過倒排索引可以查詢到,該關鍵詞出現的檔案位置是在1和3中;
- 然後,再透過正排索引查詢到檔案1和3的內容,並返回結果。
3.2. 索引不可變
ES 的搜尋高效的原因並不是像 Redis 那樣重依賴記憶體的,而是透過建立特殊的索引資料結構--倒排索引實現的。倒排索引可以說是 ES 搜尋高效和支援非結構化資料檢索的主要原因了,但是倒排索引被寫入磁碟後是不可改變的,它永遠不會修改。
寫入磁碟的倒排索引是不可變的,優勢主要表現在:
- 不需要鎖。因為如果從來不需要更新一個索引,就不必擔心多個程式同時嘗試修改,也就不需要鎖。
- 一旦索引被讀入核心的檔案系統快取,便會留在哪裡,由於其不變性,只要檔案系統快取中還有足夠的空間,那麼大部分讀請求會直接請求記憶體,而不會命中磁碟。這提供了很大的效能提升。
- 其它快取(像filter快取),在索引的生命週期內始終有效。它們不需要在每次資料改變時被重建,因為資料不會變化。
- 寫入單個大的倒排索引,可以壓縮資料,較少磁碟 IO 和需要快取索引的記憶體大小。
當然,不可變的索引有它的缺點:
- 當對舊資料進行刪除時,舊資料不會馬上被刪除,而是在 .del檔案中被標記為刪除。而舊資料只能等到段更新時才能被移除,這樣會造成大量的空間浪費。
- 若有一條資料頻繁的更新,每次更新都是新增新的標記舊的,則會有大量的空間浪費。
- 每次新增資料時都需要新增一個段來儲存資料。當段的數量太多時,對伺服器的資源例如檔案控制程式碼的消耗會非常大。
- 在查詢的結果中包含所有的結果集,需要排除被標記刪除的舊資料,這增加了查詢的負擔。
總體來看,倒排索引是拿空間換時間。犧牲了空間資源,追求更高的效能。
3.3. 段
1. 段
段
的概念提出主要是因為:在早期全文檢索中為整個檔案集合建立了一個很大的倒排索引,並將其寫入磁碟中。如果索引有更新,就需要重新全量建立一個索引來替換原來的索引。這種方式在資料量很大時效率很低,並且由於建立一次索引的成本很高,所以對資料的更新不能過於頻繁,也就不能保證時效性。
而且在底層採用了分段的儲存模式,使它在讀寫時幾乎完全避免了鎖的出現,大大提升了讀寫效能。(像 ConcurrentHashMap 的分段思想)。
2. 提交點
每一個段本身都是一個倒排索引,但索引在 Lucene 中除表示所有段的集合外,還增加了提交點
的概念。
為了提升寫的效能,Lucene並沒有每新增一條資料就增加一個段,而是採用延遲寫的策略,每當有新增的資料時,就將其先寫入記憶體中,然後批次寫入磁碟中。若有一個段被寫到硬碟,就會生成一個提交點,提交點:就是一個列出了所有已知段和記錄所有提交後的段資訊的檔案。
3. 段合併
由於自動重新整理流程每秒會建立一個新的段 ,這樣會導致短時間內的段數量暴增。而段數目太多會帶來較大的麻煩。 每一個段都會消耗檔案控制程式碼、記憶體和cpu執行週期。更重要的是,每個搜尋請求都必須輪流檢查每個段;所以段越多,搜尋也就越慢。
Elasticsearch透過在後臺進行段合併來解決這個問題。小的段被合併到大的段,然後這些大的段再被合併到更大的段。
4. 近實時搜尋
ES 是怎麼做到近實時全文搜尋?
磁碟是瓶頸。提交一個新的段到磁碟需要fsync操作,確保段被物理地寫入磁碟,即時電源失效也不會丟失資料。但是fsync是昂貴的,嚴重影響效能,當寫資料量大的時候會造成 ES 停頓卡死,查詢也無法做到快速響應。
所以fsync不能在每個檔案被索引的時就觸發,需要一種更輕量級的方式使新的檔案可以被搜尋,這意味移除fsync。
為了提升寫的效能,ES 沒有每新增一條資料就增加一個段到磁碟上,而是採用 延遲寫 的策略。
每當有新增的資料時,就將其先寫入到記憶體中,在記憶體和磁碟之間是檔案系統快取,當達到預設的時間(1秒鐘)或者記憶體的資料達到一定量時,會觸發一次重新整理(Refresh),將記憶體中的資料生成到一個新的段上並快取到檔案快取系統上,稍後再被重新整理到磁碟中並生成提交點。
這裡的記憶體使用的是ES的JVM記憶體,而檔案快取系統使用的是作業系統的記憶體。新的資料會繼續的被寫入記憶體,但記憶體中的資料並不是以段的形式儲存的,因此不能提供檢索功能。由記憶體重新整理到檔案快取系統的時候會生成了新的段,並將段開啟以供搜尋使用,而不需要等到被重新整理到磁碟。
在 Elasticsearch 中,這種寫入和開啟一個新段的輕量的過程叫做 refresh (即記憶體重新整理到檔案快取系統)。預設情況下每個分片會每秒自動重新整理一次。 這就是為什麼說 Elasticsearch 是近實時的搜尋了:檔案的改動不會立即被搜尋,但是會在一秒內可見。