elasticsearch入門概念講解(複習面試大綱)

ES理解:
es中存儲數據的基本單位是索引,比如說你現在要在es中存儲一些訂單數據,你就應該在es中創建一個索引,order_idx,所有的訂單數據就都寫到這個索引裏面去,一個索引差不多就是相當於是mysql裏的一張表。index -> type -> mapping -> document -> field。

index:mysql裏的一張表

type:沒法跟mysql裏去對比,一個index裏可以有多個type,每個type的字段都是差不多的,但是有一些略微的差別。

好比說,有一個index,是訂單index,裏面專門是放訂單數據的。就好比說你在mysql中建表,有些訂單是實物商品的訂單,就好比說一件衣服,一雙鞋子;有些訂單是虛擬商品的訂單,就好比說遊戲點卡,話費充值。就兩種訂單大部分字段是一樣的,但是少部分字段可能有略微的一些差別。

所以就會在訂單index裏,建兩個type,一個是實物商品訂單type,一個是虛擬商品訂單type,這兩個type大部分字段是一樣的,少部分字段是不一樣的。

很多情況下,一個index裏可能就一個type,但是確實如果說是一個index裏有多個type的情況,你可以認爲index是一個類別的表,具體的每個type代表了具體的一個mysql中的表

每個type有一個mapping,如果你認爲一個type是一個具體的一個表,index代表了多個type的同屬於的一個類型,mapping就是這個type的表結構定義,你在mysql中創建一個表,肯定是要定義表結構的,裏面有哪些字段,每個字段是什麼類型。。。

mapping就代表了這個type的表結構的定義,定義了這個type中每個字段名稱,字段是什麼類型的,然後還有這個字段的各種配置

實際上你往index裏的一個type裏面寫的一條數據,叫做一條document,一條document就代表了mysql中某個表裏的一行給,每個document有多個field,每個field就代表了這個document中的一個字段的值

接着你搞一個索引,這個索引可以拆分成多個shard,每個shard存儲部分數據。

接着就是這個shard的數據實際是有多個備份,就是說每個shard都有一個primary shard,負責寫入數據,但是還有幾個replica shard。primary shard寫入數據之後,會將數據同步到其他幾個replica shard上去。

通過這個replica的方案,每個shard的數據都有多個備份,如果某個機器宕機了,沒關係啊,還有別的數據副本在別的機器上呢。高可用了吧。

es集羣多個節點,會自動選舉一個節點爲master節點,這個master節點其實就是幹一些管理的工作的,比如維護索引元數據拉,負責切換primary shard和replica shard身份拉,之類的。

要是master節點宕機了,那麼會重新選舉一個節點爲master節點。

如果是非master節點宕機了,那麼會由master節點,讓那個宕機節點上的primary shard的身份轉移到其他機器上的replica shard。急着你要是修復了那個宕機機器,重啓了之後,master節點會控制將缺失的replica shard分配過去,同步後續修改的數據之類的,讓集羣恢復正常。

(1)es寫數據過程

1)客戶端選擇一個node發送請求過去,這個node就是coordinating node(協調節點)
2)coordinating node,對document進行路由,將請求轉發給對應的node(有primary shard)
3)實際的node上的primary shard處理請求,然後將數據同步到replica node
4)coordinating node,如果發現primary node和所有replica node都搞定之後,就返回響應結果給客戶端

(2)es讀數據過程

查詢,GET某一條數據,寫入了某個document,這個document會自動給你分配一個全局唯一的id,doc id,同時也是根據doc id進行hash路由到對應的primary shard上面去。也可以手動指定doc id,比如用訂單id,用戶id。

你可以通過doc id來查詢,會根據doc id進行hash,判斷出來當時把doc id分配到了哪個shard上面去,從那個shard去查詢

1)客戶端發送請求到任意一個node,成爲coordinate node
2)coordinate node對document進行路由,將請求轉發到對應的node,此時會使用round-robin隨機輪詢算法,在primary shard以及其所有replica中隨機選擇一個,讓讀請求負載均衡
3)接收請求的node返回document給coordinate node
4)coordinate node返回document給客戶端

(3)es搜索數據過程

es最強大的是做全文檢索,就是比如你有三條數據

java真好玩兒啊
java好難學啊
j2ee特別牛

你根據java關鍵詞來搜索,將包含java的document給搜索出來

