elasticsearch設計的理念就是分布式搜索引擎,底層其實還是基於lucene的,通過倒排索引的方式快速查詢。比如一本書的目錄是索引,然后快速找到每一章的的文本內容這種叫正向索引;而如果一件衣服比如有:藍色,389元,L碼這些信息,我們通過搜索這些信息就能找到這條記錄,這就叫倒排索引,實際就是通過分詞、重組、來共享前綴存儲索引。
倒排索引
比如有5條數據,左邊是id右邊是名稱,我要查詢名字包含james的記錄,就需要把所有記錄遍歷一遍才行。
如果我們把名字進行分詞,然后對這些分詞進行分組,統計它所在的文檔位置就好了。倒排索引會記錄每個分詞鎖出現的次數,以及這些分詞存在於那些doc中等等一系列信息。
es中存儲數據的基本單位是索引,比如說你現在要在es中存儲一些訂單數據,你就應該在es中創建一個索引,order_idx,所有的訂單數據就都寫到這個索引里面去,一個索引差不多就是相當於是mysql里的數據庫。index -> type -> mapping -> document -> field。
index:mysql數據庫
type:就像一張表。ES5.X中一個index可以有多個type、
ES6.X中一個type只能有一個type、
ES7.X中移除了type這個概念,此時的index就像一張表了。
mapping:定義了每個字段的類型等信息。相當於關系型數據庫中的表結構。
document:一條document就代表了mysql表里的一條記錄。
field:每個field就代表了這個document中的一個字段的值。
ES如何實現分布式的(es架構原理)
創建一個索引,這個索引可以拆分成多個shard,每個shard存儲部分數據。每個shard的數據實際是有多個備份,就是說每個shard都有一個主分片寫數據、還有一個或多個副分片同步數據(主副並不在一個服務上),es集群多個節點會自動選一個為master節點,這個節點其實就是干一些管理的工作的,比如維護索引元數據拉,負責切換 主/副節點 的身份之類的;如果一個節點的主分片掛了,副分片會切換為主分片,一個主分片及其所有副分片都掛了,集群就崩塌了。一個集群中會有以下幾個角色:
集群(cluster)
集群由一個或多個節點組成,一個集群有一個默認名稱"elasticsearch"。
節點(node)
一個Elasticsearch實例即一個Node,一台機器可以有多個實例,正常使用下每個實例應該會部署在不同的機器上。Elasticsearch 的配置文件中可以通過 node.master、node.data 來設置節點類型。node.master:表示節點是有資格競選為主節點的資格。node.data:表示節點是否存儲數據。
主節點+數據節點:即有成為主節點的資格,又存儲數據
node.master: true
node.data: tru
數據節點:節點沒有成為主節點的資格,不參與選舉,只會存儲數據
node.master: false
node.data: true
客戶端節點:不會成為主節點,也不會存儲數據,主要是針對海量請求的時候可以進⾏負載均衡
node.master: false
node.data: false
分片和副本(shard)
每個索引有一個或多個分片,每個分片存儲不同的數據。分片可分為主分片( primary shard)和副分片(replica shard),副分片是主分片的拷貝。默認每個主分片有一個副分片,一個索引的副分片的數量可以動態地調整,副分片從不與它的主分片在同一個節點上。
ES 讀/寫 數據過程
寫入文檔
1. 客戶端向任意一個節點發送新增文檔請求,比如node1(此時node1就成了協調節點)。
2. 節點對這個document進行路由,發現這個document屬於主分片P0,因為P0在node3上,所以請求會轉發到node3.
3. document在P0上新增成功后,會轉發到node1、node2上的副分片R0 R1。
4. node1發現他們都搞定之后,就會將結果響應給客戶端。
讀取文檔
1. 客戶端向任意一個節點發送讀請求,比如node2(此時node2就成了協調節點)。
2. node2對document進行路由,將請求轉發到對應的node,比如node1。
3. 此時會使用round-robin隨機輪詢算法,然后對其所有主副節點隨機選擇一個,讓讀請求負載均衡。
4. node1處理完成之后將結果給node2,node2將結果響應給客戶端。
路由算法
首先這肯定不會是隨機的,否則將來要獲取文檔的時候我們就不知道從何處尋找了。實際上,這個過程是根據下面這個公式決定的:
shard = hash(routing) % number_of_primary_shards
routing默認是文檔的id,也可以自定義,hash(routing)后得到一個數字,再對主分片個數取余,這個數字肯定是0~number_of_primary_shards-1 之間的余數,就是我們文檔所在的位置。所以我們在創建索引的時候就要確定好主分片個數,並永遠不修改,否則就找不到之前的數據了。
es大量數據(數十億級別)時,如何提高查詢效率
1. filesystem cache
往es寫入數據時,其實就是寫入到磁盤中的。而es的搜索引擎底層是嚴重依賴於文件系統緩存的,es所有的indx segment file索引數據文件就存在這里面,如果filesystem cache的內存夠大,那么你搜索的時候基本就是走內存的,性能會很高。打個比方說:
es節點有3台機器,每台機器,看起來內存很多,64G,總內存,64 * 3 = 192g;每台機器給es jvm heap是32G,那么剩下來留給filesystem cache的就是每台機器才32g,總共集群里給filesystem cache的就是32 * 3 = 96g內存。如果有1T的數據量,那就只有10%的索引文件被存到file cache里面去,效率肯定是比較低的,就是你的機器的內存,至少可以容納你的總數據量的一半。
2. 數據預熱/冷熱分離
預熱:比如file cache有50g內存,而數據超過了100g怎么辦呢?我們可以做一個后台系統定時去拉取一些容易被人訪問的數據。因為我們查詢es時,它會重新記錄這個索引的信息、進行排序將這些數據放到file cache中。
冷熱分離:大量不搜索的字段拆到別的index里面去,就有點像mysql的垂直拆分,這樣可以確保熱數據在被預熱之后,盡量都讓他們留在filesystem os cache里,別讓冷數據給沖刷掉。熱數據可能就占總數據量的10%,此時數據量很少,幾乎全都保留在filesystem cache里面了,就可以確保熱數據的訪問性能是很高的。
3. document模型設計
es里面的復雜的關聯查詢,復雜的查詢語法,盡量別用,一旦用了性能一般都不太好。有關聯的最好寫入es的時候,搞成兩個索引,order索引,orderItem索引,order索引里面就包含id order_code total_price;orderItem索引直接包含id order_code total_price id order_id goods_id purchase_count price,這樣就不需要es的語法來完成join了。
4. 分頁性能優化
es的分頁是較坑的,假如你每頁是10條數據,你現在要查詢第100頁,實際上是會把每個shard上存儲的前1000條數據都查到一個協調節點上,如果你有個5個shard,那么就有5000條數據,接着協調節點對這5000條數據進行一些合並、處理,再獲取到最終第100頁的10條數據。翻頁的時候,翻的越深,每個shard返回的數據就越多,而且協調節點處理的時間越長。非常坑爹。所以用es做分頁的時候,你會發現越翻到后面,就越是慢。有2個折中的優化方案:
1 不允許分頁,直接把數據給客戶端(這么說估計會被打)。
2 允許一頁頁的翻但是不允許選擇頁數,就像app的推薦商品一樣。用scroll api一頁頁去刷,scroll的原理實際上是保留一個數據快照,然后在一定時間內,你如果不斷的滑動往后翻頁的時候,類似於你現在在瀏覽微博,不斷往下刷新翻頁。那么就用scroll不斷通過游標獲取下一頁數據,這個性能是很高的,比es實際翻頁要好的多的多。