apache lucene_Apache Lucene中的并发查询执行
apache lucene
Apache Lucene是一個(gè)出色的并發(fā)純Java搜索引擎,如果您愿意,它可以輕松地使服務(wù)器上的可用CPU或IO資源飽和。 “典型” Lucene應(yīng)用程序的并發(fā)模型在搜索時(shí)每個(gè)查詢一個(gè)線程,但是您是否知道Lucene也可以使用多個(gè)線程同時(shí)執(zhí)行一個(gè)查詢,以大大減少最慢查詢的時(shí)間?
Lucene的IndexSearcher類負(fù)責(zé)執(zhí)行傳入的查詢以從索引中查找最匹配的匹配項(xiàng),它接受一個(gè)可選
施工期間執(zhí)行器 (例如線程池)。 如果您通過Executor并且CPU足夠閑置(即,服務(wù)器遠(yuǎn)低于其紅線QPS吞吐能力),Lucene將使用多個(gè)并發(fā)線程來查找每個(gè)查詢的總點(diǎn)擊率最高。
它是如何做到的? Lucene索引是分段的 ,這使得搜索成為一個(gè)棘手的并行問題:每個(gè)查詢都必須訪問索引中的所有細(xì)分,并收集其具有全球競(jìng)爭(zhēng)力的點(diǎn)擊量。 當(dāng)查詢?yōu)閱尉€程時(shí),因?yàn)槟鷽]有將Executor傳遞給IndexSearcher ,所以一個(gè)查詢線程必須順序訪問所有段。 如果索引很大,并且您的查詢成本很高,那么這些查詢自然會(huì)需要較高的CPU成本和掛鐘時(shí)間才能找到熱門廣告。 即使您在遠(yuǎn)遠(yuǎn)低于其紅線QPS(吞吐量)容量的情況下運(yùn)行服務(wù)器,這也會(huì)導(dǎo)致長(zhǎng)桿(P90 +)查詢延遲。
相反,當(dāng)您將Executor傳遞給IndexSearcher ,索引中的段首先被IndexSearcher分組為單個(gè)線程工作單元,稱為
螺紋片 。 默認(rèn)情況下 ,大段屬于它們自己的線程片,最多5個(gè)較小的段(最多250K總文檔)將合并為一個(gè)線程片,因?yàn)樗鼈兇蟾趴梢酝ㄟ^單個(gè)線程快速地順序搜索。 通過將IndexSearcher子類化并覆蓋其受保護(hù)的slices方法,可以輕松地自定義將段合并為線程片的方式。 只要服務(wù)器閑置到足以在一個(gè)查詢上花費(fèi)多個(gè)CPU內(nèi)核,并且該查詢的每個(gè)線程片上都有一個(gè)線程,那么每個(gè)并發(fā)查詢就會(huì)同時(shí)執(zhí)行。
這個(gè)強(qiáng)大的功能最初是由Jean-Fran?oisHalleux于16年前提出的 ,然后由Doug Cutting自己 (您好,Doug!)提出,并于大約9年前最終重構(gòu)為IndexSearcher ,此后進(jìn)行了許多迭代的改進(jìn),許多改進(jìn)現(xiàn)已展開感謝Atri Sharma ,最近添加了新的Lucene / Solr提交者 。 這就是熱情的開源軟件開發(fā)的分布式力量!
并發(fā)查詢執(zhí)行是Lucene中令人驚訝的很少使用的sleeper功能,因?yàn)樗形丛诨贚ucene構(gòu)建的兩個(gè)流行的分布式搜索應(yīng)用程序Elasticsearch和Solr中公開。 他們的并發(fā)模型是跨索引分片(通常在不同的服務(wù)器上)針對(duì)單個(gè)查詢的并發(fā)搜索,而是在每個(gè)分片內(nèi)使用單線程搜索。
這意味著需要許多并發(fā)的獨(dú)立查詢才能使集群范圍的CPU或IO資源飽和。 直到群集至少看到最低最低QPS,才能使用全部硬件資源。 對(duì)于經(jīng)常看到高查詢率的用例,此限制是可以接受的。 但是,如果Elasticsearch或Solr使用此功能,則具有較大索引和較低查詢率的其他常見用例將從單個(gè)群集節(jié)點(diǎn)內(nèi)的并發(fā)查詢執(zhí)行中受益匪淺。
摩爾定律在現(xiàn)實(shí)世界中的影響已經(jīng)發(fā)生了變化:現(xiàn)代服務(wù)器級(jí)計(jì)算機(jī)是用驚人且Swift增長(zhǎng)的并發(fā)硬件構(gòu)建的,不僅在它們的CPU中,我們現(xiàn)在在最新的c5.24xlarge AWS EC2實(shí)例中還可以看到96個(gè)內(nèi)核,圖形處理單元(GPU),內(nèi)存總線,DIMM和固態(tài)磁盤(SSD),實(shí)際上是底層的大型并發(fā)RAID 0陣列。 最近的趨勢(shì)是CPU和GPU獲得更多的并發(fā)(內(nèi)核),而每個(gè)單獨(dú)的內(nèi)核獲得的并發(fā)速度則更快。 為什么不使用所有這些增加的并發(fā)性來提高所有查詢的速度,甚至在低查詢負(fù)載時(shí)也使CPU / IO飽和?
棘手的權(quán)衡
不幸的是,盡管搜索Lucene索引是一個(gè)自然而尷尬的并行問題,但是對(duì)一個(gè)查詢使用多個(gè)線程會(huì)產(chǎn)生固有的協(xié)調(diào)開銷。 要了解原因,請(qǐng)考慮一個(gè)簡(jiǎn)單的類比:假設(shè)您需要蘋果,然后將孩子送到當(dāng)?shù)氐碾s貨店購買。 如果您只有一個(gè)孩子,則將其送給她,她會(huì)在整個(gè)農(nóng)產(chǎn)品區(qū)域四處走走,并挑選十個(gè)最好的蘋果,然后帶回家。
但是,如果您有五個(gè)孩子,然后將所有孩子都發(fā)送到商店,他們會(huì)更快地回來五次,而忽略了他們往返商店的“聯(lián)網(wǎng)”時(shí)間嗎? 他們?nèi)绾斡行У胤指罟ぷ?#xff1f;
也許您的孩子很聰明,他們首先將商店中的所有蘋果部分(如今有很多蘋果選擇 !)分成五個(gè)大致相等的部分。 每個(gè)人都圍繞著自己的蘋果區(qū)運(yùn)行,挑選她能找到的十個(gè)最好的蘋果,然后他們都在結(jié)帳柜臺(tái)集合,密切合作,從現(xiàn)在擁有的五十個(gè)蘋果中選出十個(gè)最好的蘋果? 這有點(diǎn)浪費(fèi),因?yàn)楹⒆觽兛偣彩占宋迨畟€(gè)蘋果,只是為了最終選擇實(shí)際的十個(gè)最佳蘋果,但確實(shí)比一個(gè)孩子選出十個(gè)最佳蘋果要快。
這實(shí)際上是Lucene今天實(shí)現(xiàn)并發(fā)搜索的方式:每個(gè)搜索器線程單獨(dú)工作以從一個(gè)線程切片(“映射”階段)中找到自己的前N個(gè)最佳匹配(然后,在所有查詢線程結(jié)束并重新加入主線程之后)在主線程中,主線程使用部分合并排序從每個(gè)線程切片收集的匹配中找到總前N個(gè)最佳匹配(“減少”階段)。 Lucene的CollectorManager , Collector和LeafCollector抽象都協(xié)同工作以實(shí)現(xiàn)此目的。 從現(xiàn)在開始,這意味著與單線程案例相比,完成了更多的工作
收集了M * N總匹配,然后最后減少到前N ,其中M是并發(fā)搜索線程的數(shù)量, N是請(qǐng)求檢索的頂級(jí)匹配的數(shù)量。
并發(fā)運(yùn)行每個(gè)查詢時(shí),增加的協(xié)調(diào)成本必然會(huì)損害搜索節(jié)點(diǎn)的紅線QPS容量(吞吐量),因?yàn)長(zhǎng)ucene會(huì)花費(fèi)更多的總CPU周期來查找最熱門。 但是,與此同時(shí),當(dāng)搜索節(jié)點(diǎn)具有大量備用CPU資源時(shí),它可以大大提高長(zhǎng)桿查詢的等待時(shí)間,因?yàn)樽罾щy的查詢現(xiàn)在可以同時(shí)運(yùn)行。 此外,收集更多匹配并最終合并它們的額外成本通常對(duì)總體影響不大,因?yàn)橥ǔJ敲總€(gè)匹配的匹配和排名決定了總查詢成本,尤其是隨著索引的增長(zhǎng),該成本是有效地跨線程拆分。
您可以通過限制可以同時(shí)運(yùn)行的查詢數(shù)來進(jìn)一步“擴(kuò)大”這種折衷,從而最大化每個(gè)查詢將使用多少個(gè)CPU內(nèi)核。 您還可以預(yù)先估算每個(gè)查詢的成本,并僅在其成本足夠大時(shí)并發(fā)執(zhí)行該查詢,這樣可以在單個(gè)線程中快速運(yùn)行的簡(jiǎn)單查詢不會(huì)承擔(dān)跨多個(gè)線程同步的開銷。
這種吞吐量與延遲之間的折衷令人沮喪,這意味著在您的Lucene應(yīng)用程序中使用模式方法可能很有意義。 集群負(fù)載較輕時(shí),通過限制可以同時(shí)運(yùn)行的查詢數(shù)來減少每個(gè)查詢的多個(gè)線程,從而減少長(zhǎng)桿延遲。 但是,當(dāng)群集正在運(yùn)行時(shí),接近其紅線容量時(shí),每個(gè)查詢將轉(zhuǎn)移到單個(gè)線程以最大化吞吐量。 確保您正確地測(cè)量了等待時(shí)間,并且負(fù)載測(cè)試客戶端沒有遭受普遍常見的協(xié)調(diào)遺漏錯(cuò)誤 ! 確認(rèn)您的負(fù)載測(cè)試客戶端正在使用開環(huán)測(cè)試,以便您看到真正的延遲影響,例如長(zhǎng)時(shí)間的垃圾收集暫停,I / O打ic或交換。
持續(xù)的和未來的改進(jìn)
幸運(yùn)的是,最近進(jìn)行了一些激動(dòng)人心的改進(jìn),以減少多線程查詢的額外開銷。 Lucene現(xiàn)在還使用傳入(調(diào)用)線程來幫助并發(fā)搜索 。 用于將小段分組為片(線程工作單元)的算法已得到改進(jìn) 。 現(xiàn)在,提前終止可以在多個(gè)搜索線程中使用一個(gè)共享的全局命中計(jì)數(shù)器來查詢一個(gè)查詢,從而降低了查詢的總成本。 查詢緩存將很快使用Executor進(jìn)行并發(fā)緩存,并且在某些情況下使用Executor時(shí)甚至可以更高效。 與其讓每個(gè)搜索線程完全獨(dú)??立地工作并僅在最后合并熱門匹配,不如讓它們?cè)谕瑫r(shí)收集時(shí)共享信息,例如到目前為止收集的最差得分熱門匹配,甚至在所有線程中使用單個(gè)共享優(yōu)先級(jí)隊(duì)列 。 共享優(yōu)先級(jí)隊(duì)列可能會(huì)導(dǎo)致過多的鎖定,因此,作為一種折衷,現(xiàn)在搜索可以高效地共享搜索者線程中收集到的最差命中值中的最好值 ,這顯示了令人印象深刻的luceneutil 基準(zhǔn)測(cè)試結(jié)果 。
這些改進(jìn)減少了并發(fā)搜索的額外成本,但是該成本永遠(yuǎn)不可能為零,因?yàn)楦l繁的線程上下文切換,共享優(yōu)先級(jí)隊(duì)列的鎖爭(zhēng)用,命中計(jì)數(shù)器和優(yōu)先級(jí)隊(duì)列底部以及潛在的困難后果都會(huì)帶來固有的自然成本。現(xiàn)代非均勻內(nèi)存架構(gòu)(NUMA) 。
Lucene并發(fā)搜索的一個(gè)令人驚訝且令人失望的局限性在于,完全合并的索引(直至單個(gè)段)會(huì)丟失所有并發(fā)性! 這就是Bizarro World ,因?yàn)橥ǔ?梢詫⑵渌饕喜⒌揭粋€(gè)段中以提高查詢性能! 但是,當(dāng)您查看長(zhǎng)桿查詢延遲時(shí),不幸的是,完全合并的索引會(huì)變慢,因?yàn)榧词鼓鷮xecutor傳遞給IndexSearcher所有查詢現(xiàn)在都將再次成為單線程。 即使單個(gè)新近完成的大型合并也會(huì)在您的長(zhǎng)極點(diǎn)延遲中引起鋸齒狀,因?yàn)樗鼤?huì)減少凈查詢并發(fā),盡管通過這種合并紅線群集的吞吐能力仍然有所提高。 解決這個(gè)問題的一個(gè)簡(jiǎn)單想法是允許多個(gè)線程搜索一個(gè)大的段 ,這應(yīng)該很好用,因?yàn)長(zhǎng)ucene具有自然的API,可以在段的“ docid空間”中搜索單獨(dú)的區(qū)域。
自讓-弗朗索瓦·哈勒克斯(Jean-Fran?oisHalleux)首次為L(zhǎng)ucene提出并行搜索以來,并發(fā)搜索已經(jīng)走了很長(zhǎng)一段路,我希望它還有很長(zhǎng)的路要走,以使我們真正減少使用多線程進(jìn)行昂貴查詢的額外開銷。 隨著Lucene改進(jìn)其查詢計(jì)劃和優(yōu)化,我們將達(dá)到輕松查詢運(yùn)行單線程但代價(jià)高昂的查詢同時(shí)高效運(yùn)行的地步。 這些改進(jìn)必須歸功于Lucene:現(xiàn)代服務(wù)器繼續(xù)添加越來越多的內(nèi)核,但并沒有使這些內(nèi)核變得太快,因此不可避免的是,包括Lucene在內(nèi)的現(xiàn)代軟件必須找到有效利用所有這些并發(fā)性的方法。
[我在亞馬遜工作,并且本網(wǎng)站上的帖子屬于我本人,不一定代表亞馬遜的職位]
翻譯自: https://www.javacodegeeks.com/2019/10/concurrent-query-execution-apache-lucene.html
apache lucene
總結(jié)
以上是生活随笔為你收集整理的apache lucene_Apache Lucene中的并发查询执行的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: qq钱包在哪里 在什么位置能找到qq钱包
- 下一篇: openjdk8 项目结构_OpenJD