es就會給你返回:java真好玩兒啊,java好難學啊

1)客戶端發送請求到一個coordinate node
2)協調節點將搜索請求轉發到所有的shard對應的primary shard或replica shard也可以
3)query phase:每個shard將自己的搜索結果(其實就是一些doc id),返回給協調節點,由協調節點進行數據的合併、排序、分頁等操作,產出最終結果
4)fetch phase:接着由協調節點,根據doc id去各個節點上拉取實際的document數據,最終返回給客戶端

(4)搜索的底層原理,倒排索引,畫圖說明傳統數據庫和倒排索引的區別

(5)寫數據底層原理

1)先寫入buffer,在buffer裏的時候數據是搜索不到的;同時將數據寫入translog日誌文件

2)如果buffer快滿了,或者到一定時間,就會將buffer數據refresh到一個新的segment file中,但是此時數據不是直接進入segment file的磁盤文件的,而是先進入os cache的。這個過程就是refresh。

每隔1秒鐘,es將buffer中的數據寫入一個新的segment file,每秒鐘會產生一個新的磁盤文件,segment file,這個segment file中就存儲最近1秒內buffer中寫入的數據

但是如果buffer裏面此時沒有數據,那當然不會執行refresh操作咯,每秒創建換一個空的segment file,如果buffer裏面有數據,默認1秒鐘執行一次refresh操作,刷入一個新的segment file中

操作系統裏面,磁盤文件其實都有一個東西,叫做os cache,操作系統緩存,就是說數據寫入磁盤文件之前,會先進入os cache,先進入操作系統級別的一個內存緩存中去

只要buffer中的數據被refresh操作,刷入os cache中,就代表這個數據就可以被搜索到了

爲什麼叫es是準實時的?NRT,near real-time,準實時。默認是每隔1秒refresh一次的,所以es是準實時的,因爲寫入的數據1秒之後才能被看到。

可以通過es的restful api或者java api,手動執行一次refresh操作,就是手動將buffer中的數據刷入os cache中,讓數據立馬就可以被搜索到。

只要數據被輸入os cache中,buffer就會被清空了,因爲不需要保留buffer了,數據在translog裏面已經持久化到磁盤去一份了

3)只要數據進入os cache,此時就可以讓這個segment file的數據對外提供搜索了

4)重複1~3步驟,新的數據不斷進入buffer和translog,不斷將buffer數據寫入一個又一個新的segment file中去,每次refresh完buffer清空,translog保留。隨着這個過程推進,translog會變得越來越大。當translog達到一定長度的時候,就會觸發commit操作。

buffer中的數據,倒是好,每隔1秒就被刷到os cache中去,然後這個buffer就被清空了。所以說這個buffer的數據始終是可以保持住不會填滿es進程的內存的。

每次一條數據寫入buffer,同時會寫入一條日誌到translog日誌文件中去,所以這個translog日誌文件是不斷變大的,當translog日誌文件大到一定程度的時候,就會執行commit操作。

5)commit操作發生第一步,就是將buffer中現有數據refresh到os cache中去,清空buffer

6)將一個commit point寫入磁盤文件,裏面標識着這個commit point對應的所有segment file

7)強行將os cache中目前所有的數據都fsync到磁盤文件中去

translog日誌文件的作用是什麼?就是在你執行commit操作之前,數據要麼是停留在buffer中,要麼是停留在os cache中,無論是buffer還是os cache都是內存,一旦這臺機器死了,內存中的數據就全丟了。

所以需要將數據對應的操作寫入一個專門的日誌文件,translog日誌文件中,一旦此時機器宕機,再次重啓的時候,es會自動讀取translog日誌文件中的數據,恢復到內存buffer和os cache中去。

commit操作:1、寫commit point;2、將os cache數據fsync強刷到磁盤上去;3、清空translog日誌文件

8)將現有的translog清空,然後再次重啓啓用一個translog,此時commit操作完成。默認每隔30分鐘會自動執行一次commit,但是如果translog過大,也會觸發commit。整個commit的過程,叫做flush操作。我們可以手動執行flush操作,就是將所有os cache數據刷到磁盤文件中去。

不叫做commit操作,flush操作。es中的flush操作,就對應着commit的全過程。我們也可以通過es api,手動執行flush操作,手動將os cache中的數據fsync強刷到磁盤上去,記錄一個commit point,清空translog日誌文件。

