強烈建議先讀一下本公衆號《也淺談下分佈式存儲要點》,對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_analyzed
。app
爲了增長倒排索引的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
複製代碼
剩下的,就是單機索引的事了。
ES的寫入性能能夠很高(尤爲是批量寫入),取決於你的配置。一個文檔要寫入索引,直到讀可見,要通過一系列的緩衝和合並。咱們拿ES官方博客的一張圖來講明。
ES的底層存儲是Lucene,包含一系列的反向索引。這樣的一批索引的信息就是上面提到的段(segment)。但記錄不會直接寫入段,而是先寫入一個緩衝區。
當緩衝區滿了,或者在緩衝區呆的夠久,達到了刷新時間(劃重點),會一次性將緩衝區的內容寫進段中。這也是爲何refresh_interval
屬性的配置會嚴重的影響性能。若是你不要很高的實時性,不妨將其配置的大一點。
緩衝區默認使用堆空間的10%,最小值爲48mb(針對於分片的)。若是你的索引多且寫入重,這部份內存的佔用是可觀的,能夠適當加大。
問題是Segment是不可變的,刪除、更新操做,並不能在原來的段上進行。ES對待全部的操做都是類似的,並不區別對待,刪除和更新,有着和寫入同樣的mege過程。這會生成大量的段,每一個段會佔用一個文件句柄,會浪費大量資源。ES有專門的進程負責段的自動合併,咱們不須要手動干涉。
段的合併會浪費大量的I/O和CPU資源,有tiered
(默認)、log_byte_size
、 log_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中的某些功能
客戶端節點 不保存數據也不協調集羣,僅響應用戶請求,將其發送到其餘節點
ES經過冗餘多份數據達到不一樣的用途,開箱即用。開箱即用的意思也就是認識成本低,學習成本大,先把你吸引進門再說。
ES不是一匹好馴服的野馬。一般,它並不像官方和其餘PPT宣傳的那樣無所不能,你可能常常在OOM和分片移動中度日。
但願在使用ES以前,可以瞭解它的一些底層設計結構。這樣,在遇到一些瓶頸的限制之後,可以瞭解到它爲何有這樣或者那樣的反應;另外在作一些解決方案的時候,可以多給本身一點底氣。
不過話說回來,上得了廳房,下得了廚房的女人,你們仍是都喜歡~