from:http://www.infoq.com/cn/articles/anatomy-of-an-elasticsearch-cluster-part03


剖析Elasticsearch集群系列涵蓋了當(dāng)今最流行的分布式搜索引擎Elasticsearch的底層架構(gòu)和原型實例。本文是這個系列的第三篇,我們將討論Elasticsearch是如何提供近實時搜索并權(quán)衡搜索相關(guān)性計算的。

本系列已經(jīng)得到原文著者Ronak Nathani的授權(quán)

在本系列的前一篇中,我們討論了Elastisearch如何解決分布式系統(tǒng)中的一些基本挑戰(zhàn)。在本文中,我們將探討Elasticsearch在近實時搜索及其權(quán)衡計算搜索相關(guān)性方面的內(nèi)容,Insight Data的工程師們已經(jīng)在使用Elasticsearch構(gòu)建的數(shù)據(jù)平臺之上,對此有所實踐。我將在本文中主要講述:

近實時搜索

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

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

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

為什么深層分頁在分布式搜索中是有害的?

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

如果我們想看到排名為50,00050,010的結(jié)果,那么每個分片要創(chuàng)建一個容量為50,010的優(yōu)先隊列來存儲結(jié)果,而協(xié)調(diào)節(jié)點要在內(nèi)存中對數(shù)量為shards * 50,010的結(jié)果進(jìn)行排序。這個級別的分頁有可能得到結(jié)果,也有可以無法實現(xiàn),這取決于我們的硬件資源,但是這足以說明,我們得非常小心地使用深分頁,因為這非常容易使我們的集群崩潰。

一種獲取全部匹配結(jié)果文檔的可行性方案是使用scroll API,它的角色更像關(guān)系數(shù)據(jù)庫中的游標(biāo)。使用scroll API無法進(jìn)行排序,每個分片只要有匹配搜索的文檔,就會持續(xù)發(fā)送結(jié)果給協(xié)調(diào)節(jié)點。

獲取大量文檔的時候,對結(jié)果進(jìn)行得分排序會非常昂貴。并且由于Elasticsearch是分布式系統(tǒng),為每個文檔計算搜索相關(guān)性得分是非常昂貴的。現(xiàn)在,讓我們一起看看計算搜索相關(guān)性的諸多權(quán)衡中的一種。

計算搜索相關(guān)性中的權(quán)衡

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

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

這里有2種應(yīng)對本地idf得分的辦法,但都不建議真正在生產(chǎn)環(huán)境中使用。

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

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

查看英文原文:Anatomy of an Elasticsearch Cluster: Part III