分佈式全文檢索引擎-Elasticsearch

Elasticsearch是一個基於Apache Lucene(TM)的開源搜索引擎。

  • 分佈式的實時文件存儲,每個字段都被索引並可被搜索

  • 分佈式的實時分析搜索引擎

  • 可以擴展到上百臺服務器,處理PB級結構化或非結構化數據

 

在Elasticsearch中,文檔歸屬於一種類型(type),而這些類型存在於索引(index)中,我們可以畫一些簡單的對比圖來類比傳統關係型數據庫:

Relational DB -> Databases -> Tables -> Rows -> Columns 

Elasticsearch -> Indices -> Types -> Documents -> Fields
操作 示例
PUT /{index}/{type}/{id}
{
  "field": "value",
  ...
}

 

DELETE /{index}/{type}/{id}

GET /{index}/{type}/{id}?pretty

 

pretty讓Elasticsearch美化輸出(pretty-print)JSON響應以便更加容易閱讀。_source字段不會被美化,它的樣子與我們輸入的一致

PUT /{index}/{type}/{id}

 

_version增加了

created標識爲false因爲同索引、同類型下已經存在同ID的文檔

 

面試問題 (當前版本 7.X 項目使用5.6.3)

問題 答案
ES 集羣

集羣:由一個或多個節點組成 節點:一個Elasticsearch實例 (有主從)

分片:一個索引有多個分片,創建索引時主分片固定,複製分片可變(有主從)

集羣健康: green 主從分片都可用 yellow 主可用 部分從不可用 red 部分主不可用

Elasticsearch是如何實現Master選舉的?

參考:

https://blog.csdn.net/ailiandeziwei/article/details/87856210

https://www.cnblogs.com/zziawanblog/p/6577383.html

一個ES集羣是由許多節點(Node)構成的,Node可以有不同的類型。通過配置,可以產生四種不同類型的Node:Node是一個node.master和node.data的true/false的兩兩組合。

 

當node.master爲true時,其表示這個node是一個master的候選節點,可以參與選舉,在ES的文檔中常被稱作master-eligible node,類似於MasterCandidate。ES正常運行時只能有一個master(即leader),多於1個時會發生腦裂。

 

master 選舉: 選主是ZenDiscovery模塊負責的

  1. 對所有可以成爲master的節點根據nodeId排序,每次選舉每個節點都把自己所知道節點排一次序,然後選出第一個(第0位)節點,暫且認爲它是master節點。

    注:先根據節點的clusterStateVersion比較,clusterStateVersion越大,優先級越高。clusterStateVersion相同時,進入compareNodes,其內部按照節點的Id比較(Id爲節點第一次啓動時隨機生成)。

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

  3. 對於brain split問題,需要把候選master節點最小值設置爲可以成爲master節點數n/2+1(quorum )

Elasticsearch是如何避免腦裂現象的?

1.當集羣中master候選的個數不小於3個(node.master: true)。可以通過discovery.zen.minimum_master_nodes 這個參數的設置來避免腦裂,設置爲(N/2)+1。

 

2.假如集羣master候選節點爲2的時候,這種情況是不合理的,最好把另外一個node.master改成false。如果我們不改節點設置,還是套上面的(N/2)+1公式,此時discovery.zen.minimum_master_nodes應該設置爲2。這就出現一個問題,兩個master備選節點,只要有一個掛,就選不出master了。

Elasticsearch 文檔索引過程描述

1.協調節點默認使用文檔ID參與計算(也支持通過routing),以便爲路由提供合適的分片。

 

2.當分片所在的節點接收到來自協調節點的請求後,會將請求寫入到Memory Buffer,然後定時(默認是每隔1秒)寫入到Filesystem Cache,這個從Momery Buffer到Filesystem Cache的過程就叫做refresh

 

3.當然在某些情況下,存在Momery Buffer和Filesystem Cache的數據可能會丟失,ES是通過translog的機制來保證數據的可靠性的。其實現機制是接收到請求後,同時也會寫入到translog中,當Filesystem cache中的數據寫入到磁盤中時,纔會清除掉,這個過程叫做flush

 

4.在flush過程中,內存中的緩衝將被清除,內容被寫入一個新段,段的fsync將創建一個新的提交點,並將內容刷新到磁盤,舊的translog將被刪除並開始一個新的translog。

 

5.flush觸發的時機是定時觸發(默認30分鐘)或者translog變得太大(默認爲512M)時。

ES 中索引的不變性及索引的更新、刪除

參考:

https://www.cnblogs.com/wenBlog/p/8489197.html

不變性:寫到磁盤的倒序索引是不變的,自從寫到磁盤就再也不變。

 

動態更新索引:使用不只一個的索引。 新添額外的索引來反映新的更改來替代重寫所有倒序索引的方案。 Lucene引進了per-segment搜索的概念。一個segment是一個完整的倒序索引的子集,所以現在index在Lucene中的含義就是一個segments的集合,每個segment都包含一些提交點(commit point)。

 

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

 

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

