面試題
es 寫入數(shù)據(jù)的工作原理是什么按 ?es 查詢數(shù)據(jù)的工作原理是什么笆ㄑ睢母截?底層的 lucene 介紹一下唄?倒排索引了解嗎橄教?
面試官心理分析
問這個清寇,其實面試官就是要看看你了解不了解 es 的一些基本原理,因為用 es 無非就是寫入數(shù)據(jù)护蝶,搜索數(shù)據(jù)华烟。你要是不明白你發(fā)起一個寫入和搜索請求的時候,es 在干什么持灰,那你真的是......
對 es 基本就是個黑盒盔夜,你還能干啥?你唯一能干的就是用 es 的 api 讀寫數(shù)據(jù)了。要是出點什么問題喂链,你啥都不知道返十,那還能指望你什么呢?
面試題剖析
es 寫數(shù)據(jù)過程
- 客戶端選擇一個 node 發(fā)送請求過去椭微,這個 node 就是
coordinating node
(協(xié)調(diào)節(jié)點)洞坑。 -
coordinating node
對 document 進行路由,將請求轉(zhuǎn)發(fā)給對應(yīng)的 node(有 primary shard)蝇率。 - 實際的 node 上的
primary shard
處理請求检诗,然后將數(shù)據(jù)同步到replica node
。 -
coordinating node
如果發(fā)現(xiàn)primary node
和所有replica node
都搞定之后瓢剿,就返回響應(yīng)結(jié)果給客戶端。
es 讀數(shù)據(jù)過程
可以通過 doc id
來查詢悠轩,會根據(jù) doc id
進行 hash间狂,判斷出來當時把 doc id
分配到了哪個 shard 上面去,從那個 shard 去查詢火架。
- 客戶端發(fā)送請求到任意一個 node鉴象,成為
coordinate node
。 -
coordinate node
對doc id
進行哈希路由何鸡,將請求轉(zhuǎn)發(fā)到對應(yīng)的 node纺弊,此時會使用round-robin
隨機輪詢算法,在primary shard
以及其所有 replica 中隨機選擇一個骡男,讓讀請求負載均衡淆游。 - 接收請求的 node 返回 document 給
coordinate node
。 -
coordinate node
返回 document 給客戶端隔盛。
es 搜索數(shù)據(jù)過程
es 最強大的是做全文檢索犹菱,就是比如你有三條數(shù)據(jù):
java真好玩兒啊
java好難學(xué)啊
j2ee特別牛
你根據(jù) java
關(guān)鍵詞來搜索,將包含 java
的 document
給搜索出來吮炕。es 就會給你返回:java真好玩兒啊腊脱,java好難學(xué)啊。
- 客戶端發(fā)送請求到一個
coordinate node
龙亲。 - 協(xié)調(diào)節(jié)點將搜索請求轉(zhuǎn)發(fā)到所有的 shard 對應(yīng)的
primary shard
或replica shard
陕凹,都可以。 - query phase:每個 shard 將自己的搜索結(jié)果(其實就是一些
doc id
)返回給協(xié)調(diào)節(jié)點鳄炉,由協(xié)調(diào)節(jié)點進行數(shù)據(jù)的合并杜耙、排序、分頁等操作拂盯,產(chǎn)出最終結(jié)果泥技。 - fetch phase:接著由協(xié)調(diào)節(jié)點根據(jù)
doc id
去各個節(jié)點上拉取實際的document
數(shù)據(jù),最終返回給客戶端。
寫數(shù)據(jù)底層原理
先寫入內(nèi)存 buffer珊豹,在 buffer 里的時候數(shù)據(jù)是搜索不到的簸呈;同時將數(shù)據(jù)寫入 translog 日志文件。
如果 buffer 快滿了店茶,或者到一定時間蜕便,就會將內(nèi)存 buffer 數(shù)據(jù) refresh
到一個新的 segment file
中,但是此時數(shù)據(jù)不是直接進入 segment file
磁盤文件贩幻,而是先進入 os cache
轿腺。這個過程就是 refresh
。
每隔 1 秒鐘丛楚,es 將 buffer 中的數(shù)據(jù)寫入一個新的 segment file
族壳,每秒鐘會產(chǎn)生一個新的磁盤文件 segment file
,這個 segment file
中就存儲最近 1 秒內(nèi) buffer 中寫入的數(shù)據(jù)趣些。
但是如果 buffer 里面此時沒有數(shù)據(jù)仿荆,那當然不會執(zhí)行 refresh 操作,如果buffer里面有數(shù)據(jù)坏平,默認 1 秒鐘執(zhí)行一次 refresh 操作拢操,刷入一個新的 segment file 中。
操作系統(tǒng)里面舶替,磁盤文件其實都有一個東西令境,叫做 os cache
,即操作系統(tǒng)緩存顾瞪,就是說數(shù)據(jù)寫入磁盤文件之前舔庶,會先進入 os cache
,先進入操作系統(tǒng)級別的一個內(nèi)存緩存中去陈醒。只要 buffer
中的數(shù)據(jù)被 refresh 操作刷入 os cache
中栖茉,這個數(shù)據(jù)就可以被搜索到了。
為什么叫 es 是準實時的孵延? NRT
吕漂,全稱 near real-time
。默認是每隔 1 秒 refresh 一次的尘应,所以 es 是準實時的惶凝,因為寫入的數(shù)據(jù) 1 秒之后才能被看到∪郑可以通過 es 的 restful api
或者 java api
苍鲜,手動執(zhí)行一次 refresh 操作,就是手動將 buffer 中的數(shù)據(jù)刷入 os cache
中玷犹,讓數(shù)據(jù)立馬就可以被搜索到混滔。只要數(shù)據(jù)被輸入 os cache
中,buffer 就會被清空了,因為不需要保留 buffer 了坯屿,數(shù)據(jù)在 translog 里面已經(jīng)持久化到磁盤去一份了油湖。
重復(fù)上面的步驟,新的數(shù)據(jù)不斷進入 buffer 和 translog领跛,不斷將 buffer
數(shù)據(jù)寫入一個又一個新的 segment file
中去乏德,每次 refresh
完 buffer 清空,translog保留吠昭。隨著這個過程推進喊括,translog 會變得越來越大。當 translog 達到一定長度的時候矢棚,就會觸發(fā) commit
操作郑什。
commit 操作發(fā)生第一步,就是將 buffer 中現(xiàn)有數(shù)據(jù) refresh
到 os cache
中去蒲肋,清空 buffer蘑拯。然后,將一個 commit point
寫入磁盤文件肉津,里面標識著這個 commit point
對應(yīng)的所有 segment file
,同時強行將 os cache
中目前所有的數(shù)據(jù)都 fsync
到磁盤文件中去舱沧。最后清空 現(xiàn)有 translog 日志文件妹沙,重啟一個 translog,此時 commit 操作完成熟吏。
這個 commit 操作叫做 flush
距糖。默認 30 分鐘自動執(zhí)行一次 flush
,但如果 translog 過大牵寺,也會觸發(fā) flush
悍引。flush 操作就對應(yīng)著 commit 的全過程,我們可以通過 es api帽氓,手動執(zhí)行 flush 操作趣斤,手動將 os cache 中的數(shù)據(jù) fsync 強刷到磁盤上去。
translog 日志文件的作用是什么黎休?你執(zhí)行 commit 操作之前浓领,數(shù)據(jù)要么是停留在 buffer 中,要么是停留在 os cache 中势腮,無論是 buffer 還是 os cache 都是內(nèi)存联贩,一旦這臺機器死了,內(nèi)存中的數(shù)據(jù)就全丟了捎拯。所以需要將數(shù)據(jù)對應(yīng)的操作寫入一個專門的日志文件 translog
中泪幌,一旦此時機器宕機,再次重啟的時候,es 會自動讀取 translog 日志文件中的數(shù)據(jù)祸泪,恢復(fù)到內(nèi)存 buffer 和 os cache 中去吗浩。
translog 其實也是先寫入 os cache 的,默認每隔 5 秒刷一次到磁盤中去浴滴,所以默認情況下拓萌,可能有 5 秒的數(shù)據(jù)會僅僅停留在 buffer 或者 translog 文件的 os cache 中,如果此時機器掛了升略,會丟失 5 秒鐘的數(shù)據(jù)微王。但是這樣性能比較好,最多丟 5 秒的數(shù)據(jù)品嚣。也可以將 translog 設(shè)置成每次寫操作必須是直接 fsync
到磁盤炕倘,但是性能會差很多。
實際上你在這里翰撑,如果面試官沒有問你 es 丟數(shù)據(jù)的問題罩旋,你可以在這里給面試官炫一把,你說眶诈,其實 es 第一是準實時的涨醋,數(shù)據(jù)寫入 1 秒后可以搜索到;可能會丟失數(shù)據(jù)的逝撬。有 5 秒的數(shù)據(jù)浴骂,停留在 buffer、translog os cache宪潮、segment file os cache 中溯警,而不在磁盤上,此時如果宕機狡相,會導(dǎo)致 5 秒的數(shù)據(jù)丟失梯轻。
數(shù)據(jù)寫入 segment file 之后,同時就建立好了倒排索引尽棕。
刪除/更新數(shù)據(jù)底層原理
如果是刪除操作喳挑,commit 的時候會生成一個 .del
文件,里面將某個 doc 標識為 deleted
狀態(tài)滔悉,那么搜索的時候根據(jù) .del
文件就知道這個 doc 是否被刪除了蟀悦。
如果是更新操作,就是將原來的 doc 標識為 deleted
狀態(tài)氧敢,然后新寫入一條數(shù)據(jù)日戈。
buffer 每次 refresh 一次,就會產(chǎn)生一個 segment file
孙乖,所以默認情況下是 1 秒鐘一個 segment file
浙炼,這樣下來 segment file
會越來越多份氧,此時會定期執(zhí)行 merge。每次 merge 的時候弯屈,會將多個 segment file
合并成一個蜗帜,同時這里會將標識為 deleted
的 doc 給物理刪除掉,然后將新的 segment file
寫入磁盤资厉,這里會寫一個 commit point
厅缺,標識所有新的 segment file
,然后打開 segment file
供搜索使用宴偿,同時刪除舊的 segment file
湘捎。
底層 lucene
簡單來說,lucene 就是一個 jar 包窄刘,里面包含了封裝好的各種建立倒排索引的算法代碼窥妇。我們用 Java 開發(fā)的時候,引入 lucene jar娩践,然后基于 lucene 的 api 去開發(fā)就可以了活翩。
通過 lucene,我們可以將已有的數(shù)據(jù)建立索引翻伺,lucene 會在本地磁盤上面材泄,給我們組織索引的數(shù)據(jù)結(jié)構(gòu)。
倒排索引
在搜索引擎中吨岭,每個文檔都有一個對應(yīng)的文檔 ID拉宗,文檔內(nèi)容被表示為一系列關(guān)鍵詞的集合。例如未妹,文檔 1 經(jīng)過分詞簿废,提取了 20 個關(guān)鍵詞空入,每個關(guān)鍵詞都會記錄它在文檔中出現(xiàn)的次數(shù)和出現(xiàn)位置络它。
那么,倒排索引就是關(guān)鍵詞到文檔 ID 的映射歪赢,每個關(guān)鍵詞都對應(yīng)著一系列的文件化戳,這些文件中都出現(xiàn)了關(guān)鍵詞。
舉個栗子埋凯。
有以下文檔:
DocId | Doc |
---|---|
1 | 谷歌地圖之父跳槽 Facebook |
2 | 谷歌地圖之父加盟 Facebook |
3 | 谷歌地圖創(chuàng)始人拉斯離開谷歌加盟 Facebook |
4 | 谷歌地圖之父跳槽 Facebook 與 Wave 項目取消有關(guān) |
5 | 谷歌地圖之父拉斯加盟社交網(wǎng)站 Facebook |
對文檔進行分詞之后点楼,得到以下倒排索引。
WordId | Word | DocIds |
---|---|---|
1 | 谷歌 | 1,2,3,4,5 |
2 | 地圖 | 1,2,3,4,5 |
3 | 之父 | 1,2,4,5 |
4 | 跳槽 | 1,4 |
5 | 1,2,3,4,5 | |
6 | 加盟 | 2,3,5 |
7 | 創(chuàng)始人 | 3 |
8 | 拉斯 | 3,5 |
9 | 離開 | 3 |
10 | 與 | 4 |
.. | .. | .. |
另外白对,實用的倒排索引還可以記錄更多的信息掠廓,比如文檔頻率信息,表示在文檔集合中有多少個文檔包含某個單詞甩恼。
那么蟀瞧,有了倒排索引沉颂,搜索引擎可以很方便地響應(yīng)用戶的查詢。比如用戶輸入查詢 Facebook
悦污,搜索系統(tǒng)查找倒排索引铸屉,從中讀出包含這個單詞的文檔,這些文檔就是提供給用戶的搜索結(jié)果切端。