天天看點

ElasticSearch 面試 4 連炮,你頂得住麼?

面試題

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 面試 4 連炮,你頂得住麼?
    es-write

es 讀資料過程

可以通過 

doc id

 來查詢,會根據 

doc id

 進行 hash,判斷出來當時把 

doc id

 配置設定到了哪個 shard 上面去,從那個 shard 去查詢。

  • 用戶端發送請求到任意 一個 node,成為 

    coordinate node

  • coordinate node

     對 

    doc id

     進行哈希路由,将請求轉發到對應的 node,此時會使用 

    round-robin

     随機輪詢算法 ,在 

    primary shard

     以及其所有 replica 中随機選擇一個,讓讀請求負載均衡。
  • 接收請求的 node 傳回 document 給 

    coordinate node

  • coordinate node

     傳回 document 給用戶端。

es 搜尋資料過程

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

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

你根據 

java

 關鍵詞來搜尋,将包含 

java

的 

document

 給搜尋出來。es 就會給你傳回:java真好玩兒啊,java好難學啊。

  • 用戶端發送請求到一個 

    coordinate node

  • 協調節點将搜尋請求轉發到所有 的 shard 對應的 

    primary shard

     或 

    replica shard

    ,都可以。
  • query phase:每個 shard 将自己的搜尋結果(其實就是一些 

    doc id

    )傳回給協調節點,由協調節點進行資料的合并、排序、分頁等操作,産出最終結果。
  • fetch phase:接着由協調節點根據 

    doc id

     去各個節點上拉取實際 的 

    document

     資料,最終傳回給用戶端。

寫資料底層原理

ElasticSearch 面試 4 連炮,你頂得住麼?

es-write-detail

先寫入記憶體 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 中現有資料 

refresh

 到 

os 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 秒的資料丢失 。

資料寫入 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 的映射,每個關鍵詞都對應着一系列的檔案,這些檔案中都出現了關鍵詞。

舉個栗子。

有以下文檔:

DocId Doc
1 谷歌地圖之父跳槽 Facebook
2 谷歌地圖之父加盟 Facebook
3 谷歌地圖創始人拉斯離開谷歌加盟 Facebook
4 谷歌地圖之父跳槽 Facebook 與 Wave 項目取消有關
5 谷歌地圖之父拉斯加盟社交網站 Facebook

對文檔進行分詞之後,得到以下反向索引 。

WordId Word DocIds
谷歌 1,2,3,4,5
地圖
之父 1,2,4,5
跳槽 1,4
Facebook
6 加盟 2,3,5
7 創始人
8 拉斯 3,5
9 離開
10
..

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