1. 程式人生 > >elasticsearch原理剖析

elasticsearch原理剖析

elasticsearch原理


Master/Slave架構 VS P2P 環形結構

  • Master-Slave
    master 節點負責管理整個系統,監視 slave 節點的執行狀態,同時為其下的每一個 slave 節點分配儲存的範圍,是查詢和寫入的入口.master 節點一般全域性只有 1個,該節點的狀態將嚴重影響整個系統的效能,當 master 節點宕機時,會引起整個系統的癱瘓.實踐中,經常設定多個副本 master 節點,通過聯機熱備的方式提高系統的容錯性.

  • P2P 環形結構
    P2P結構中沒有master節點

Master-Slave結構的系統設計簡單,可控性好,但 master
中心節點易成為瓶頸(bigtable為master節點不成為瓶頸做了很多措施);P2P環形結構的系統無中心節點,自協調性好,擴充套件方便,但可控性較差,且系統設計比master-slave 結構的系統要複雜.

Hbase,bigtable,es和HDFS一樣採用master/slave架構。

hbase存多讀少,不適合高併發查詢,適合存資料; es是全文檢索,適合日誌分析日誌統計之類。

es概述

ES 在 Master 被選舉之前是一個 P2P 的系統,但是當 Master 被選取後,它的管理本質上是 Master 和 slave的模式。

Elasticsearch 看名字就能大概瞭解下它是一個彈性的搜尋引擎。首先彈性隱含的意思是分散式,單機系統是沒法彈起來的,然後加上靈活的伸縮機制,就是這裡的 Elastic 包含的意思。它的搜尋儲存功能主要是 Lucene 提供的,Lucene 相當於其儲存引擎,它在之上封裝了索引,查詢,以及分散式相關的介面。

Elasticsearch是一個實時(索引資料到能被搜尋大概1s左右)的分散式搜尋和分析引擎,主要用於全文搜尋,結構化搜尋以及分析。Elasticsearch使用Lucene作為內部引擎,但是在使用它做全文搜尋時,只需要使用統一開發好的API即可,而不需要了解其背後複雜的Lucene的執行原理,可以說是一個開箱即用的分散式實現,其內部定義了大量的預設值。Elasticsearch並不僅僅是Lucene這麼簡單,它不但包括了全文搜尋功能,還可以進行以下工作:

  1. 分散式實時檔案儲存,並將每一個欄位都編入索引,使其可以被搜尋。

  2. 實時分析的分散式搜尋引擎。

  3. 可以擴充套件到上百臺伺服器,處理PB級別的結構化或非結構化資料。

ES提供了兩套API(內部呼叫都指向同一個地方)分別是基於curl的rest API和Java API,通過API不僅能對資料的CURD進行處理,還能對索引及es叢集進行關聯

es的基本概念

Elasticsearch的底層搜尋是以lucene來實現的。es其主要是提供了一個分散式的框架來擴充套件了lucene,從而實現大資料量的,分散式搜尋功能。其實現思想很簡單,將大資料量分而治之,雜湊分成多份,然後對每一份進行“lucene處理”——用lucene索引、檢索,最後將每份結果合併返回。

image.png-63.1kB

Lucene中包含了四種基本資料型別,分別是:
Index:索引,由很多的Document組成。
Document:由很多的Field組成,是Index和Search的最小單位。
Field:由很多的Term組成,包括Field Name和Field Value。
Term:由很多的位元組組成。一般將Text型別的Field Value分詞之後的每個最小單元叫做Term。

image.png-6.7kB