9)translog其實也是先寫入os cache的,默認每隔5秒刷一次到磁盤中去,所以默認情況下,可能有5秒的數據會僅僅停留在buffer或者translog文件的os cache中,如果此時機器掛了,會丟失5秒鐘的數據。但是這樣性能比較好,最多丟5秒的數據。也可以將translog設置成每次寫操作必須是直接fsync到磁盤,但是性能會差很多。

實際上你在這裏,如果面試官沒有問你es丟數據的問題,你可以在這裏給面試官炫一把,你說,其實es第一是準實時的,數據寫入1秒後可以搜索到;可能會丟失數據的,你的數據有5秒的數據,停留在buffer、translog os cache、segment file os cache中,有5秒的數據不在磁盤上,此時如果宕機,會導致5秒的數據丟失。

如果你希望一定不能丟失數據的話,你可以設置個參數,官方文檔,百度一下。每次寫入一條數據,都是寫入buffer,同時寫入磁盤上的translog,但是這會導致寫性能、寫入吞吐量會下降一個數量級。本來一秒鐘可以寫2000條,現在你一秒鐘只能寫200條,都有可能。

10)如果是刪除操作,commit的時候會生成一個.del文件,裏面將某個doc標識爲deleted狀態,那麼搜索的時候根據.del文件就知道這個doc被刪除了

11)如果是更新操作,就是將原來的doc標識爲deleted狀態,然後新寫入一條數據

12)buffer每次refresh一次,就會產生一個segment file,所以默認情況下是1秒鐘一個segment file,segment file會越來越多,此時會定期執行merge

13)每次merge的時候,會將多個segment file合併成一個,同時這裏會將標識爲deleted的doc給物理刪除掉,然後將新的segment file寫入磁盤,這裏會寫一個commit point,標識所有新的segment file,然後打開segment file供搜索使用,同時刪除舊的segment file。

es裏的寫流程,有4個底層的核心概念,refresh、flush、translog、merge

當segment file多到一定程度的時候,es就會自動觸發merge操作,將多個segment file給merge成一個segment file。

