ElasticSearch寫入資料的工作原理是什麼?

李紅歐巴發表於2019-03-26

面試題

es 寫入資料的工作原理是什麼啊?es 查詢資料的工作原理是什麼啊?底層的 lucene 介紹一下唄?倒排索引瞭解嗎?

面試官心理分析

問這個,其實面試官就是要看看你瞭解不瞭解 es 的一些基本原理,因為用 es 無非就是寫入資料,搜尋資料。你要是不明白你發起一個寫入和搜尋請求的時候,es 在幹什麼,那你真的是......

對 es 基本就是個黑盒,你還能幹啥?你唯一能幹的就是用 es 的 api 讀寫資料了。要是出點什麼問題,你啥都不知道,那還能指望你什麼呢?

面試題剖析

es 寫資料過程

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


ElasticSearch寫入資料的工作原理是什麼?


es 讀資料過程

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

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

es 搜尋資料過程

es 最強大的是做全文檢索,就是比如你有三條資料:

java真好玩兒啊
java好難學啊
j2ee特別牛複製程式碼

你根據 java 關鍵詞來搜尋,將包含 javadocument 給搜尋出來。es 就會給你返回:java真好玩兒啊,java好難學啊。

  • 客戶端傳送請求到一個 coordinate node
  • 協調節點將搜尋請求轉發到所有的 shard 對應的 primary shardreplica shard,都可以。
  • query phase:每個 shard 將自己的搜尋結果(其實就是一些 doc id)返回給協調節點,由協調節點進行資料的合併、排序、分頁等操作,產出最終結果。
  • fetch phase:接著由協調節點根據 doc id 去各個節點上拉取實際document 資料,最終返回給客戶端。
寫請求是寫入 primary shard,然後同步給所有的 replica shard;讀請求可以從 primary shard 或 replica shard 讀取,採用的是隨機輪詢演算法。

寫資料底層原理


ElasticSearch寫入資料的工作原理是什麼?


先寫入記憶體 buffer,在 buffer 裡的時候資料是搜尋不到的;同時將資料寫入 translog 日誌檔案。

如果 buffer 快滿了,或者到一定時間,就會將記憶體 buffer 資料 refresh 到一個新的 segment file 中,但是此時資料不是直接進入 segment file 磁碟檔案,而是先進入 os cache 。這個過程就是 refresh

每隔 1 秒鐘,es 將 buffer 中的資料寫入一個新的 segment file,每秒鐘會產生一個新的磁碟檔案 segment file,這個 segment file 中就儲存最近 1 秒內 buffer 中寫入的資料。

但是如果 buffer 裡面此時沒有資料,那當然不會執行 refresh 操作,如果 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 裡面已經持久化到磁碟去一份了。

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

commit 操作發生第一步,就是將 buffer 中現有資料 refreshos cache 中去,清空 buffer。然後,將一個 commit point寫入磁碟檔案,裡面標識著這個 commit point 對應的所有 segment file,同時強行將 os cache 中目前所有的資料都 fsync 到磁碟檔案中去。最後清空 現有 translog 日誌檔案,重啟一個 translog,此時 commit 操作完成。

這個 commit 操作叫做 flush。預設 30 分鐘自動執行一次 flush,但如果 translog 過大,也會觸發 flush。flush 操作就對應著 commit 的全過程,我們可以通過 es api,手動執行 flush 操作,手動將 os cache 中的資料 fsync 強刷到磁碟上去。

translog 日誌檔案的作用是什麼?你執行 commit 操作之前,資料要麼是停留在 buffer 中,要麼是停留在 os cache 中,無論是 buffer 還是 os cache 都是記憶體,一旦這臺機器死了,記憶體中的資料就全丟了。所以需要將資料對應的操作寫入一個專門的日誌檔案 translog 中,一旦此時機器當機,再次重啟的時候,es 會自動讀取 translog 日誌檔案中的資料,恢復到記憶體 buffer 和 os cache 中去。

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 秒的資料丟失

總結一下,資料先寫入記憶體 buffer,然後每隔 1s,將資料 refresh 到 os cache,到了 os cache 資料就能被搜尋到(所以我們才說 es 從寫入到能被搜尋到,中間有 1s 的延遲)。每隔 5s,將資料寫入 translog 檔案(這樣如果機器當機,記憶體資料全沒,最多會有 5s 的資料丟失),translog 大到一定程度,或者預設每隔 30mins,會觸發 commit 操作,將緩衝區的資料都 flush 到 segment file 磁碟檔案中。

資料寫入 segment file 之後,同時就建立好了倒排索引。

刪除/更新資料底層原理

如果是刪除操作,commit 的時候會生成一個 .del 檔案,裡面將某個 doc 標識為 deleted 狀態,那麼搜尋的時候根據 .del 檔案就知道這個 doc 是否被刪除了。

如果是更新操作,就是將原來的 doc 標識為 deleted 狀態,然後新寫入一條資料。

buffer 每 refresh 一次,就會產生一個 segment file,所以預設情況下是 1 秒鐘一個 segment file,這樣下來 segment file 會越來越多,此時會定期執行 merge。每次 merge 的時候,會將多個 segment file 合併成一個,同時這裡會將標識為 deleted 的 doc 給物理刪除掉,然後將新的 segment file 寫入磁碟,這裡會寫一個 commit point,標識所有新的 segment file,然後開啟 segment file 供搜尋使用,同時刪除舊的 segment file

底層 lucene

簡單來說,lucene 就是一個 jar 包,裡面包含了封裝好的各種建立倒排索引的演算法程式碼。我們用 Java 開發的時候,引入 lucene jar,然後基於 lucene 的 api 去開發就可以了。

通過 lucene,我們可以將已有的資料建立索引,lucene 會在本地磁碟上面,給我們組織索引的資料結構。

倒排索引

在搜尋引擎中,每個文件都有一個對應的文件 ID,文件內容被表示為一系列關鍵詞的集合。例如,文件 1 經過分詞,提取了 20 個關鍵詞,每個關鍵詞都會記錄它在文件中出現的次數和出現位置。

那麼,倒排索引就是關鍵詞到文件 ID 的對映,每個關鍵詞都對應著一系列的檔案,這些檔案中都出現了關鍵詞。

舉個栗子。

有以下文件:

ElasticSearch寫入資料的工作原理是什麼?

對文件進行分詞之後,得到以下倒排索引

ElasticSearch寫入資料的工作原理是什麼?

另外,實用的倒排索引還可以記錄更多的資訊,比如文件頻率資訊,表示在文件集合中有多少個文件包含某個單詞。

那麼,有了倒排索引,搜尋引擎可以很方便地響應使用者的查詢。比如使用者輸入查詢 Facebook,搜尋系統查詢倒排索引,從中讀出包含這個單詞的文件,這些文件就是提供給使用者的搜尋結果。

要注意倒排索引的兩個重要細節:

  • 倒排索引中的所有詞項對應一個或多個文件;
  • 倒排索引中的詞項根據字典順序升序排列

上面只是一個簡單的栗子,並沒有嚴格按照字典順序升序排列。


免費Java資料需要自己領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高併發分散式、大資料、機器學習等技術。
傳送門:mp.weixin.qq.com/s/JzddfH-7y…


相關文章