es 中的幾個核心概念

  • 叢集(Cluster)一組擁有共同的 cluster name 的節點。
  • 節點(Node) 叢集中的一個 Elasticearch 例項。
  • 索引(Index) 相當於關係資料庫中的database概念,一個叢集中可以包含多個索引。這個是個邏輯概念。
  • 主分片(Primary shard) 索引的子集,索引可以切分成多個分片,分佈到不同的叢集節點上。分片對應的是 Lucene 中的索引。
  • 副本分片(Replica shard)每個主分片可以有一個或者多個副本。
  • 型別(Type)相當於資料庫中的table概念,mapping是針對 Type 的。同一個索引裡可以包含多個 Type。
  • Mapping 相當於資料庫中的schema,用來約束欄位的型別,不過 Elasticsearch 的 mapping 可以自動根據資料建立。
  • 文件(Document) 相當於資料庫中的row。
  • 欄位(Field)相當於資料庫中的column。
  • 分配(Allocation) 將分片分配給某個節點的過程,包括分配主分片或者副本。如果是副本,還包含從主分片複製資料的過程。
  • gateway: 代表es索引快照的儲存方式,es預設是先把索引存放到記憶體中,當記憶體滿了時再持久化到本地硬碟。gateway對索引快照進行儲存,當這個es叢集關閉再重新啟動時就會從gateway中讀取索引備份資料。es支援多種型別的gateway,有本地檔案系統(預設),分散式檔案系統,Hadoop的HDFS和amazon的s3雲端儲存服務。

es的索引資料結構

傳統資料庫為特定列增加一個索引,例如B-Tree索引來加速檢索。Elasticsearch和Lucene使用倒排索引(inverted index)來達到相同目的,倒排索引中用到的資料結構是FST樹

es優點

elasticsearch主要優勢是:速度快,使用方便,分散式的,檢索,功能強大。
ES官方的想做的是ELK結合起來做日誌分析等工作。估計這也是它最多的應用場景。

Elasticsearch 現在的主要目標市場已經從站內搜尋轉移到了監控與日誌資料的收集儲存和分析,也就是大家常談論的ELK。

Elasticsearch 現在主要的應用場景有三塊。站內搜尋,主要和 Solr 競爭,屬於後起之秀。NoSQL json文件資料庫,主要搶佔 Mongo 的市場,它在讀寫效能上優於 Mongo,同時也支援地理位置查詢,還方便地理位置和文字混合查詢,屬於歪打正著。監控,統計以及日誌類時間序的資料的儲存和分析以及視覺化,這方面是引領者。

es架構

es.png-102.6kB

Gateway是ES用來儲存索引的檔案系統,支援多種型別。
Gateway的上層是一個分散式的lucene框架。
Lucene之上是ES的模組,包括:索引模組、搜尋模組、對映解析模組等
ES模組之上是 Discovery、Scripting和第三方外掛。Discovery是ES的節點發現模組,不同機器上的ES節點要組成叢集需要進行訊息通訊,叢集內部需要選舉master節點,這些工作都是由Discovery模組完成。支援多種發現機制,如 Zen 、EC2、gce、Azure。Scripting用來支援在查詢語句中插入javascript、python等指令碼語言,scripting模組負責解析這些指令碼,使用指令碼語句效能稍低。
ES也支援多種第三方外掛。
再上層是ES的傳輸模組和JMX.傳輸模組支援多種傳輸協議,如 Thrift、memecached、http,預設使用http。JMX是java的管理框架,用來管理ES應用。
最上層是ES提供給使用者的介面,可以通過RESTful介面或java api和ES叢集進行互動。

服務發現以及選主 ZenDiscovery

  1. 節點啟動後先ping(這裡的ping是 Elasticsearch 的一個RPC命令。如果 discovery.zen.ping.unicast.hosts 有設定,則ping設定中的host,否則嘗試ping localhost 的幾個埠, Elasticsearch 支援同一個主機啟動多個節點)Ping的response會包含該節點的基本資訊以及該節點認為的master節點。
  2. 選舉開始,先從各節點認為的master中選,規則很簡單,按照id的字典序排序,取第一個。
  3. 如果各節點都沒有認為的master,則從所有節點中選擇,規則同上。這裡有個限制條件就是 discovery.zen.minimum_master_nodes,如果節點數達不到最小值的限制,則迴圈上述過程,直到節點數足夠可以開始選舉。
  4. 最後選舉結果是肯定能選舉出一個master,如果只有一個local節點那就選出的是自己。
  5. 如果當前節點是master,則開始等待節點數達到 minimum_master_nodes(最小候選節點數),然後提供服務。
  6. 如果當前節點不是master,則嘗試加入master。

