上廳房,下廚房,ElasticSearch有的忙

小姐姐味道發表於2019-01-14

強烈建議先讀一下本公眾號《也淺談下分散式儲存要點》,對ES會有更好的認識。ES融合了倒排索引、行存、列存的諸多特點,已經不再是一個簡單的全文搜尋引擎。

ES是從廚房裡走出來的,原型是Shay Banon給妻子做的食譜搜尋引擎,一開始便充滿了愛的味道。

兒女情長什麼的,最影響程式設計師發揮了!

最終,ElasticSearch斬斷情愫,成為一個基於Lucene的分散式儲存。安裝後訪問其頁面,會顯示:”You Know, for Search“,這句平凡的語句無意中透露的自信,深於一切懷疑和平淡。

幾點說明

幾點說明
索引中的Type定義會引起諸多歧義,已經在6.0版本廢棄
作為搜尋時,是類實時性系統,寫入到讀取之間存在延遲,一般為1秒 ;但當作為儲存時,是RT的
ES索引的分片數一旦確定不可改變,既成事實的Mapping也是
你優化了寫入,就可能干擾了查詢和可靠性,大多時候不可兼得
你會經常碰到OOM,顯然ES是記憶體大戶,缺乏這方面的保護
ES沒有事務

基礎概念

ES是典型的分散式系統。包含一到多個記憶體型節點。有分片,也有副本,可靠性高,自適應能力強。ES採用多機並行能力來進行擴充套件,是建立在一系列層級資料結構上的。這些抽象作為一個全域性的路由表,存在於每個執行的例項上,給了ES強大的功能和擴充套件能力。

上廳房,下廚房,ElasticSearch有的忙

名詞 解釋
Cluster 叢集,有著統一的名稱。包含一個master節點,和落幹其他型別節點。一般為對等節點
Node 節點,ES執行例項,指定叢集中的任意一臺即可加入
Index 索引,邏輯名稱空間。類比DBMS中的
Shard 分片,每個索引包含一個或多個分片,用來將資料分佈在不同例項上
Replica 每個分片又分為主副本和從副本,用來保證HA和Failover。每一個replica為具體lucene例項
Segment 段,倒排索引的子集,會不斷的合併以減少資源佔用
Document 文件,為具體存放的某條記錄,比如某條訂單資訊
Feild 欄位,文件中的某個欄位,可以有不同的型別

索引型別

倒排索引

很多同學有一個誤解,以為ES是一個全文搜尋引擎,那麼就只有倒排索引這一種索引型別,那是錯的。資料在寫入es時,會產生多份資料用於不同查詢方式,使用的索引結構也不相同。

ES預設是對所有欄位進行索引的(也就是倒排索引),如果不需要,可以在mapping中將index屬性設定為no;如果欄位需要精確查詢,則設定為not_analyzed

為了增加倒排索引的Term查詢速度,ES還專門做了Term index,它的本質是一棵Trie(字首)樹(使用FST技術壓縮)。

_all是一個特殊的欄位,可以根據某個關鍵詞,搜尋整個文件內容(而不是某個欄位),這個預設是關閉的。

列式儲存

按照以上的倒排索引結構,查詢包含某個term的文件是非常迅捷的。如果要對這個欄位進行排序的話,倒排索引就捉襟見肘了,需要使用其他的儲存結構進行索引。

ES使用冗餘的方式進行解決這個問題,它儲存了另一份資料,也就是Doc Values。可以說Doc Values是一個列式儲存結構,適合排序、聚合操作等。放在記憶體中的fielddata功能和它類似,但沒有記憶體容量的限制,大資料量優先使用。

到此為止,ES已經預設按照不同的結構儲存了兩份資料了。但如果你不需要,還是可以禁用的。同樣是在mapping玩具中,給欄位賦予屬性"doc_values":false即可。

假如你用的是ELKB系列,倒排索引根本就沒用到。

式儲存

而作為行存_source欄位,以json方式儲存了原始文件。一般是不需要關閉的。但如果你的文件欄位比較多,根據搜尋後查出列表,再根據列表的資料到其他儲存獲取,那麼就可以將_source關掉。類似的,設定"_source"{"enabled":false}即可。

如果只想要幾個欄位被儲存,可以使用include。

"_source":{
    "includes":["field1","field2"]
}
複製程式碼

ES有太多的這種細化的自定義,不再詳敘。

寫入過程

找到分片

