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


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

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

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

近實(shí)時(shí)搜索

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

文件系統(tǒng)緩存可以擁有文件句柄,文件可以是開放的、可讀的或者是關(guān)閉的,但是它存在于內(nèi)存之中。因?yàn)樗⑿麻g隔默認(rèn)是1秒,變更不能立即可見,所以說是近實(shí)時(shí)的。因?yàn)?a style="text-decoration: none; color: #286ab2; outline: none !important; margin: 0px; border: 0px; padding: 0px;">translog是尚未落盤的變更持久化記錄,它能有助于CRUD操作方面的近實(shí)時(shí)性。對(duì)于每次請(qǐng)求來說,在查找相關(guān)段之前,任何最近的變更都能從translog搜索到,因此客戶端可以訪問到所有的近實(shí)時(shí)變更。

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

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

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

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

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

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

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

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

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

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

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

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

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