Elasticsearch 將以上服務發現以及選主的流程叫做 ZenDiscovery 。由於它支援任意數目的叢集(1-N),所以不能像 Zookeeper/Etcd那樣限制節點必須是奇數,也就無法用投票的機制來選主,而是通過一個規則,只要所有的節點都遵循同樣的規則,得到的資訊都是對等的,選出來的主節點肯定是一致的。但分散式系統的問題就出在資訊不對等的情況,這時候很容易出現腦裂(Split-Brain)的問題,大多數解決方案就是設定一個quorum值,要求可用節點必須大於quorum(一般是超過半數節點),才能對外提供服務。而 Elasticsearch 中,這個quorum的配置就是 discovery.zen.minimum_master_nodes 。

es是如何實現Master選舉的

Elasticsearch的選舉是ZenDiscovery模組負責的,通過多播或單播技術來發現同一個叢集中的其他節點並與它們連線。

一個節點如何選取它自己認為的master節點?
它會對所有可以成為master的節點(node.master: true)根據nodeId字典排序,,然後選出第一個(第0位)節點,暫且認為它是master節點。

如果對某個節點的投票數達到一定的值(可以成為master節點數n/2+1)並且該節點自己也選舉自己,那這個節點就是master。否則重新選舉一直到滿足上述條件。

多播和單播

基於以下假設: 叢集由cluster.name設定項相同的節點自動連線而成,同一個網段中存在多個獨立的叢集.Zen 發現機制是ElasticSearch中預設的用來發現新節點的功能模組,而且叢集啟動後預設生效。Zen發現機制預設配置是用多播來尋找其它的節點。如果各個模組工作正常,該節點就會自動新增到與節點中叢集名字(cluster.name)一樣的叢集,同時其它的節點都能感知到新節點的加入。在比較大的叢集中,多播發現機制可能會產生太多不必要的流量開銷,Zen發現機制引入了第二種發現節點的方法:單播模式。關於名詞多播和單播:
連結:https://www.zhihu.com/question/29360024/answ

  • 多播:當節點並非叢集的一部分時(比如節點只是剛剛啟動或者重啟 ),它會發送一個多播的ping請求到網段中,該請求只是用來通知所有能連線到節點和叢集它已經準備好加入到叢集中。
  • 單播: 關閉多播,就可以安全地使用單播。當節點不是叢集的一部分時(比如節點重啟,啟動或者由於某些錯誤從叢集中斷開),節點會發送一個ping請求到事先設定好的地址中,來通知叢集它已經準備好加入到叢集中了。

為了安全考慮,阿里一般用單播模式。

ElasticSearch執行時會啟動兩個探測程序。一個程序用於從主節點向叢集中其它節點發送ping請求來檢測節點是否正常可用。另一個程序的工作反過來了,其它的節點向主節點發送ping請求來驗證主節點是否正常且忠於職守

es是如何避免腦裂現象的

可以通過discovery.zen.minimum_master_nodes
這個引數的設定來避免腦裂,設定為(N/2)+1。

es的叢集只有一個節點的話可以有副本嗎?

Elasticsearch 禁止同一個分片的主分片和副本分片在同一個節點上,所以如果是一個節點的叢集是不能有副本的。

叢集如何恢復以及容災

