面試官:想了解應聘者以前公司接觸的 ES 使用場景、規模,有沒有作過比較大node
規模的索引設計、規劃、調優。面試
解答:算法
如實結合本身的實踐場景回答便可。api
好比:ES 集羣架構 13 個節點,索引根據通道不一樣共 20+索引,根據日期,每日數組
遞增 20+,索引:10 分片,每日遞增 1 億+數據,緩存
每一個通道天天索引大小控制:150GB 以內。服務器
僅索引層面調優手段:網絡
1.一、設計階段調優數據結構
一、根據業務增量需求,採起基於日期模板建立索引,經過 roll over API 滾動索架構
引;
二、使用別名進行索引管理;
三、天天凌晨定時對索引作 force_merge 操做,以釋放空間;
四、採起冷熱分離機制,熱數據存儲到 SSD,提升檢索效率;冷數據按期進行 shrink 操做,以縮減存儲;
五、採起 curator 進行索引的生命週期管理;
六、僅針對須要分詞的字段,合理的設置分詞器;
七、Mapping 階段充分結合各個字段的屬性,是否須要檢索、是否須要存儲等。……..
1.二、寫入調優
一、寫入前副本數設置爲 0;
二、寫入前關閉 refresh_interval 設置爲-1,禁用刷新機制;
三、寫入過程當中:採起 bulk 批量寫入;
四、寫入後恢復副本數和刷新間隔;
五、儘可能使用自動生成的 id。
1.三、查詢調優
一、禁用 wildcard;
二、禁用批量 terms(成百上千的場景);
三、充分利用倒排索引機制,能 keyword 類型儘可能 keyword;
四、數據量大時候,能夠先基於時間敲定索引再檢索;
五、設置合理的路由機制。
1.四、其餘調優
部署調優,業務調優等。
上面的說起一部分,面試者就基本對你以前的實踐或者運維經驗有所評估了。
面試官:想了解你對基礎概念的認知。
解答:通俗解釋一下就能夠。 傳統的咱們的檢索是經過文章,逐個遍歷找到對應關鍵詞的位置。 而倒排索引,是經過分詞策略,造成了詞和文章的映射關係表,這種詞典+映射表 即爲倒排索引。
有了倒排索引,就能實現 o(1)時間複雜度的效率檢索文章了,極大的提升了 檢索效率。
學術的解答方式:
倒排索引,相反於一篇文章包含了哪些詞,它從詞出發,記載了這個詞在哪些文 檔中出現過,由兩部分組成——詞典和倒排表。
加分項:倒排索引的底層實現是基於:FST(Finite State Transducer)數據結構。
lucene 從 4+版本後開始大量使用的數據結構是 FST。FST 有兩個優勢:
一、空間佔用小。經過對詞典中單詞前綴和後綴的重複利用,壓縮了存儲空間;
二、查詢速度快。O(len(str))的查詢時間複雜度。
面試官:想了解大數據量的運維能力。
解答:索引數據的規劃,應在前期作好規劃,正所謂「設計先行,編碼在後」, 這樣纔能有效的避免突如其來的數據激增致使集羣處理能力不足引起的線上客戶 檢索或者其餘業務受到影響。
如何調優,正如問題 1 所說,這裏細化一下:
3.1 動態索引層面
基於模板+時間+rollover api 滾動建立索引,舉例:設計階段定義:blog 索 引的模板格式爲:blog_index_時間戳的形式,天天遞增數據。
這樣作的好處:不至於數據量激增致使單個索引數據量很是大,接近於上線 2 的 32 次冪-1,索引存儲達到了 TB+甚至更大。
一旦單個索引很大,存儲等各類風險也隨之而來,因此要提早考慮+及早避免。
3.2 存儲層面
冷熱數據分離存儲,熱數據(好比最近 3 天或者一週的數據),其他爲冷數據。 對於冷數據不會再寫入新數據,能夠考慮按期 force_merge 加 shrink 壓縮操做, 節省存儲空間和檢索效率。
3.3 部署層面
一旦以前沒有規劃,這裏就屬於應急策略。
結合 ES 自身的支持動態擴展的特色,動態新增機器的方式能夠緩解集羣壓力,注意:若是以前主節點等規劃合理,不須要重啓集羣也能完成動態新增的。
面試官:想了解 ES 集羣的底層原理,再也不只關注業務層面了。
解答: 前置前提:
一、只有候選主節點(master:true)的節點才能成爲主節點。
二、最小主節點數(min_master_nodes)的目的是防止腦裂。
這個我看了各類網上分析的版本和源碼分析的書籍,雲裏霧裏。 覈對了一下代碼,核心入口爲 findMaster,選擇主節點成功返回對應 Master,否 則返回 null。選舉流程大體描述以下:
第一步:確認候選主節點數達標,elasticsearch.yml 設置的值discovery.zen.minimum_master_nodes;
第二步:比較:先斷定是否具有 master 資格,具有候選主節點資格的優先返回; 若兩節點都爲候選主節點,則 id 小的值會主節點。注意這裏的 id 爲 string 類型。
題外話:獲取節點 id 的方法
1GET /_cat/nodes?v&h=ip,port,heapPercent,heapMax,id,name 2ip port heapPercent heapMax id name
面試官:想了解 ES 的底層原理,再也不只關注業務層面了。
解答: 這裏的索引文檔應該理解爲文檔寫入 ES,建立索引的過程。 文檔寫入包含:單文檔寫入和批量 bulk 寫入,這裏只解釋一下:單文檔寫入流程。
記住官方文檔中的這個圖。
第一步:客戶寫集羣某節點寫入數據,發送請求。(若是沒有指定路由/協調節點, 請求的節點扮演路由節點的角色。)
第二步:節點 1 接受到請求後,使用文檔_id 來肯定文檔屬於分片 0。請求會被轉 到另外的節點,假定節點 3。所以分片 0 的主分片分配到節點 3 上。
第三步:節點 3 在主分片上執行寫操做,若是成功,則將請求並行轉發到節點 1 和節點 2 的副本分片上,等待結果返回。全部的副本分片都報告成功,節點 3 將 向協調節點(節點 1)報告成功,節點 1 向請求客戶端報告寫入成功。
若是面試官再問:第二步中的文檔獲取分片的過程?
回答:藉助路由算法獲取,路由算法就是根據路由和文檔 id 計算目標的分片 id 的過程。
1shard = hash(_routing) % (num_of_primary_shards)
面試官:想了解 ES 搜索的底層原理,再也不只關注業務層面了。
解答:
搜索拆解爲「query then fetch」 兩個階段。
query 階段的目的:定位到位置,但不取。
步驟拆解以下:
一、假設一個索引數據有 5 主+1 副本 共 10 分片,一次請求會命中(主或者副本 分片中)的一個。
二、每一個分片在本地進行查詢,結果返回到本地有序的優先隊列中。
三、第 2)步驟的結果發送到協調節點,協調節點產生一個全局的排序列表。
fetch 階段的目的:取數據。
路由節點獲取全部文檔,返回給客戶端。
面試官:想了解對 ES 集羣的運維能力。
解答:
一、關閉緩存 swap;
二、堆內存設置爲:Min(節點內存/2, 32GB);
三、設置最大文件句柄數;
四、線程池+隊列大小根據業務須要作調整;
五、磁盤存儲 raid 方式——存儲有條件使用 RAID10,增長單節點性能以及避免單 節點存儲故障。
面試官:想了解你的知識面的廣度和深度。
解答:
Lucene 是有索引和搜索的兩個過程,包含索引建立,索引,搜索三個要點。能夠
基於這個脈絡展開一些。
最近面試一些公司,被問到的關於 Elasticsearch 和搜索引擎相關的問題,以及自
己總結的回答。
一、Elasticsearch 的選主是 ZenDiscovery 模塊負責的,主要包含 Ping(節點之 間經過這個 RPC 來發現彼此)和 Unicast(單播模塊包含一個主機列表以控制哪 些節點須要 ping 通)這兩部分;
二、對全部能夠成爲 master 的節點(node.master: true)根據 nodeId 字典排 序,每次選舉每一個節點都把本身所知道節點排一次序,而後選出第一個(第 0 位) 節點,暫且認爲它是 master 節點。
三、若是對某個節點的投票數達到必定的值(能夠成爲 master 節點數 n/2+1)並 且該節點本身也選舉本身,那這個節點就是 master。不然從新選舉一直到知足上 述條件。
四、補充:master 節點的職責主要包括集羣、節點和索引的管理,不負責文檔級 別的管理;data 節點能夠關閉 http 功能*。
一、當集羣 master 候選數量不小於 3 個時,能夠經過設置最少投票經過數量 (discovery.zen.minimum_master_nodes)超過全部候選節點一半以上來解 決腦裂問題;
二、當候選數量爲兩個時,只能修改成惟一的一個 master 候選,其餘做爲 data 節點,避免腦裂問題。
一、TransportClient 利用 transport 模塊遠程鏈接一個 elasticsearch 集羣。它並 不加入到集羣中,只是簡單的得到一個或者多個初始化的 transport 地址,並以輪 詢的方式與這些地址進行通訊。
協調節點默認使用文檔 ID 參與計算(也支持經過 routing),以便爲路由提供適的分片。
shard = hash(document_id) % (num_of_primary_shards)
一、當分片所在的節點接收到來自協調節點的請求後,會將請求寫入到 Memory Buffer,而後定時(默認是每隔 1 秒)寫入到 Filesystem Cache,這個從 Momery Buffer 到 Filesystem Cache 的過程就叫作 refresh;
二、固然在某些狀況下,存在 Momery Buffer 和 Filesystem Cache 的數據可能會 丟失,ES 是經過 translog 的機制來保證數據的可靠性的。其實現機制是接收到請 求後,同時也會寫入到 translog 中,當 Filesystem cache 中的數據寫入到磁盤中 時,纔會清除掉,這個過程叫作 flush;
三、在 flush 過程當中,內存中的緩衝將被清除,內容被寫入一個新段,段的 fsync 將建立一個新的提交點,並將內容刷新到磁盤,舊的 translog 將被刪除並開始個新的 translog。
四、flush 觸發的時機是定時觸發(默認 30 分鐘)或者 translog 變得太大(默認 爲 512M)時;
補充:關於 Lucene 的 Segement:
一、Lucene 索引是由多個段組成,段自己是一個功能齊全的倒排索引。
二、段是不可變的,容許 Lucene 將新的文檔增量地添加到索引中,而不用從頭重 建索引。
三、對於每個搜索請求而言,索引中的全部段都會被搜索,而且每一個段會消耗 CPU 的時鐘周、文件句柄和內存。這意味着段的數量越多,搜索性能會越低。
四、爲了解決這個問題,Elasticsearch 會合並小段到一個較大的段,提交新的合併 段到磁盤,並刪除那些舊的小段。
一、刪除和更新也都是寫操做,可是 Elasticsearch 中的文檔是不可變的,所以不 能被刪除或者改動以展現其變動;
二、磁盤上的每一個段都有一個相應的.del 文件。當刪除請求發送後,文檔並無真 的被刪除,而是在.del 文件中被標記爲刪除。該文檔依然能匹配查詢,可是會在 結果中被過濾掉。當段合併時,在.del 文件中被標記爲刪除的文檔將不會被寫入 新段。
三、在新的文檔被建立時,Elasticsearch 會爲該文檔指定一個版本號,當執行更新 時,舊版本的文檔在.del 文件中被標記爲刪除,新版本的文檔被索引到一個新段。 舊版本的文檔依然能匹配查詢,可是會在結果中被過濾掉。
一、搜索被執行成一個兩階段過程,咱們稱之爲 Query Then Fetch;
二、在初始查詢階段時,查詢會廣播到索引中每個分片拷貝(主分片或者副本分 片)。 每一個分片在本地執行搜索並構建一個匹配文檔的大小爲 from + size 的 優先隊列。
PS:在搜索的時候是會查詢 Filesystem Cache 的,可是有部分數據還在 Memory Buffer,因此搜索是近實時的。
三、每一個分片返回各自優先隊列中全部文檔的 ID 和排序值給協調節點,它合併 這些值到本身的優先隊列中來產生一個全局排序後的結果列表。
四、接下來就是取回階段,協調節點辨別出哪些文檔須要被取回並向相關的分片 提交多個 GET 請求。每一個分片加載並_豐富_文檔,若是有須要的話,接着返回 文檔給協調節點。一旦全部的文檔都被取回了,協調節點返回結果給客戶端。
五、補充:Query Then Fetch 的搜索類型在文檔相關性打分的時候參考的是本分 片的數據,這樣在文檔數量較少的時候可能不夠準確,DFS Query Then Fetch 增 加了一個預查詢的處理,詢問 Term 和 Document frequency,這個評分更準確, 可是性能會變差。*
SEE:
一、64 GB 內存的機器是很是理想的, 可是 32 GB 和 16 GB 機器也是很常見的。 少於 8 GB 會拔苗助長。
二、若是你要在更快的 CPUs 和更多的核心之間選擇,選擇更多的核心更好。多 個內核提供的額外併發遠賽過稍微快一點點的時鐘頻率。
三、若是你負擔得起 SSD,它將遠遠超出任何旋轉介質。 基於 SSD 的節點,查 詢和索引性能都有提高。若是你負擔得起,SSD 是一個好的選擇。
四、即便數據中心們近在咫尺,也要避免集羣跨越多個數據中心。絕對要避免集羣 跨越大的地理距離。
五、請確保運行你應用程序的 JVM 和服務器的 JVM 是徹底同樣的。 在 Elasticsearch 的幾個地方,使用 Java 的本地序列化。
六、經過設置 gateway.recover_after_nodes、gateway.expected_nodes、 gateway.recover_after_time 能夠在集羣重啓的時候避免過多的分片交換,這可 能會讓數據恢復從數個小時縮短爲幾秒鐘。
七、Elasticsearch 默認被配置爲使用單播發現,以防止節點無心中加入集羣。只 有在同一臺機器上運行的節點纔會自動組成集羣。最好使用單播代替組播。
八、不要隨意修改垃圾回收器(CMS)和各個線程池的大小。
九、把你的內存的(少於)一半給 Lucene(但不要超過 32 GB!),經過 ES_HEAP_SIZE 環境變量設置。
十、內存交換到磁盤對服務器性能來講是致命的。若是內存交換到磁盤上,一個100 微秒的操做可能變成 10 毫秒。 再想一想那麼多 10 微秒的操做時延累加起來。 不難看出 swapping 對於性能是多麼可怕。
十一、Lucene 使用了_大量 的_文件。同時,Elasticsearch 在節點和 HTTP 客戶端之間進行通訊也使用了大量的套接字。 全部這一切都須要足夠的文件描述符。你應該增長你的文件描述符,設置一個很大的值,如 64,000。
補充:索引階段性能提高方法
一、使用批量請求並調整其大小:每次批量數據 5–15 MB 大是個不錯的起始點。
二、存儲:使用 SSD
三、段和合並:Elasticsearch 默認值是 20 MB/s,對機械磁盤應該是個不錯的設置。若是你用的是 SSD,能夠考慮提升到 100–200 MB/s。若是你在作批量導入, 徹底不在乎搜索,你能夠完全關掉合併限流。另外還能夠增長index.translog.flush_threshold_size 設置,從默認的 512 MB 到更大一些的值,好比 1 GB,這能夠在一次清空觸發的時候在事務日誌裏積累出更大的段。
四、若是你的搜索結果不須要近實時的準確度,考慮把每一個索引的 index.refresh_interval 改到 30s。
五、若是你在作大批量導入,考慮經過設置 index.number_of_replicas: 0 關閉副 本。
一、SEE:https://elasticsearch.cn/article/32
二、倒排詞典的索引須要常駐內存,沒法 GC,須要監控 data node 上 segment memory 增加趨勢。
三、各種緩存,field cache, filter cache, indexing cache, bulk queue 等等,要 設置合理的大小,而且要應該根據最壞的狀況來看 heap 是否夠用,也就是各種緩 存所有佔滿的時候,還有 heap 空間能夠分配給其餘任務嗎?避免採用 clear cache 等「自欺欺人」的方式來釋放內存。
四、避免返回大量結果集的搜索與聚合。確實須要大量拉取數據的場景,能夠採用 scan & scroll api 來實現。
五、cluster stats 駐留內存並沒有法水平擴展,超大規模集羣能夠考慮分拆成多個集 羣經過 tribe node 鏈接。
六、想知道 heap 夠不夠,必須結合實際應用場景,並對集羣的 heap 使用狀況作 持續的監控。
Elasticsearch 提供的首個近似聚合是 cardinality 度量。它提供一個字段的基數, 即該字段的_distinct_或者_unique_值的數目。它是基於 HLL 算法的。HLL 會先對 咱們的輸入做哈希運算,而後根據哈希運算的結果中的 bits 作機率估算從而獲得 基數。其特色是:可配置的精度,用來控制內存的使用(更精確 = 更多內存); 小的數據集精度是很是高的;咱們能夠經過配置參數,來設置去重須要的固定內 存使用量。不管數千仍是數十億的惟一值,內存使用量只與你配置的精確度相關。
一、能夠經過版本號使用樂觀併發控制,以確保新版本不會被舊版本覆蓋,由應用 層來處理具體的衝突;
二、另外對於寫操做,一致性級別支持 quorum/one/all,默認爲 quorum,即只 有當大多數分片可用時才容許寫操做。但即便大多數可用,也可能存在由於網絡 等緣由致使寫入副本失敗,這樣該副本被認爲故障,分片將會在一個不一樣的節點 上重建。
三、對於讀操做,能夠設置 replication 爲 sync(默認),這使得操做在主分片和副 本分片都完成後纔會返回;若是設置 replication 爲 async 時,也能夠經過設置搜 索請求參數_preference 爲 primary 來查詢主分片,確保文檔是最新版本。
Marvel 讓你能夠很簡單的經過 Kibana 監控 Elasticsearch。你能夠實時查看你 的集羣健康狀態和性能,也能夠分析過去的集羣、索引和節點指標。
SEE 基於 word2vec 和 Elasticsearch 實現個性化搜索
經常使用字典數據結構以下所示
Trie 的核心思想是空間換時間,利用字符串的公共前綴來下降查詢時間的開銷達到提升效率的目的。
它有 3 個基本性質:
一、根節點不包含字符,除根節點外每個節點都只包含一個字符。
二、從根節點到某一節點,路徑上通過的字符鏈接起來,爲該節點對應的字符串。
三、每一個節點的全部子節點包含的字符都不相同。
一、能夠看到,trie 樹每一層的節點數是 26^i 級別的。因此爲了節省空間,咱們 還能夠用動態鏈表,或者用數組來模擬動態。而空間的花費,不會超過單詞數×單 詞長度。
二、實現:對每一個結點開一個字母集大小的數組,每一個結點掛一個鏈表,使用左兒 子右兄弟表示法記錄這棵樹;
三、對於中文的字典樹,每一個節點的子節點用一個哈希表存儲,這樣就不用浪費太 大的空間,並且查詢速度上能夠保留哈希的複雜度 O(1)。
一、拼寫糾錯是基於編輯距離來實現;編輯距離是一種標準的方法,它用來表示經 過插入、刪除和替換操做從一個字符串轉換到另一個字符串的最小操做步數;
二、編輯距離的計算過程:好比要計算 batyu 和 beauty 的編輯距離,先建立一個 7×8 的表(batyu 長度爲 5,coffee 長度爲 6,各加 2),接着,在以下位置填入 黑色數字。其餘格的計算過程是取如下三個值的最小值:
若是最上方的字符等於最左方的字符,則爲左上方的數字。不然爲左上方的數字 +1。(對於 3,3 來講爲 0)
左方數字+1(對於 3,3 格來講爲 2)
上方數字+1(對於 3,3 格來講爲 2)
最終取右下角的值即爲編輯距離的值 3。
對於拼寫糾錯,咱們考慮構造一個度量空間(Metric Space),該空間內任何關 系知足如下三條基本條件:
d(x,y) = 0 -- 假如 x 與 y 的距離爲 0,則 x=y
d(x,y) = d(y,x) -- x 到 y 的距離等同於 y 到 x 的距離
d(x,y) + d(y,z) >= d(x,z) -- 三角不等式
一、根據三角不等式,則知足與 query 距離在 n 範圍內的另外一個字符轉 B,其與 A 的距離最大爲 d+n,最小爲 d-n。
二、BK 樹的構造就過程以下:每一個節點有任意個子節點,每條邊有個值表示編輯 距離。全部子節點到父節點的邊上標註 n 表示編輯距離剛好爲 n。好比,咱們有樹父節點是」book」和兩個子節點」cake」和」books」,」book」到」books」的邊標號 1,」book」到」cake」的邊上標號 4。從字典裏構造好樹後,不管什麼時候你想插入新單詞時,計算該單詞與根節點的編輯距離,而且查找數值爲d(neweord, root)的邊。遞歸得與各子節點進行比較,直到沒有子節點,你就能夠建立新的子節點並將新單詞保存在那。好比,插入」boo」到剛纔上述例子的樹中,咱們先檢查根節點,查找 d(「book」, 「boo」) = 1 的邊,而後檢查標號爲 1 的邊的子節點,獲得單詞」books」。咱們再計算距離 d(「books」, 「boo」)=2,則將新單詞插在」books」以後,邊標號爲 2。
三、查詢類似詞以下:計算單詞與根節點的編輯距離 d,而後遞歸查找每一個子節點 標號爲 d-n 到 d+n(包含)的邊。假如被檢查的節點與搜索單詞的距離 d 小於 n, 則返回該節點並繼續查詢。好比輸入 cape 且最大容忍距離爲 1,則先計算和根的 編輯距離 d(「book」, 「cape」)=4,而後接着找和根節點之間編輯距離爲 3 到 5 的,這個就找到了 cake 這個節點,計算 d(「cake」, 「cape」)=1,知足條件 因此返回cake,而後再找和 cake 節點編輯距離是 0 到 2 的,分別找到 cape 和 cart 節點,這樣就獲得cape這個知足條件的結果。