Elasticsearch 文搜索引過程描述
  1. 搜索被執行成一個兩階段過程,我們稱之爲 Query Then Fetch

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

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

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

在併發情況下,Elasticsearch如果保證讀寫一致?
  • 可以通過版本號使用樂觀併發控制,以確保新版本不會被舊版本覆蓋,由應用層來處理具體的衝突;

  • 另外對於寫操作,一致性級別支持quorum/one/all,默認爲quorum,即只有當大多數分片可用時才允許寫操作。但即使大多數可用,也可能存在因爲網絡等原因導致寫入副本失敗,這樣該副本被認爲故障,分片將會在一個不同的節點上重建。

  • 對於讀操作,可以設置replication爲sync(默認),這使得操作在主分片和副本分片都完成後纔會返回;如果設置replication爲async時,也可以通過設置搜索請求參數_preference爲primary來查詢主分片,確保文檔是最新版本。

Elasticsearch在部署時,對Linux的設置有哪些優化方法?
  1. 64 GB 內存的機器是非常理想的, 但是32 GB 和16 GB 機器也是很常見的。少於8 GB 會適得其反。

  2. 如果你要在更快的 CPUs 和更多的核心之間選擇,選擇更多的核心更好。多個內核提供的額外併發遠勝過稍微快一點點的時鐘頻率。

  3. 如果你負擔得起 SSD,它將遠遠超出任何旋轉介質。

  4. 即使數據中心們近在咫尺,也要避免集羣跨越多個數據中心。絕對要避免集羣跨越大的地理距離。

  5. 把你的內存的(少於)一半給 Lucene(但不要超過 32 GB!),通過ES_HEAP_SIZE 環境變量設置。

es 在數據量很大的情況下(數十億級別)如何提高查詢效率?

參考:

https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/es-optimizing-query-performance.md

  1. filesystem cache。 從 es 中根據 name 和 age 去搜索,拿到的結果可能就 20 個 doc id,然後根據 doc id 到 hbase 裏去查詢每個 doc id 對應的完整的數據,給查出來,再返回給前端。 議用 es + hbase 這麼一個架構

  2. 數據預熱 做一個專門的緩存預熱子系統,就是對熱數據每隔一段時間,就提前訪問一下,讓數據進入 filesystem cache 裏面去。這樣下次別人訪問的時候,性能一定會好很多。

  3. 冷熱分離 將大量的訪問很少、頻率很低的數據,單獨寫一個索引,然後將訪問很頻繁的熱數據單獨寫一個索引。 冷數據寫入一個索引中,然後熱數據寫入另外一個索引中,這樣可以確保熱數據在被預熱之後,儘量都讓他們留在 filesystem os cache 裏,別讓冷數據給沖刷掉

  4. document 模型設計 在 Java 系統裏就完成關聯,將關聯好的數據直接寫入 es 中。搜索的時候,就不需要利用 es 的搜索語法來完成 join 之類的關聯搜索了。

  5. 分頁性能優化 es 的分頁是較坑的,爲啥呢?舉個例子吧,假如你每頁是 10 條數據,你現在要查詢第 100 頁,實際上是會把每個 shard 上存儲的前 1000 條數據都查到一個協調節點上,如果你有個 5 個 shard,那麼就有 5000 條數據,接着協調節點對這 5000 條數據進行一些合併、處理,再獲取到最終第 100 頁的 10 條數據。

    有什麼解決方案嗎?

    不允許深度分頁(默認深度分頁性能很差)

    類似於 app 裏的推薦商品不斷下拉出來一頁一頁的

    類似於微博中,下拉刷微博,刷出來一頁一頁的,你可以用 scroll api,關於如何使用,自行上網搜索。

    scroll 會一次性給你生成所有數據的一個快照,然後每次滑動向後翻頁就是通過遊標 scroll_id 移動,獲取下一頁下一頁這樣子,性能會比上面說的那種分頁性能要高很多很多,基本上都是毫秒級的。

    但是,唯一的一點就是,這個適合於那種類似微博下拉翻頁的,不能隨意跳到任何一頁的場景

es 生產集羣的部署架構是什麼?每個索引的數據量大概有多少?每個索引大概有多少個分片?

我們 es 集羣的日增量數據大概是 2000 萬條,每天日增量數據大概是 500MB,每月增量數據大概是 6 億,15G。目前系統已經運行了幾個月,現在 es 集羣裏數據總量大概是 100G 左右。

目前線上有 5 個索引(這個結合你們自己業務來,看看自己有哪些數據可以放 es 的),每個索引的數據量大概是 20G,所以這個數據量之內,我們每個索引分配的是 8 個 shard,比默認的 5 個 shard 多了 3 個 shard。

ES 和 mysql 的區別

1.結構名稱不同 數據庫--表--行--列 索引--類型--文檔--字段

2.ES分佈式搜索,傳統數據庫遍歷式搜索

3.ES採用倒排索引,傳統數據庫採用B+樹索引

4.ES沒有用戶驗證和權限控制

5.ES沒有事務的概念,不支持回滾,誤刪不能恢復

6.ES免費,完全開源;傳統數據庫部分免費

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章