分散式系統的一個要求就是要保證高可用。如果是故障導致節點掛掉,Elasticsearch 就會主動allocation。但如果節點丟失後立刻allocation,稍後節點恢復又立刻加入,會造成浪費。Elasticsearch的恢復流程大致如下:

  • 叢集中的某個非master節點丟失網路連線
  • 如果該節點上的分片有副本,那麼master提升該節點上的所有主分片的在其他節點上的副本為主分片。cluster叢集狀態變為 yellow ,因為副本數不夠
    等待一個超時設定的時間,如果丟失節點回來就可以立即恢復(預設為1分鐘,通過 index.unassigned.node_left.delayed_timeout 設定)。如果該分片已經有寫入,則通過translog進行增量同步資料。
    否則將副本分配給其他節點,開始同步資料。
  • 但如果該節點上的分片沒有副本,則無法恢復,叢集狀態會變為red,表示可能要丟失該分片的資料了。

如果是主節點master掛掉怎麼辦呢?當從節點們發現和主節點連線不上了,那麼他們會自己決定再選舉出一個節點為主節點。但是這裡有個腦裂的問題,假設有5臺機器,3臺在一個機房,2臺在另一個機房,當兩個機房之間的聯絡斷了之後,每個機房的節點會自己聚會,推舉出一個主節點。
這個時候就有兩個主節點存在了,當機房之間的聯絡恢復了之後,這個時候就會出現資料衝突了。解決的辦法就是設定引數: discovery.zen.minimum_master_nodes
為3(超過一半的節點數),那麼當兩個機房的連線斷了之後,就會以大於等於3的機房的master為主,另外一個機房的節點就停止服務了。

es搜尋的過程描述(預設搜尋方式Query Then Fetch)

Query Then Fetch
如果你搜索時,沒有指定搜尋方式,就是使用的這種搜尋方式。這種搜尋方式,大概分兩個步驟,第一步,先向所有的shard發出請求,各分片只返回排序和排名相關的資訊(注意,不包括文件document),然後按照各分片返回的分數進行重新排序和排名,取前size個文件。然後進行第二步,去相關的shard取document。這種方式返回的document與使用者要求的size是相等的。

搜尋被執行成一個兩階段過程,我們稱之為 Query Then Fetch

在初始查詢階段時,查詢會廣播到索引中每一個分片拷貝(主分片或者副本分片)。 每個分片在本地執行搜尋並構建一個匹配文件的大小為 from + size 的優先佇列。PS:在搜尋的時候是會查詢Filesystem Cache的,但是有部分資料還在Memory Buffer,所以搜尋是近實時的。

每個分片返回各自優先佇列中 所有文件的 ID 和排序值 給協調節點,它合併這些值到自己的優先佇列中來產生一個全域性排序後的結果列表。

接下來就是 取回階段,協調節點辨別出哪些文件需要被取回並向相關的分片提交多個 GET 請求。每個分片載入並豐富文件,如果有需要的話,接著返回文件給協調節點。一旦所有的文件都被取回了,協調節點返回結果給客戶端。

es叢集分片的讀寫操作流程

1、路由計算(routing)和副本一致性(replica)

  • routing

Elasticsearch針對路由計算選擇了一個很簡單的方法,計算如下:

routing = hash(routing) % number_of_primary_shards

每個資料都有一個routing引數,預設情況下,就使用其_id值,將其_id值計算hash後,對索引的主分片數取餘,就是資料實際應該儲存到的分片ID

由於取餘這個計算,完全依賴於分母,所以導致Elasticsearch索引有一個限制,索引的主分片數,不可以隨意修改。因為一旦主分片數不一樣,索引資料不可讀。

  • 副本一致性(replica)

作為分散式系統,資料副本可算是一個標配。Elasticsearch資料寫入流程。自然涉及副本,在有副本配置的情況下,資料從發向Elasticsearch節點,到接到Elasticsearch節點響應返回,流向如下
1.png-40.7kB
1)客戶端請求傳送給master Node1節點,這裡也可以傳送給其他節點

