1. 程式人生 > >分散式搜尋引擎Elasticsearch(一)

分散式搜尋引擎Elasticsearch(一)

Elasticsearch是一個基於Lucene的開源分散式搜尋引擎,具有分散式多使用者能力,基於RESTful web介面。Elasticsearch是用Java開發的,並作為Apache許可條款下的開放原始碼釋出,是第二流行的企業搜尋引擎。設計用於雲端計算中,能夠達到實時搜尋、高效能運算;同時Elasticsearch的橫向擴充套件能力非常強,不需要重啟服務,基本上達到了零配置。但是目前來說相關資料很少,同時版本更新很快,bug存在,API繁多並且變化。

初識:

索引是一種資料結構,它允許對儲存在其中的單詞進行快速隨機訪問。
當需要從大量文字中快速檢索文字目標時,必須首先將文字內容轉換成能夠進行快速搜尋的格式

,以建立針對文字的索引資料結構,此即為索引過程。

它通常由邏輯上互不相關的幾個步驟組成。

索引處理就是從索引中查詢單詞,從而找到包含該單詞的文件的過程。搜尋質量主要由查準率(Precision)和查全率(Recall)兩個指標進行衡量。查準率用來衡量搜尋系列過濾非相關文件的能力,而查全率用來衡量搜尋系統查詢相關文件的能力。

另外,除了快速搜尋大量文字和搜尋速度之後,搜尋過程還涉及到了許多其它問題,例如單項查詢、多項查詢、短語查詢、萬用字元查詢、結果ranking和排序,以及友好的查詢輸入方式等。這些問題的解決,通常需要多個元件協作完成。

ElasticSearch卻也不僅只是一個全文字搜尋引擎,它還是一個分散式實時文件儲存,其中每個field均是被索引的資料且可被搜尋;也是一個帶實時分析功能的分散式搜尋引擎,並且能夠擴充套件至數以百計的伺服器儲存及處理PB級的資料。

如前所述,ElasticSearch在底層利用Lucene完成其索引功能,因此其許多基本概念源於Lucene。

ElasticSearch概念和設計

索引

索引(index)是Elasticsearch存放資料的地方。索引是具有類似特性的文件的集合。類比傳統的關係型資料庫領域來說,索引相當於SQL中的一個數據庫,或者一個數據儲存方案(schema)。但與關係型資料庫相比,Elasticsearch可以快速、搞笑地對索引中的資料進行全文檢索,並且不需要儲存原始資料。如果你熟悉MongoDB,就可以將Elasticsearch的索引理解為MongoDB中的集合。如果你熟悉CouchDB,就可以將索引理解為CouchDB中的資料庫。
索引由其名稱(必須為全小寫字元)進行標識,並通過引用此名稱完成文件的建立、搜尋、更新及刪除操作。一個ES叢集中可以按需建立任意數目的索引。

型別(Type)

型別是索引內部的邏輯分割槽(category/partition),然而其意義完全取決於使用者需求。因此,一個索引內部可定義一個或多個型別(type)。一般來說,型別就是為那些擁有相同的域的文件做的預定義。
例如,在索引中,可以定義一個用於儲存使用者資料的型別,一個儲存日誌資料的型別,以及一個儲存評論資料的型別。文件型別可以幫助我們輕鬆地區分這些物件。值得注意的是,每個文件可以有不同的結構。在實際操作中,將該文件劃分為不同型別對資料操作有明顯的幫助。劃分時需要牢記一些限制條件,其中一個限制條件就是不同的文件型別對同一欄位不能設定為不同的欄位型別。類比傳統的關係型資料庫領域來說,型別相當於“表”。

文件

