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

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

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

兒女情長什麼的,最影響程序員發揮了!json

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

幾點說明

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

基礎概念

ES是典型的分佈式系統。包含一到多個內存型節點。有分片,也有副本,可靠性高,自適應能力強。ES採用多機並行能力來進行擴展,是創建在一系列層級數據結構上的。這些抽象做爲一個全局的路由表,存在於每一個運行的實例上,給了ES強大的功能和擴展能力。 bash

名詞 解釋
Cluster 集羣,有着統一的名稱。包含一個master節點,和落幹其餘類型節點。通常爲對等節點
Node 節點,ES運行實例,指定集羣中的任意一臺便可加入
Index 索引,邏輯命名空間。類比DBMS中的
Shard 分片,每一個索引包含一個或多個分片,用來將數據分佈在不一樣實例上
Replica 每一個分片又分爲主副本和從副本,用來保證HA和Failover。每個replica爲具體lucene實例
Segment 段,倒排索引的子集,會不斷的合併以減小資源佔用
Document 文檔,爲具體存放的某條記錄,好比某條訂單信息
Feild 字段,文檔中的某個字段,能夠有不一樣的類型

索引類型

倒排索引

不少同窗有一個誤解,覺得ES是一個全文搜索引擎,那麼就只有倒排索引這一種索引類型,那是錯的。數據在寫入es時,會產生多份數據用於不一樣查詢方式,使用的索引結構也不相同。數據結構

ES默認是對全部字段進行索引的(也就是倒排索引),若是不須要,能夠在mapping中將index屬性設置爲no;若是字段須要精確查找,則設置爲not_analyzedapp

爲了增長倒排索引的Term查找速度,ES還專門作了Term index,它的本質是一棵Trie(前綴)樹(使用FST技術壓縮)。async

_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字段值。

默認參與Hash計算的字段是 _id,使用ES自帶的生成器能較好的平均數據,使用自定義的id可能會產生數據傾斜。

shard = hash(routing) % number_of_primary_shards
複製代碼

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

單機Shard的寫入過程

ES的寫入性能能夠很高(尤爲是批量寫入),取決於你的配置。一個文檔要寫入索引,直到讀可見,要通過一系列的緩衝和合並。咱們拿ES官方博客的一張圖來講明。

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以前,可以瞭解它的一些底層設計結構。這樣,在遇到一些瓶頸的限制之後,可以瞭解到它爲何有這樣或者那樣的反應;另外在作一些解決方案的時候,可以多給本身一點底氣。

不過話說回來,上得了廳房,下得了廚房的女人,你們仍是都喜歡~

相關文章
相關標籤/搜索