2)Node1節點用資料的_id計算出資料應該儲存在shard0上,通過cluster state資訊發現shard0的主分片在Node3節點上,Node1轉發請求資料給Node3,Node3完成資料的索引,索引過程在上篇部落格中詳細介紹了。

3)Node3並行轉發資料給分配有shard0的副本分片Node1和Node2上。當收到任一節點彙報副本分片資料寫入成功以後,Node3即返回給初始的接受節點Node1,宣佈資料寫入成功。Node1成功返回給客戶端。

2、shard的allocate配置
上文介紹了分片的索引過程,通過路由計算可以確定文字所在的分片id,那麼分片在叢集中的分配策略是如何確定的?
一般來說,某個shard分配在哪個節點上,是由Elasticsearch自動決定的。以下幾種情況會觸發分配動作。

新索引生成
索引的刪除
新增副本分片
節點增減引發的資料均衡

es中的shard(分片)

Shard 實際上是一個 Lucene 的一個例項(Lucene Index),但往往一個 Elastic Index 都是由多個 Shards (primary & replica)構成的。
特別注意,在單個 Lucene 例項裡最多包含2,147,483,519 (= Integer.MAX_VALUE - 128) 個 Documents。

Lucene Index結構

一個 Lucene Index 在檔案系統的表現上來看就是儲存了一系列檔案的一個目錄。一個 Lucene Index 由許多獨立的 segments 組成,而 segments 包含了文件中的詞彙字典、詞彙字典的倒排索引以及 Document 的欄位資料(設定為Stored.YES的欄位),所有的 segments 資料儲存於 _.cfs的檔案中。

Segment

segment.png-64.6kB
Segment 直接提供了搜尋功能的,ES 的一個 Shard (Lucene Index)中是由大量的 Segment 檔案組成的,且每一次 fresh 都會產生一個新的 Segment 檔案,這樣一來 Segment 檔案有大有小,相當碎片化。ES 內部則會開啟一個執行緒將小的 Segment 合併(Merge)成大的 Segment,減少碎片化,降低檔案開啟數,提升 I/O 效能。

Segment 檔案是不可變更的。當一個 Document 更新的時候,實際上是將舊的文件標記為刪除,然後索引一個新的文件。在 Merge 的過程中會將舊的 Document 刪除掉。具體到檔案系統來說,文件 A 是寫入到 .cfs 檔案裡的,刪除文件 A 實際上是在.del檔案裡標記某個 document 已被刪除,那麼下次查詢的時候則會跳過這個文件,是為邏輯刪除。當歸並(Merge)的時候,老的 segment 檔案將會被刪除,合併成新的 segment 檔案,這個時候也就是物理刪除了。

Es-cluster.png-37.5kB
1_3xcgM8oZUTSV5ZVEjCRnNA.png-10.2kB

新建index,但是還未插入資料時的目錄結構:
mulu.png-31.9kB
插入資料之後會多很多檔案:

儲存原文_source的檔案.fdt .fdm .fdx;

儲存倒排索引的檔案.tim .tip .doc;

用於聚合排序的列存檔案.dvd .dvm;

全文檢索檔案.pos .pay .nvd .nvm等。

載入到記憶體中的檔案有.fdx .tip .dvm,

其中.tip佔用記憶體最大,而.fdt . tim .dvd文-件佔用磁碟最大
另外segment較小時檔案內容是儲存在.cfs檔案中,.cfe檔案儲存Lucene各檔案在.cfs檔案的位置資訊,這是為了減少Lucene開啟的檔案控制代碼數。

儲存檔案型別比較可見:https://www.itcodemonkey.com/article/8954.html

參考連結

https://zhuanlan.zhihu.com/p/33671444
https://yq.aliyun.com/articles/581877
https://www.cnblogs.com/LBSer/p/4119841.html
https://my.oschina.net/u/2935389/blog/754674
https://blog.csdn.net/yangwenbo214/article/details/77802331
https://www.jianshu.com/p/2cac077e05cf