文件(document)是Elasticsearch中儲存的主要實體。文件由欄位(行資料的列 “key/value” 的 “key”)組成,Elasticsearch允許一個欄位出現多次,擁有一個名字及一個或多個值,該類欄位被稱為多值欄位(multivalued)。每個欄位對應一種型別(字串型、數值型、日期型等)。欄位型別可以是複合的,欄位可以包含其他子文件或陣列。欄位型別在Elasticsearch中非常重要,它使得搜尋引擎知道應如何執行不同的操作,如比較、排序等。幸運的是Elasticsearch可以自動確定欄位型別。與關係型資料庫不同,Elasticsearch的文件不需要有固定結構,不同文件可以具有不同的欄位集合,而且在程式開發時不需要知道文件的欄位。當然,使用者也可以通過模式對映(schema mapping)定義文件結構。
這裡寫圖片描述

叢集(Cluster)

ES叢集是一個或多個節點的集合,它們共同儲存了整個資料集,並提供了聯合索引以及可跨所有節點的搜尋能力。多節點組成的叢集擁有處理大型資料集並實現容錯功能,它可以在一個或幾個節點出現故障時保證服務的整體可用性。叢集靠其獨有的名稱進行標識,預設名稱為“elasticsearch”。節點靠其叢集名稱來決定加入哪個ES叢集,一個節點只能屬一個叢集。果不考慮冗餘能力等特性,僅有一個節點的ES叢集一樣可以實現所有的儲存及搜尋功能。
叢集裡的每個伺服器則被稱為一個節點(node)。可以通過索引分片(分割成更小的個體此處不懂可以看下邊的分片和副本的概念)將海量資料進行分割並分佈到不同節點。通過副本(索引部分的拷貝)可以實現更強的可用性和更高的效能。這些伺服器被統稱為一個叢集(cluster),

節點(Node)

運行了單個例項的ES主機稱為節點,它是叢集的一個成員,可以儲存資料、參與叢集索引及搜尋操作。類似於叢集,節點靠其名稱進行標識,預設為啟動時自動生成的隨機Marvel字元名稱。使用者可以按需要自定義任何希望使用的名稱,但出於管理的目的,此名稱應該儘可能有較好的識別性。
節點通過為其配置的ES叢集名稱確定其所要加入的叢集。

分片副本

  • ES的“分片(shard)”機制可將一個索引內部的資料分佈地儲存於多個節點,它通過將一個索引切分為多個底層物理的Lucene索引完成索引資料的分割儲存功能,這每一個物理的Lucene索引稱為一個分片(shard)。也就是單個主機上的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分散式地儲存於這些節點上。ES可自動在節點間按需要移動shard,例如增加節點或節點故障時。簡而言之,分片實現了叢集的分散式儲存,而副本實現了其分散式處理及冗餘功能。

對映(Mapping)

ES中,所有的文件在儲存之前都要首先進行分析。使用者可根據需要定義如何將文字分割成token、哪些token應該被過濾掉,以及哪些文字需要進行額外處理等等。
另外,ES還提供了額外功能,例如將域中的內容按需排序。事實上,ES也能自動根據其值確定域的型別。

資料重新分佈recovery

代表資料恢復或叫資料重新分佈,es在有節點加入或退出時會根據機器的負載對索引分片進行重新分配,掛掉的節點重新啟動時也會進行資料恢復。

資料來源river

代表es的一個數據源,也是其它儲存方式(如:資料庫)同步資料到es的一個方法。它是以外掛方式存在的一個es服務,通過讀取river中的資料並把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的。

索引快照gateway

代表es索引快照的儲存方式,es預設是先把索引存放到記憶體中,當記憶體滿了時再持久化到本地硬碟。gateway對索引快照進行儲存,當這個es叢集關閉再重新啟動時就會從gateway中讀取索引備份資料。es支援多種型別的gateway,有本地檔案系統(預設),分散式檔案系統,Hadoop的HDFS和amazon的s3雲端儲存服務。

discovery.zen

代表es的自動發現節點機制,es是一個基於p2p的系統,它先通過廣播尋找存在的節點,再通過多播協議來進行節點之間的通訊,同時也支援點對點的互動。

Transport

代表es內部節點或叢集與客戶端的互動方式,預設內部是使用tcp協議進行互動,同時它支援http協議(json格式)、thrift、servlet、memcached、zeroMQ等的傳輸協議(通過外掛方式整合)。