ES性能分析
(1)性能優化的殺手鐗——filesystem cache
(推薦文章:https://blog.csdn.net/youzhouliu/article/details/80461226)
os cache,操作系統的緩存

你往es裏寫的數據,實際上都寫到磁盤文件裏去了,磁盤文件裏的數據操作系統會自動將裏面的數據緩存到os cache裏面去

es的搜索引擎嚴重依賴於底層的filesystem cache,你如果給filesystem cache更多的內存,儘量讓內存可以容納所有的indx segment file索引數據文件,那麼你搜索的時候就基本都是走內存的,性能會非常高。

性能差距可以有大,我們之前很多的測試和壓測,如果走磁盤一般肯定上秒,搜索性能絕對是秒級別的,1秒,5秒,10秒。但是如果是走filesystem cache,是走純內存的,那麼一般來說性能比走磁盤要高一個數量級,基本上就是毫秒級的,從幾毫秒到幾百毫秒不等。

案例:
比如說,你,es節點有3臺機器,每臺機器,看起來內存很多,64G,總內存,64 * 3 = 192g

每臺機器給es jvm heap是32G,那麼剩下來留給filesystem cache的就是每臺機器才32g,總共集羣裏給filesystem cache的就是32 * 3 = 96g內存

我就問他,ok,那麼就是你往es集羣裏寫入的數據有多少數據量?

如果你此時,你整個,磁盤上索引數據文件,在3臺機器上,一共佔用了1T的磁盤容量,你的es數據量是1t,每臺機器的數據量是300g

你覺得你的性能能好嗎?filesystem cache的內存才100g,十分之一的數據可以放內存,其他的都在磁盤,然後你執行搜索操作,大部分操作都是走磁盤,性能肯定差

當時他們的情況就是這樣子,es在測試,弄了3臺機器,自己覺得還不錯,64G內存的物理機。自以爲可以容納1T的數據量。

歸根結底,你要讓es性能要好,最佳的情況下,就是你的機器的內存,至少可以容納你的總數據量的一半

比如說,你一共要在es中存儲1T的數據,那麼你的多臺機器留個filesystem cache的內存加起來綜合,至少要到512G,至少半數的情況下,搜索是走內存的,性能一般可以到幾秒鐘,2秒,3秒,5秒

如果最佳的情況下,我們自己的生產環境實踐經驗,所以說我們當時的策略,是僅僅在es中就存少量的數據,就是你要用來搜索的那些索引,內存留給filesystem cache的,就100G,那麼你就控制在100gb以內,相當於是,你的數據幾乎全部走內存來搜索,性能非常之高,一般可以在1秒以內

比如說你現在有一行數據

id name age …30個字段

但是你現在搜索,只需要根據id name age三個字段來搜索

如果你傻乎乎的往es裏寫入一行數據所有的字段,就會導致說70%的數據是不用來搜索的,結果硬是佔據了es機器上的filesystem cache的空間,單挑數據的數據量越大,就會導致filesystem cahce能緩存的數據就越少

僅僅只是寫入es中要用來檢索的少數幾個字段就可以了,比如說,就寫入es id name age三個字段就可以了,然後你可以把其他的字段數據存在mysql裏面,我們一般是建議用es + hbase的這麼一個架構。

hbase的特點是適用於海量數據的在線存儲,就是對hbase可以寫入海量數據,不要做複雜的搜索,就是做很簡單的一些根據id或者範圍進行查詢的這麼一個操作就可以了

從es中根據name和age去搜索,拿到的結果可能就20個doc id,然後根據doc id到hbase裏去查詢每個doc id對應的完整的數據,給查出來,再返回給前端。

你最好是寫入es的數據小於等於,或者是略微大於es的filesystem cache的內存容量

然後你從es檢索可能就花費20ms,然後再根據es返回的id去hbase裏查詢,查20條數據,可能也就耗費個30ms,可能你原來那麼玩兒,1T數據都放es,會每次查詢都是5~10秒,現在可能性能就會很高,每次查詢就是50ms。

elastcisearch減少數據量僅僅放要用於搜索的幾個關鍵字段即可,儘量寫入es的數據量跟es機器的filesystem cache是差不多的就可以了;其他不用來檢索的數據放hbase裏,或者mysql。

所以之前有些學員也是問,我也是跟他們說,儘量在es裏,就存儲必須用來搜索的數據,比如說你現在有一份數據,有100個字段,其實用來搜索的只有10個字段,建議是將10個字段的數據,存入es,剩下90個字段的數據,可以放mysql,hadoop hbase,都可以

這樣的話,es數據量很少,10個字段的數據,都可以放內存,就用來搜索,搜索出來一些id,通過id去mysql,hbase裏面去查詢明細的數據
(2)數據預熱

假如說,哪怕是你就按照上述的方案去做了,es集羣中每個機器寫入的數據量還是超過了filesystem cache一倍,比如說你寫入一臺機器60g數據,結果filesystem cache就30g,還是有30g數據留在了磁盤上。

舉個例子,就比如說,微博,你可以把一些大v,平時看的人很多的數據給提前你自己後臺搞個系統,每隔一會兒,你自己的後臺系統去搜索一下熱數據,刷到filesystem cache裏去,後面用戶實際上來看這個熱數據的時候,他們就是直接從內存裏搜索了,很快。

電商,你可以將平時查看最多的一些商品,比如說iphone 8,熱數據提前後臺搞個程序,每隔1分鐘自己主動訪問一次,刷到filesystem cache裏去。

對於那些你覺得比較熱的,經常會有人訪問的數據,最好做一個專門的緩存預熱子系統,就是對熱數據,每隔一段時間,你就提前訪問一下,讓數據進入filesystem cache裏面去。這樣期待下次別人訪問的時候,一定性能會好一些。

(3)冷熱分離

關於es性能優化,數據拆分,我之前說將大量不搜索的字段,拆分到別的存儲中去,這個就是類似於後面我最後要講的mysql分庫分表的垂直拆分。

es可以做類似於mysql的水平拆分,就是說將大量的訪問很少,頻率很低的數據,單獨寫一個索引,然後將訪問很頻繁的熱數據單獨寫一個索引

你最好是將冷數據寫入一個索引中,然後熱數據寫入另外一個索引中,這樣可以確保熱數據在被預熱之後,儘量都讓他們留在filesystem os cache裏,別讓冷數據給沖刷掉。

你看,假設你有6臺機器,2個索引,一個放冷數據,一個放熱數據,每個索引3個shard

3臺機器放熱數據index;另外3臺機器放冷數據index

然後這樣的話,你大量的時候是在訪問熱數據index,熱數據可能就佔總數據量的10%,此時數據量很少,幾乎全都保留在filesystem cache裏面了,就可以確保熱數據的訪問性能是很高的。

但是對於冷數據而言,是在別的index裏的,跟熱數據index都不再相同的機器上,大家互相之間都沒什麼聯繫了。如果有人訪問冷數據,可能大量數據是在磁盤上的,此時性能差點,就10%的人去訪問冷數據;90%的人在訪問熱數據。

(4)document模型設計

有不少同學問我,mysql,有兩張表

訂單表:id order_code total_price

1 測試訂單 5000

訂單條目表:id order_id goods_id purchase_count price

1 1 1 2 2000
2 1 2 5 200

我在mysql裏,都是select * from order join order_item on order.id=order_item.order_id where order.id=1

1 測試訂單 5000 1 1 1 2 2000
1 測試訂單 5000 2 1 2 5 200

在es裏該怎麼玩兒,es裏面的複雜的關聯查詢,複雜的查詢語法,儘量別用,一旦用了性能一般都不太好

設計es裏的數據模型

寫入es的時候,搞成兩個索引,order索引,orderItem索引

order索引,裏面就包含id order_code total_price
orderItem索引,裏面寫入進去的時候,就完成join操作,id order_code total_price id order_id goods_id purchase_count price

寫入es的java系統裏,就完成關聯,將關聯好的數據直接寫入es中,搜索的時候,就不需要利用es的搜索語法去完成join來搜索了

document模型設計是非常重要的,很多操作,不要在搜索的時候纔想去執行各種複雜的亂七八糟的操作。es能支持的操作就是那麼多,不要考慮用es做一些它不好操作的事情。如果真的有那種操作,儘量在document模型設計的時候,寫入的時候就完成。另外對於一些太複雜的操作,比如join,nested,parent-child搜索都要儘量避免,性能都很差的。

很多同學在問我,很多複雜的亂七八糟的一些操作,如何執行

兩個思路,在搜索/查詢的時候,要執行一些業務強相關的特別複雜的操作:

1)在寫入數據的時候,就設計好模型,加幾個字段,把處理好的數據寫入加的字段裏面
2)自己用java程序封裝,es能做的,用es來做,搜索出來的數據,在java程序裏面去做,比如說我們,基於es,用java封裝一些特別複雜的操作

