剖析 Elasticsearch 集羣系列第三篇 近實時搜索、深層分頁問題和搜索相關性權衡之道

最近在學習ES,發現《剖析 Elasticsearch 集羣系列》文章寫得挺好,轉載過來記錄下。

原文:https://www.infoq.cn/article/anatomy-of-an-elasticsearch-cluster-part03/

剖析 Elasticsearch 集羣系列涵蓋了當今最流行的分佈式搜索引擎 Elasticsearch 的底層架構和原型實例。本文是這個系列的第三篇,我們將討論 Elasticsearch 是如何提供近實時搜索並權衡搜索相關性計算的。 

在本系列的前一篇中,我們討論了 Elastisearch 如何解決分佈式系統中的一些基本挑戰。在本文中,我們將探討 Elasticsearch 在近實時搜索及其權衡計算搜索相關性方面的內容,Insight Data 的工程師們已經在使用 Elasticsearch 構建的數據平臺之上,對此有所實踐。我將在本文中主要講述:

近實時搜索

雖然 Elasticsearch 中的變更不能立即可見,它還是提供了一個近實時的搜索引擎。如前一篇中所述,提交 Lucene 的變更到磁盤是一個代價昂貴的操作。爲了避免在文檔對查詢依然有效的時候,提交變更到磁盤,Elasticsearch 在內存緩衝和磁盤之間提供了一個文件系統緩存。內存緩存 (默認情況下) 每 1 秒刷新一次,在文件系統緩存中使用倒排索引創建一個新的段。這個段是開放的並對搜索有效。

文件系統緩存可以擁有文件句柄,文件可以是開放的、可讀的或者是關閉的,但是它存在於內存之中。因爲刷新間隔默認是 1 秒,變更不能立即可見,所以說是近實時的。因爲 translog 是尚未落盤的變更持久化記錄,它能有助於 CRUD 操作方面的近實時性。對於每次請求來說,在查找相關段之前,任何最近的變更都能從 translog 搜索到,因此客戶端可以訪問到所有的近實時變更。

你可以在創建 / 更新 / 刪除操作後顯式地刷新索引,使變更立即可見,但我並不推薦你這樣做,因爲這樣會創建出來非常多的小 segment 而影響搜索性能。對於每次搜索請求來說,給定 Elasticsearch 索引分片中的全部 Lucene 段都會被搜索到,但是,對於 Elasticsearch 來說,獲取全部匹配的文檔或者很深結果頁的文檔是有害的。讓我們來一起看看爲什麼是這樣。 

爲什麼深層分頁在分佈式搜索中是有害的?

當我們的一次搜索請求在 Elasticsearch 中匹配了很多的文檔,默認情況下,返回的第一頁只包含前 10 條結果。search API 提供了fromsize參數,用於指定對於匹配搜索的全部文檔,要返回多深的結果。舉例來說,如果我們想看到匹配搜索的文檔中,排名爲5060之間的文檔,可以設置from=50size=10。當每個分片接收到這個搜索請求後,各自會創建一個容量爲from+size的優先隊列來存儲該分片上的搜索結果,然後將結果返回給協調節點。 

åæElasticsearché群系å第ä¸ç¯ è¿å®æ¶æç´¢ãæ·±å±å页é®é¢åæç´¢ç¸å³æ§æè¡¡ä¹é

如果我們想看到排名爲50,00050,010的結果,那麼每個分片要創建一個容量爲50,010的優先隊列來存儲結果,而協調節點要在內存中對數量爲 shards * 50,010的結果進行排序。這個級別的分頁有可能得到結果,也有可以無法實現,這取決於我們的硬件資源,但是這足以說明,我們得非常小心地使用深分頁,因爲這非常容易使我們的集羣崩潰。

一種獲取全部匹配結果文檔的可行性方案是使用 scroll API ,它的角色更像關係數據庫中的遊標。使用scroll API 無法進行排序,每個分片只要有匹配搜索的文檔,就會持續發送結果給協調節點。

獲取大量文檔的時候,對結果進行得分排序會非常昂貴。並且由於 Elasticsearch 是分佈式系統,爲每個文檔計算搜索相關性得分是非常昂貴的。現在,讓我們一起看看計算搜索相關性的諸多權衡中的一種。 

計算搜索相關性中的權衡

Elasticsearch 使用 tf-idf 來計算搜索相關性。由於其分佈式的性質,計算全局的 idf(inverse document frequency,逆文檔頻率) 非常昂貴。反之可以這樣,每個分片計算本地的 idf 並將相關性得分分配給結果文檔,返回的結果只關乎該分片上的文檔。同樣地,所有分片使用本地 idf 計算的相關性得分,返回結果文檔,協調節點對所有結果排序並返回前幾條。這樣做在大多數情況下是沒有問題的,除非索引的關鍵字詞項有傾斜或者單個分片上沒有代表全局的足夠數據。

比如說,如果我們搜索“insight”這個詞,但包含"insight"這個詞項的大多數文檔都存放在一個分片上,這樣以來匹配查詢的文檔將不能公平地在每個分片上進行排序,因爲每個分片上的本地 idf 的值非常不同,得到的搜索結果可能不會非常相關。同樣地,如果沒有足夠的數據,那麼對於某些搜索而言,本地 idf 的值可能大有不同,結果也會不如預期相關。在有足夠數據的真實場景中,本地 idf 值一般會趨於均等,搜索結果是相關的,因爲文檔得到了公平的得分。

這裏有 2 種應對本地 idf 得分的辦法,但都不建議真正在生產環境中使用。 

  • 一種辦法是一索引一分片,本地 idf 即是全局 idf,但這沒有爲並行計算 / 水平伸縮留有餘地,對於大型索引並不實用。
  • 另一種辦法是在搜索請求中使用dfs_query_then_search (dfs = distributed frequency search,分佈式頻率搜索) 參數,這樣以來,會首先計算每個分片的本地 idf,然後綜合這些本地 idf 的值來計算整個索引的全局 idf 值,最後使用全局 idf 計算相關性得分來返回結果。這種方式不爲生產環境推薦,因爲有足夠的數據確保詞項頻率分佈均勻。

在本系列的過去幾篇中,我們回顧了一些 Elasticsearch 的基本原則,對於我們理解並上手 Elasticsearch,這些內容非常重要。在接下來的一篇中,我將使用 Apache Spark 來研究 Elasticsearch 中的索引數據。 

發佈了74 篇原創文章 · 獲贊 88 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章