1.對(duì)已知文檔的搜索
如果被搜索的文檔(不論是單個(gè)文檔,還是批量文檔)能夠從主分片或任意一個(gè)副本分片中被檢索到,則與索引文檔過(guò)程相同,對(duì)已知文檔的搜索也會(huì)用到路由算法,Elasticsearch 中的路由算法如下所示:
Shard = hash(routing) % number_of_primary_shards
2.對(duì)未知文檔的搜索
除了對(duì)已知文檔的搜索外,大部分請(qǐng)求實(shí)際上是不知道查詢(xún)條件會(huì)命中哪些文檔的。這些被查詢(xún)條件命中的文檔可能位于 Elasticsearch 集群中的任意位置上。因此,搜索請(qǐng)求的執(zhí)行不得不去查詢(xún)每個(gè)索引中的每一個(gè)分片。
在 Elasticsearch 中,搜索過(guò)程分為查詢(xún)階段(Query Pahse)和獲取階段(Fetch Phase)。
在查詢(xún)階段,查詢(xún)請(qǐng)求會(huì)廣播到索引中的每一個(gè)主分片和備份中,每一個(gè)分片都會(huì)在本地執(zhí)行檢索,并在本地各建立一個(gè)優(yōu)先級(jí)隊(duì)列(Priority Queue)。該優(yōu)先級(jí)隊(duì)列是一份根據(jù)文檔相關(guān)度指標(biāo)進(jìn)行排序的列表,列表的長(zhǎng)度由 from 和 size 兩個(gè)分頁(yè)參數(shù)決定。
查詢(xún)階段可以再細(xì)分成3個(gè)小的子階段:
(1)客戶(hù)端發(fā)送一個(gè)檢索請(qǐng)求給某個(gè)節(jié)點(diǎn)A,此時(shí)節(jié)點(diǎn)A會(huì)創(chuàng)建一個(gè)空的優(yōu)先級(jí)隊(duì)列,并配置好分頁(yè)參數(shù)from與size。
(2)節(jié)點(diǎn)A將搜索請(qǐng)求發(fā)送給該索引中的每一個(gè)分片,每個(gè)分片在本地執(zhí)行檢索,并將結(jié)果添加到本地優(yōu)先級(jí)隊(duì)列中。
(3)每個(gè)分片返回本地優(yōu)先級(jí)序列中所記錄的ID與sort值,并發(fā)送給節(jié)點(diǎn)A。節(jié)點(diǎn)A將這些值合并到自己的本地優(yōu)先級(jí)隊(duì)列中,并做出全局的排序。
在獲取階段,主要是基于上一階段找到所要搜索文檔的具體位置,將文檔數(shù)據(jù)內(nèi)容取回并返回給客戶(hù)端。
在Elasticsearch中,默認(rèn)的搜索類(lèi)型就是上面介紹的Query then Fetch。上述描述運(yùn)作方式就是Query then Fetch。Query then Fetch有可能會(huì)出現(xiàn)打分偏離的情形,幸好,Elasticsearch還提供了一個(gè)稱(chēng)為"DFS Query then Fetch"的搜索方式,它和Query then Fetch基本相同,但是它會(huì)執(zhí)行一個(gè)查詢(xún)來(lái)計(jì)算整體文檔的frequency。其處理過(guò)程如下所示:
(1)預(yù)查詢(xún)每個(gè)分片,詢(xún)問(wèn)Term和Document Frequency等信息。
(2)發(fā)送查詢(xún)請(qǐng)求到每個(gè)分片。
(3)找到各個(gè)分片中所有匹配的文檔,并使用全局的Term/Document Frequency信息進(jìn)行打分。在執(zhí)行過(guò)程中依然需要對(duì)結(jié)果構(gòu)建一個(gè)優(yōu)先隊(duì)列,如排序等。
(4)返回關(guān)于結(jié)果的元數(shù)據(jù)到請(qǐng)求節(jié)點(diǎn)。需要指出的是,此時(shí)實(shí)際文檔還沒(méi)有發(fā)送到請(qǐng)求節(jié)點(diǎn),發(fā)送的只是分?jǐn)?shù)。
(5)請(qǐng)求節(jié)點(diǎn)將來(lái)自所有分片的分?jǐn)?shù)合并起來(lái),并在請(qǐng)求節(jié)點(diǎn)上進(jìn)行排序,文檔被按照查詢(xún)要求進(jìn)行選擇。最終,實(shí)際文檔從它們各自所在的獨(dú)立的分片上被檢索出來(lái),結(jié)果被返回給讀者。
3.對(duì)詞條的搜索
具體到一個(gè)分片,ELasticsearch是如何按照詞條進(jìn)行進(jìn)行搜索的呢?
當(dāng)詞條數(shù)量較少時(shí),我們可以順序遍歷詞條獲取結(jié)果,但如果詞條有成千上萬(wàn)個(gè)時(shí),Elasticsearch為了能快速找到某個(gè)詞條,它對(duì)所有的詞條都進(jìn)行了排序,隨后使用二分法查找詞條,其查找效率為log(N)。這個(gè)過(guò)程就像查字典一樣,因此排序詞條的集合也稱(chēng)為T(mén)erm Dictionary。
為了提高查詢(xún)性能,Elasticsearch直接通過(guò)內(nèi)存查找詞條,而非從磁盤(pán)中讀取。但當(dāng)詞條太多時(shí),顯然Term Dictionary也會(huì)很大,此時(shí)全部放在內(nèi)存有些不現(xiàn)實(shí),于是引入了Term Index。
Term Index就像字典中的索引頁(yè),其中的內(nèi)容如字母A開(kāi)頭的有哪些詞條,這些詞條分別在哪頁(yè)。通過(guò)Term Index,Elasticsearch也可以快速定位到Term Dictionary的某個(gè)OffSet(位置偏移),然后從這個(gè)位置再往后順序查找。
前面提及了單個(gè)詞條的搜索方法,而在實(shí)際應(yīng)用中,更常見(jiàn)的往往是多個(gè)詞條拼接程的"聯(lián)合查詢(xún)"。