2 ES的核心Luence

Lucene 是當今最先進,最高效的全功能開源搜尋引擎框架。但只Lucene是一個軟體類庫,如果要發揮Lucene的功能,還需要開發一個呼叫Lucene類庫的應用程式。Elasticsearch 作為一個應用程式使用 Lucene 作為內部引擎進行以下工作:

  • (1)分散式實時檔案儲存,並將每一個欄位都編入索引,使其可以被搜尋。
  • (2)實時分析的分散式搜尋引擎。
  • (3)可擴充套件上百臺伺服器,處理PB級別的結構化或非結構化資料。

在ES 中文件是Lucene索引和搜尋的原子單位,它是包含了一個或多個域的容器,而域的值則是真正被搜尋的內容。每個域都有其標識名稱,通常為一個文字值或二進位制值。將文件加入索引中時,需要首先將資料轉換成Lucene能識別的文件和域,域值是被搜尋的物件。

預設情況下,所有文件都沒有加權值,或者說其加權因子都為1.0。通過改變文件的加權因子,可以指示Lucene在計算相關性時或多或少地考慮到該文件針對索引中其它文件的重要程度,從而能夠將有著較大加權因子的文件排列在較前的位置。

2.2.3 ES的實時性

ES通過開新檔案的方式使資料更快的被檢索使用提高實時性。Lucene 在倒排索引的基礎上通過將新收到的資料寫到新的索引檔案裡做到實時更新條件下資料的可用和可靠。Lucene 把每次生成的倒排索引,叫做一個段(segment)。然後另外使用一個commit檔案,記錄索引內所有的segment。而生成segment 的資料來源,則是記憶體中的 buffer。也就是說,將新接收的資料存在buffer中,記憶體buffer資料轉移到磁碟時生成一個新的segment,同時更新commit 檔案來記錄這個新生成的segment。

由於磁碟速度太慢,所以為了達到實時性效果ES的做法是在記憶體的buffer生成一個新的segment時,把這個segment 刷到檔案系統快取中。此時lucene可以檢索這個新生成的segment。檔案系統快取寫入到磁碟期間可能會產生資料丟失情況,例如發生主機錯誤,硬體故障等異常。

為了保證資料的安全性,ES在把資料寫到記憶體buffer的時候同時還記錄了一個translog日誌。當異常發生時translog日誌檔案依然保持原樣。ES會從commit位置開始,恢復整個translog檔案中的記錄保持資料一致性。當檔案系統的快取真正同步到磁碟上時,commit檔案才更新。Translog才清空。為了保證translog的一致性,ES預設每5秒或者每次請求操作之前都強制將translog的內容同步到磁碟上。這樣降低了一點點ES的效能,但是提高了資料的安全性。ES提供了單獨的/_refresh介面,預設將新生成的segment刷到檔案系統快取的時間間隔是1秒,對於大部分應用來說1秒的時間間隔都算是實時搜尋了。使用者如果對1秒間隔還不滿意的,可以主動呼叫該介面來保證搜尋可見。ES也提供了/_flush介面,預設每30分鐘(或者translog檔案大小大於512MB,老版本是200MB)主動重新整理一次translog。這兩個行為,可以分別通過引數修改。

不過對於ELK的日誌場景來說,我們並不需要如此高的實時性,而是需要更快的寫入效能。所以,一般來說,我們反而會通過/_settings介面或者定製 template的方式,加大refresh_interval 引數:

# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_settings -d'
{ "refresh_interval": "10s" }’

如果是匯入歷史資料的場合,那甚至可以先完全關閉掉:

# curl -XPUT http://127.0.0.1:9200/logstash-2015.05.01 -d'
{"settings" : {"refresh_interval": "-1"}}'

2.2.4 ES的segment合併

