1、前言api
上一篇說了這篇要講解Search機制,可是在這個以前咱們要明白下文件是怎麼存儲的,咱們先來說文件的存儲而後再來探究機制;緩存
2、文檔存儲
併發
以前說過文檔是存儲在分片上的,這裏要思考一個問題:文檔是經過什麼方式去分配到分片上的?咱們來思考以下幾種方式:async
1.經過文檔與分片取模實現,這樣作的好處在於能夠將文檔平均分配到因此的分片上;性能
2.隨機分配固然也能夠,這種可能形成分配不均,照成空間浪費;日誌
3.輪詢這種是最不可取的,採用這種你須要創建文檔與分片的映射關係,這樣會致使成本太大;blog
通過一輪強烈的思考,咱們選擇方案1,沒錯你想對了,這裏Elasticsearch也和咱們思考的是同樣的,咱們來揭露下他分配的公式:排序
shard_num = hash(_routing)%num_primary_shards索引
routing是一個關鍵參數,默認是文檔id,能夠自行指定;隊列
number_of_primary_shard 主分片數;
以前咱們介紹過節點的類型,接下來咱們來介紹下,當文檔建立時候的流程:
假設的狀況:1個主節點和2個數據節點,1個副本:
文檔的建立:
1.客戶端向節點發起建立文檔的請求;
2.經過routing計算文檔存儲的分片,查詢集羣確認分片在數據節點1上,而後轉發文檔到數據節點1;
3.數據節點1接收到請求建立文檔,同時發送請求到該住分片的副本;
4.主分片的副本接收到請求則開始建立文檔;
5.副本分片文檔完成之後發送成功的通知給主分片;
6.當主分片接收到建立完成的信息之後,發送給節點建立成功的通知;
7.節點返回結果給客戶端;
3、Search機制
搜索的類型其實有2種:Query Then Ferch和DFS Query Then Ferch,當咱們明白如何存儲文件的是時候,再去理解這兩種查詢方式會很簡單;
Query Then Ferch
Search在執行的時候分爲兩個步驟運做Query(查詢)和Fetch(獲取),基本流程以下:
1.將查詢分配到每一個分片;
2.找到全部匹配的文檔,並在當前的分片的文檔使用Term/Document Frequency信息進行打分;
3.構建結果的優先級隊列(排序,分頁等);
4.將結果的查詢到的數據返回給請求節點。請注意,實際文檔還沒有發送,只是分數;
5.將全部分片的分數在請求節點上合併和排序,根據查詢條件選擇文檔;
6.最後從篩選出的文檔所在的各個分片中檢索實際的文檔;
7.結果將返回給客戶端;
DFS Query Then Ferch
DFS是在進行真正的查詢以前, 先把各個分片的詞頻率和文檔頻率收集一下, 而後進行詞搜索的時候, 各分片依據全局的詞頻率和文檔頻率進行搜索和排名,基本流程以下:
1.提早查詢每一個shard,詢問Term和Document frequency;
2.將查詢分配到每一個分片;
3.找到全部匹配到的文檔,而且在全部分片的文檔使用Term/Document Frequency信息進行打分;
4.構建結果的優先級隊列(排序,分頁等);
5.將結果的查詢到的數據返回給請求節點。請注意,實際文檔還沒有發送,只是分數;
6.來自全部分片的分數合併起來,並在請求節點上進行排序,文檔被按照查詢要求進行選擇;
7.最終,實際文檔從他們各自所在的獨立的分片上檢索出來;
8.結果被返回給客戶端;
總結下,根據上面的兩種查詢方式來看的DFS查詢得分明顯會更接近真實的得分,可是DFS 在拿到全部文檔後再重新完整的計算一次相關性算分,耗費更多的cpu和內存,執行性能也比較低下,通常不建議使用。使用方式以下:
另外要是當文檔數據很少的時候能夠考慮使用一個分片;
這個地方咱們在作一些深刻的思考:咱們搜索的時候是經過倒排索引,可是倒排索引一旦創建是不能修改的,這樣作有那些好處壞處?
好處:
1.不用考慮併發寫入文件的問題,杜絕鎖機制帶來的性能問題;
2.文件再也不更改,能夠充分利用文件系統緩存,只需載入一次,只要內容足夠,對該文件的讀取都會從內存中讀取,性能高;
壞處:
寫入新文檔時,必須從新構建倒排索引文件,而後替換老文件後,新文檔才能被檢索,致使文檔實時性差;
問題:
對於新插入文檔的時候寫入倒排索引,致使倒排索引重建的問題,假如根據新文檔來從新構建倒排索引文件,而後生成一個新的倒排索引文件,再把用戶的原查詢切掉,切換到新的倒排索引。這樣開銷會很大,會致使實時性很是差。
Elasticsearch如何解決文件搜索的實時性問題:
新文檔直接生成新的倒排索引文件,查詢的時候同時查詢全部的倒排文件,而後作結果的彙總計算便可,ES是是經過Lucene構建的,Lucene就是經過這種方案處理的,咱們介紹下Luncene構建文檔的時候的結構:
1.Luncene中單個倒排索引稱爲Segment,合在一塊兒稱爲Index,這個Index與Elasticsearch概念是不相同的,Elasticsearch中的每一個Shard對應一個Lucene Index;
2.Lucene會有個專門文件來記錄全部的Segment信息,稱爲Commit Point,用來維護Segment的信息;
Segment寫入磁盤的時候過程當中也是比較慢的,Elasticsearch提供一個Refresh機制,是經過文件緩存的機制實現的,接下咱們介紹這個過程:
1.在refresh以前的文檔會先存儲在一個Buffer裏面,refresh時將Buffer中的全部文檔清空,並生成Segment;
2.Elasticsearch默認每1秒執行一次refresh,所以文檔的實時性被提升到1秒,這也是Elasticsearch稱爲近實時(Near Real Time)的緣由;
Segment寫入磁盤前若是發生了宕機,那麼其中的文檔就沒法恢復了,怎麼處理這個問題?
Elasticsearch引入translog(事務日誌)機制,translog的寫入也能夠設置,默認是request,每次請求都會寫入磁盤(fsync),這樣就保證全部數據不會丟,但寫入性能會受影響;若是改爲async,則按照配置觸發trangslog寫入磁盤,注意這裏說的只是trangslog自己的寫盤。Elasticsearch啓動時會檢查translog文件,並從中恢復數據;
還有一種機制flush機制,負責將內存中的segment寫入磁盤和刪除舊的translog文件;
新增的時候搞定了,那刪除和更新怎麼辦?
刪除的時候,Lucene專門維護一個.del文件,記錄全部已經刪除的文檔,del文件上記錄的是文檔在Lucene內部的id,查詢結果返回前過濾掉.del中的全部文檔;
更新就簡單了,先刪除而後再建立;
隨着Segment增長,查詢一次會涉及不少,查詢速度會變慢,Elasticsearch會定時在後臺進行Segment Merge的操做,減小Segment的數量,經過force_merge api能夠手動強制作Segment Merge的操做;
另外能夠參考下這篇文段合併;
4、下節預告
原本還想說下聚合、排序方面的查詢,可是看篇幅也差很少了,留點時間出去浪,最近不光要撒狗糧,也要撒知識,國慶節期間準備完成Elastic Stack系列,下面接下來還會有4-5篇左右,歡迎你們加羣438836709,歡迎你們關注我公衆號!