某個分片具體在哪個節點上,由ES自行決定。每個節點都快取了這些路由資訊,所以,你的請求傳送到任何一個ES節點上,都可以執行。

ES選擇的分片路由演算法是Hash,這決定了它的分片數一旦確定,不可更改。因為一旦變了,路由的資料就完全不正確了。

如果Request中指定了路由條件,則直接使用Request中的Routing,否則使用Mapping中配置的,如果Mapping中無配置,則使用預設的_id欄位值。

上廳房,下廚房,ElasticSearch有的忙
預設參與Hash計算的欄位是_id,使用ES自帶的生成器能較好的平均資料,使用自定義的id可能會產生資料傾斜。

shard = hash(routing) % number_of_primary_shards
複製程式碼

剩下的,就是單機索引的事了。

單機Shard的寫入過程

ES的寫入效能可以很高(尤其是批量寫入),取決於你的配置。一個文件要寫入索引,直到讀可見,要經過一系列的緩衝和合並。我們拿ES官方部落格的一張圖來說明。

上廳房,下廚房,ElasticSearch有的忙

ES的底層儲存是Lucene,包含一系列的反向索引。這樣的一批索引的資訊就是上面提到的段(segment)。但記錄不會直接寫入段,而是先寫入一個緩衝區。

當緩衝區滿了,或者在緩衝區呆的夠久,達到了重新整理時間(劃重點),會一次性將緩衝區的內容寫進段中。這也是為什麼refresh_interval屬性的配置會嚴重的影響效能。如果你不要很高的實時性,不妨將其配置的大一點。

緩衝區預設使用堆空間的10%,最小值為48mb(針對於分片的)。如果你的索引多且寫入重,這部分記憶體的佔用是可觀的,可以適當加大。


問題是Segment是不可變的,刪除、更新操作,並不能在原來的段上進行。ES對待所有的操作都是相似的,並不區別對待,刪除和更新,有著和寫入一樣的mege過程。這會生成大量的段,每個段會佔用一個檔案控制程式碼,會浪費大量資源。ES有專門的程式負責段的自動合併,我們不需要手動干涉。

段的合併會浪費大量的I/O和CPU資源,有tiered(預設)、log_byte_sizelog_doc三種合併策略,每種策略都有各自的配置引數。可惜的是,索引一旦確定,策略就不能更改了。調整這些引數,大多情況下效果顯著。

常用的配置引數是執行歸併的執行緒數,max_bytes_per_sec已經不再使用了。

index.merge.scheduler.max_thread_count
複製程式碼

如果你的I/O過重,可以適量減少此值的大小。


即然是先寫到緩衝區,就有丟的可能,比如突然斷電。為了解決此問題,ES在寫Buffer的同時,也將資料寫入一個叫做translog的檔案。這個檔案是順序寫,所以速度比較快。translog是故障恢復時,回放故障發生前夕資料的唯一途徑。這些資料,沒有機會能夠寫入到Lucene中。

將translog的寫入週期改成async的,而不是基於請求的,會顯著減少I/O佔用。

作業系統在將磁碟寫入檔案時,也會有相應的buffer cache,這與其他DB如MySQL、PG的工作方式是一樣的,使用fsync保證檔案能夠刷到磁碟上,不多描述。

節點型別

ES按照不同的用途和場景,劃分了不同的節點型別。

Master Node 有資格被選擇為主節點,然後控制整個叢集

Data Node 該節點能夠儲存資料和執行操作

Tribe Node 部落節點,可以連線多個叢集,對外提供統一的入口

Ingest Node 定義一個pipeline來處理資料,可以替代logstash中的某些功能

客戶端節點 不儲存資料也不協調叢集,僅響應使用者請求,將其傳送到其他節點

End

ES通過冗餘多份資料達到不同的用途,開箱即用。開箱即用的意思也就是認識成本低,學習成本大,先把你吸引進門再說。

ES不是一匹好馴服的野馬。通常,它並不像官方和其他PPT宣傳的那樣無所不能,你可能經常在OOM和分片移動中度日。

希望在使用ES之前,能夠了解它的一些底層設計結構。這樣,在遇到一些瓶頸的限制以後,能夠了解到它為什麼有這樣或者那樣的反應;另外在做一些解決方案的時候,能夠多給自己一點底氣。

不過話說回來,上得了廳房,下得了廚房的女人,大家還是都喜歡~

上廳房,下廚房,ElasticSearch有的忙

相關文章