ES通過新建segment提高實時性,會產生大量零碎的segment。給伺服器負載帶來壓力,消耗cup記憶體等資源。所以ES後臺會通過一個獨立的執行緒在不影響新的segment產生的情況下,不斷的主動將零碎的segment做資料歸併,儘量讓索引內只保留有少量的,每個都比較大的segment檔案。歸併過程中,尚未完成歸併的較大的segment是被排除在檢索可見範圍之外的。歸併完成後,較大的segment寫入到磁碟,commit 檔案做出相應的變更,刪除之前較小的segment,改成新的大的segment。當檢索請求都從小的segment轉移到大的segment時,刪除那些小的segment。從而降低segment的數量。

歸併執行緒是按照一定的執行策略來挑選segment 進行歸併的。主要有以下幾條:

  • index.merge.policy.floor_segment預設2MB,小於這個大小的segment,優先被歸併。
  • index.merge.policy.max_merge_at_once預設一次最多歸併10 個segment。
  • index.merge.policy.max_merge_at_once_explicit預設optimize 時一次最多歸併30 個segment。

由於預設的最大segment大小是5GB。一個比較龐大的資料索引,會有不少的segment永遠存在,這對檔案控制代碼,記憶體等資源都是極大的浪費。由於歸併任務太消耗資源,所以選擇在負載較低的時間段,通過optimize介面,強制歸併segment。

2.2.5 ES路由計算

在一個沒有額外依賴的簡單的分散式方案中,ES對任一資料計算起儲存對應的分片方式如下:shard=hash(routing)%number_of_primary_shards每條資料都有自己的routing引數,預設情況下,將其_id值進行計算雜湊後,對索引中的主分片數取餘,就是資料實際應該儲存到的分片ID。由於這個計算方式使用索引中的主分片數作為分母,所以整個索引中的主分片數不能更改,一但更改就會導致所有資料的儲存位置計算結果發生變化,索引資料就不可讀了。

基於這個路由規則,資料在ES 叢集中的儲存流程如圖2-2。當客戶端發起請求給node1節點,node1拿到請求資料通過資料的_id計算出資料應該儲存在shard0上,通過cluster state 資訊發現shard0在node3上,所以轉發請求到node3。Node3完成請求資料的索引過程,將資料存入主分片0然後並行轉發資料給分配有shard0的副本的node1和node2節點。當收到任一節點彙報副本分片資料寫入成功,node3才返回給初始接收客戶請求的節點node1,宣佈資料寫入成功。node1返回成功響應給客戶端。

這裡寫圖片描述

2.2.6 ES的shard分配機制

Shard分配在那個節點上是由ES自動決定的,只有新索引生成、索引刪除、新增副本分片和節點增減引發的資料均衡情況會觸發shard分配操作。

ES為了保護節點資料安全,採用磁碟限額策略。預設每隔30 秒會檢查各個節點的資料目錄磁碟使用情況,預設達到85%時,新索引就不會再分配到這個節點上。預設達到90%時就會觸發該節點現存分片的資料均衡,把資料挪到其他的節點去。

ES叢集的資料均衡策略是以各個節點的分片總數為基準的。這是均衡搜尋壓力提高效能的好方法。但對於ELK場景,一般壓力集中在新索引的資料寫入方面。當叢集擴容是,新加入叢集的節點,分片數遠遠低於其他節點。這個時候如果有新的索引建立,ES會預設將新索引的所有主分片幾乎全分配到這臺節點上。整個叢集的寫入壓力,壓在這個節點上,結果是直接把這個節點壓死。叢集出現異常。故對於ELK場景應該預先計算好索引的分片數,配置好單節點的分片限額。

為了解決大規模查詢,大量讀IO操作及聚合計算導致機器load增高cpu使用率升高,影響阻塞到新資料的寫入,ES採用了讀寫分離方案。可以設定N臺機器做熱資料的儲存,上面只放當天的資料。只需在這N臺節點的elasticsearc.yml 中配置node.tag: hot之前的M臺機器作為冷資料節點,只需在這M臺節點配置node.tag:stale。每天計劃任務更新索引的配置把N臺節點的配置更改為stale,索引會自動遷移到M臺冷資料節點。

關於安裝請看下篇部落格