(5)分頁性能優化

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

分佈式的,你要查第100頁的10條數據,你是不可能說從5個shard,每個shard就查2條數據?最後到協調節點合併成10條數據?你必須得從每個shard都查1000條數據過來,然後根據你的需求進行排序、篩選等等操作,最後再次分頁,拿到裏面第100頁的數據。

你翻頁的時候,翻的越深,每個shard返回的數據就越多,而且協調節點處理的時間越長。非常坑爹。所以用es做分頁的時候,你會發現越翻到後面,就越是慢。

我們之前也是遇到過這個問題,用es作分頁,前幾頁就幾十毫秒,翻到10頁之後,幾十頁的時候,基本上就要5~10秒才能查出來一頁數據了

1)不允許深度分頁/默認深度分頁性能很慘

你係統不允許他翻那麼深的頁,pm,默認翻的越深,性能就越差

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

類似於微博中,下拉刷微博,刷出來一頁一頁的,你可以用scroll api,自己百度

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

針對這個問題,你可以考慮用scroll來進行處理,scroll的原理實際上是保留一個數據快照,然後在一定時間內,你如果不斷的滑動往後翻頁的時候,類似於你現在在瀏覽微博,不斷往下刷新翻頁。那麼就用scroll不斷通過遊標獲取下一頁數據,這個性能是很高的,比es實際翻頁要好的多的多。

但是唯一的一點就是,這個適合於那種類似微博下拉翻頁的,不能隨意跳到任何一頁的場景。同時這個scroll是要保留一段時間內的數據快照的,你需要確保用戶不會持續不斷翻頁翻幾個小時。

無論翻多少頁,性能基本上都是毫秒級的

因爲scroll api是隻能一頁一頁往後翻的,是不能說,先進入第10頁,然後去120頁,回到58頁,不能隨意亂跳頁。所以現在很多產品,都是不允許你隨意翻頁的,app,也有一些網站,做的就是你只能往下拉,一頁一頁的翻

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