淺談 Elasticsearch 基礎與架構
點選上方藍字關注【 智慧資料部落 】
作者簡介:黎超,清華大學精儀系研究生,目前實習於中國民生銀行資訊科技部大資料平臺開發中心團隊,研究方向為推薦引擎、機器學習。
Elasticsearch 是一個基於Apache Lucene(TM)的開源搜尋引擎,簡稱ES。在分散式系統中應用廣泛,不需要理解其深層工作原理就可以實現搜尋,容易上手,下面本文將簡單介紹其起源、基礎與架構,以及其分散式應用。
1. 起源
一個叫做Shay Banon的剛結婚不久的失業開發者,想為學廚妻子設計食譜搜尋引擎,於是開發出了早期版本的Lucene,並抽象其程式碼以便Java程式員可以在應用中增添搜尋功能。他釋出了第一個開源專案“Compass”。後來他找到一份工作,他把Compass重寫成一個服務——Elasticsearch。第一個公開版本在2010年2月,後來在Github上廣受歡迎,程式碼貢獻者超過300人,於是一家主營Elasticsearch的公司就此成立……
總的來說,Elasticsearch是一個開源的高擴充套件的分散式全文檢索引擎,它可以近乎實時的儲存、檢索資料;本身擴充套件性很好,可以擴充套件到上百臺伺服器,處理PB級別的資料。
2. ES的基礎與架構
2.1 搜尋引擎
ES是一個搜尋引擎,因此我們來介紹一下搜尋程式的基本原理。搜尋程式一般由索引鏈及搜尋元件組成。
索引鏈功能的實現需要按照幾個獨立的步驟依次完成:檢索原始內容、根據原始內容來建立對應的文件、對建立的文件進行索引。
搜尋元件用於接收使用者的查詢請求並返回相應結果,一般由使用者介面、構建可程式設計查詢語句的方法、查詢語句執行引擎及結果展示元件組成。
2.2 ES的架構
ES的架構如下圖所示,Gateway表示ES索引的持久化儲存方式;River代表的是一個數據源,這也是其他儲存方式(比如:資料庫)同步資料到ES的方法;discovery.zen代表 elasticsearch的自動節點發現機制,首先它會通過以廣播的方式去尋找存在的節點,然後再通過多播協議來進行節點之間的通訊,於此同時也支援點對點的互動操作;Transport代表 elasticsearch 內部的節點或者叢集與客戶端之間的互動方式。
除此之外ES還具有一下等特性:
-
分散式索引、搜尋
-
索引自動分片、負載均衡
-
自動發現機器、元件叢集
-
主持Rsetful風格介面
2.3 ES的基礎
ES具有近實時性(Near Realtime[NRT]),這一點有兩個意思,從寫入資料到資料可以被搜尋到有一個小延遲(大概1秒);基於es執行搜尋和分析可以達到秒級。
ES硬體分為叢集(Cluster),節點(Node),分片和複製(Shards & Replicas),一個叢集可以有多個節點,一個節點就是一臺伺服器,可以儲存多個主分片和複製分片。
ES的索引與傳統資料庫可以簡單對照如下。一個索引包含多個type(Elastic 6.x 版只允許每個 Index 包含一個 Type,7.x版將會徹底移除 Type),一個type可以包含多個文件,一個文件包含多個欄位。
去掉type能夠使資料儲存在獨立的index中,這樣即使有相同的欄位名稱也不會出現衝突,就像ElasticSearch出現的第一句話一樣“你知道的,為了搜尋····”,去掉type就是為了提高ES處理資料的效率。除此之外,在同一個索引的不同type下儲存欄位數不一樣的實體會導致儲存中出現稀疏資料,影響Lucene壓縮文件的能力,導致ES查詢效率的降低。
2.4 ES互動方式
-
客戶端方式(Java API):ES內建傳輸客戶端TransportClient,使用ES傳輸協議,通過9300埠與Java客戶端通訊,叢集中各個節點同上。
-
節點客戶端(node client):節點客戶端以無資料節點(none data node)身份加入叢集,換言之,它自己不儲存任何資料,但是它知道資料在叢集中的具體位置,並且能夠直接轉發請求到對應的節點上。
-
傳輸客戶端(Transport client):這個更輕量的傳輸客戶端能夠傳送請求到遠端叢集。它自己不加入叢集,只是簡單轉發請求給叢集中的節點。
3. 分散式特性
3.1 ES分散式特性
Elasticsearch致力於隱藏分散式系統的複雜性。以下這些操作都是在底層自動完成的:
-
無論是增加節點,還是移除節點,分片都可以做到無縫的擴充套件和遷移
-
將你的文件分割槽到不同的容器或者分片(shards)中,它們可以存在於一個或多個節點中。
-
將分片均勻的分配到各個節點,對索引和搜尋做負載均衡。
-
冗餘每一個分片,防止硬體故障造成的資料丟失。
-
將叢集中任意一個節點上的請求路由到相應資料所在的節點。
3.2 分散式增刪改查
3.2.1 路由文件到分片
當你索引一個文件,它被儲存在單獨一個主分片上。Elasticsearch是如何知道文件屬於哪個分片的呢?當你建立一個新文件,它是如何知道是應該儲存在分片1還是分片2上的呢? 根據一個簡單的演算法決定:
routing值是一個任意字串,它預設是_id但也可以自定義。這個routing字串通過雜湊函式生成一個數字,除以主切片的數量得到一個餘數,餘數的範圍永遠是0到number_of_primary_shards - 1,這個數字就是特定文件所在的分片。
3.2.2 新建、索引和刪除文件
新建、索引和刪除請求都是寫(write)操作,它們必須在主分片上成功完成才能複製到相關的複製分片上。
下面我們羅列在主分片和複製分片上成功新建、索引或刪除一個文件必要的順序步驟:
-
客戶端給Node 1傳送新建、索引或刪除請求。
-
節點使用文件的_id確定文件屬於分片0。它轉發請求到Node3,分片0位於這個節點上。
-
Node 3在主分片上執行請求,如果成功,它轉發請求到相應的位於Node 1和Node 2的複製節點上。當所有的複製節點報告成功,Node 3報告成功到請求的節點,請求的節點再報告給客戶端。
客戶端接收到成功響應的時候,文件的修改已經被應用於主分片和所有的複製分片。你的修改生效了。
3.2.3 索引文件
下面我們羅列在主分片或複製分片上檢索一個文件必要的順序步驟:
-
客戶端給Node 1傳送get請求。
-
節點使用文件的_id確定文件屬於分片0。分片0對應的複製分片在三個節點上都有。此時,它轉發請求到Node 2。
-
Node 2返回文件(document)給Node 1然後返回給客戶端。
對於讀請求,為了平衡負載,請求節點會為每個請求選擇不同的分片——它會迴圈所有分片副本。
可能的情況是,一個被索引的文件已經存在於主分片上卻還沒來得及同步到複製分片上。這時複製分片會報告文件未找到,主分片會成功返回文件。一旦索引請求成功返回給使用者,文件則在主分片和複製分片都是可用的。
3.2.4 區域性更新
下面我們羅列執行區域性更新必要的順序步驟:
-
客戶端給Node 1傳送更新請求。
-
它轉發請求到主分片所在節點Node 3。
-
Node 3從主分片檢索出文檔,修改_source欄位的JSON,然後在主分片上重建索引。如果有其他程序修改了文件,它以retry_on_conflict設定的次數重複步驟3,都未成功則放棄。
-
如果Node 3成功更新文件,它同時轉發文件的新版本到Node 1和Node 2上的複製節點以重建索引。當所有複製節點報告成功,Node 3返回成功給請求節點,然後返回給客戶端。
值得注意的是,當主分片轉發更改給複製分片時,並不是轉發更新請求,而是轉發整個文件的新版本。記住這些修改轉發到複製節點是非同步的,它們並不能保證到達的順序與傳送相同。如果Elasticsearch轉發的僅僅是修改請求,修改的順序可能是錯誤的,那得到的就是個損壞的文件。
3.2.5 多文件模式——批量請求
mget和bulk API與單獨的文件類似。差別是請求節點知道每個文件所在的分片。它把多文件請求拆成每個分片的對文件請求,然後轉發每個參與的節點。
一旦接收到每個節點的應答,然後整理這些響應組合為一個單獨的響應,最後返回給客戶端。
下面我們將羅列通過一個mget請求檢索多個文件的順序步驟:
-
客戶端向Node 1傳送mget請求。
-
Node 1為每個分片構建一個多條資料檢索請求,然後轉發到這些請求所需的主分片或複製分片上。當所有回覆被接收,Node 1構建響應並返回給客戶端。
下面我們將羅列使用一個bulk執行多個create、index、delete和update請求的順序步驟:
-
客戶端向Node 1傳送bulk請求。
-
Node 1為每個分片構建批量請求,然後轉發到這些請求所需的主分片上。
-
主分片一個接一個的按序執行操作。當一個操作執行完,主分片轉發新文件(或者刪除部分)給對應的複製節點,然後執行下一個操作。一旦所有複製節點報告所有操作已成功完成,節點就報告success給請求節點,後者(請求節點)整理響應並返回給客戶端。
Bulk API還可以在最上層使用replication和consistency引數,routing引數則在每個請求的元資料中使用。
3.3 分散式搜尋
由於不知道哪個文件會匹配查詢(文件可能存放在叢集中的任意分片上),所以搜尋需要一個更復雜的模型。一個搜尋不得不通過查詢每一個我們感興趣的索引的分片副本,來看是否含有任何匹配的文件。
但是,找到所有匹配的文件只完成了這件事的一半。在搜尋(search)API返回一頁結果前,來自多個分片的結果必須被組合放到一個有序列表中。因此,搜尋的執行過程分兩個階段,稱為 查詢 然後 取回 (query then fetch)。
3.3.1 查詢階段
查詢階段包含三步:
-
客戶端傳送一個search(搜尋)請求給Node 3,Node 3建立了一個長度為from+size的空優先順序佇列。
-
Node 3 轉發這個搜尋請求到索引中每個分片的原本或副本。每個分片在本地執行這個查詢並且結果將結果到一個大小為from+size的有序本地優先佇列裡去。
-
每個分片返回document的ID和它優先佇列裡的所有document的排序值給協調節點Node 3。Node 3把這些值合併到自己的優先佇列裡產生全域性排序結果。
協調節點將這些分片級的結果合併到自己的有序優先佇列裡。這個就代表了最終的全域性有序結果集。到這裡,查詢階段結束。
整個過程類似於歸併排序演算法,先分組排序再歸併到一起,對於這種分散式場景非常適用。
注意:一個索引可以由一個或多個原始分片組成,所以一個對於單個索引的搜尋請求也需要能夠把來自多個分片的結果組合起來。一個對於多(multiple)或全部(all)索引的搜尋的工作機制和這完全一致——僅僅是多了一些分片而已。
3.3.2 取回 階段
查詢階段辨別出那些滿足搜尋請求的document,但我們仍然需要取回那些document本身。
分發階段由以下步驟構成:
-
協調節點辨別出哪個document需要取回,並且向相關分片發出GET請求。
-
每個分片載入document並且根據需要豐富(enrich)它們,然後再將document返回協調節點。
-
一旦所有的document都被取回,協調節點會將結果返回給客戶端。
3.3.3 搜尋選項
-
preference(偏愛):避免結果震盪
preference引數允許你控制使用哪個分片或節點來處理搜尋請求。她接受如下一些引數 primary,primary_first, local, only_node:xyz, prefer_node:xyz和shards:2,3。然而通常最有用的值是一些隨機字串,它們可以避免結果震盪問題(the bouncing results problem)。
想像一下,你正在按照timestamp欄位來對你的結果排序,並且有兩個docuent有相同的timestamp。由於搜尋請求是在所有有效的分片副本間輪詢的,這兩個document可能在原始分片裡是一種順序,在副本分片裡是另一種順序。
這就是被稱為結果震盪(bouncing results)的問題:使用者每次重新整理頁面,結果順序會發生變化。避免這個問題方法是對於同一個使用者總是使用同一個分片。方法就是使用一個隨機字串例如使用者的會話ID(session ID)來設定prefeence引數。
-
timeout (超時): 避免單一問題節點拖慢整個搜尋請求
通常,協調節點會等待接收所有分片的回答。如果有一個節點遇到問題,它會拖慢整個搜尋請求。
timeout引數告訴協調節點最多等待多久,就可以放棄等待而將已有結果返回。返回部分結果總比什麼都沒有好。
-
routing (路由選擇): 可限制搜尋分片,用於設計大搜索系統。
在路由值那節裡,我們解釋瞭如何在建立索引時提供一個自定義的routing引數來保證所有相關的document(如屬於單個使用者的document)被存放在一個單獨的分片中。在搜尋時,你可以指定一個或多個routing 值來限制只搜尋那些分片而不是搜尋index裡的全部分片:GET /_search?routing=user_1,user2 這個技術在設計非常大的搜尋系統時就會派上用場了。
-
search_type (搜尋型別): 根據特定目的設定搜尋型別。
雖然query_then_fetch是預設的搜尋型別,但也可以根據特定目的指定其它的搜尋型別,例如:GET /_search?search_type=count
-
count(計數):count(計數)搜尋型別只有一個query(查詢)的階段。當不需要搜尋結果只需要知道滿足查詢的document的數量時,可以使用這個查詢型別。
-
query_and_fetch(查詢並且取回):query_and_fetch(查詢並且取回)搜尋型別將查詢和取回階段合併成一個步驟。這是一個內部優化選項,當搜尋請求的目標只是一個分片時可以使用,例如指定了routing(路由選擇)值時。雖然你可以手動選擇使用這個搜尋型別,但是這麼做基本上不會有什麼效果。
-
dfs_query_then_fetch 和 dfs_query_and_fetch:dfs搜尋型別有一個預查詢的階段,它會從全部相關的分片裡取回專案頻數來計算全域性的專案頻數。
-
scan(掃描):scan(掃描)搜尋型別是和scroll(滾屏)API連在一起使用的,可以高效地取回巨大數量的結果。它是通過禁用排序來實現的。
4. 總結
本文見解來源於調研,因為知識水平有限且ES的內容實在太多,只介紹了ES的基礎、架構以及分散式方面的簡單執行方式,對於資料、搜尋、對映分析、排序、索引管理以及各種高階的搜尋手段並未進行介紹。
感興趣的同學可以去看一下 Elasticsearch權威指南(中文版) ,內容很全面。
參考文獻
-
https://es.xiaoleilu.com/ 《Elasticsearch權威指南》
歡迎閱讀、關注、轉載、收藏
長按識別二維碼