本文集主要是總結(jié)自己在項目中使用ES 的經(jīng)驗教訓(xùn)秆吵,包括各種實戰(zhàn)和調(diào)優(yōu)。
Elasticsearch是使用Java編寫的一種開源搜索引擎医寿,它在內(nèi)部使用Lucene做索引與搜索顿痪,通過對Lucene的封裝,提供了一套簡單一致的RESTfulAPI痛阻。Elasticsearch也是一種分布式的搜索引擎架構(gòu)菌瘪,可以很簡單地擴展到上百個服務(wù)節(jié)點,并支持PB級別的數(shù)據(jù)查詢阱当,使系統(tǒng)具備高可用和高并發(fā)特性俏扩。
核心概念
Elasticsearch的核心概念如下。
Cluster:集群弊添,由一個或多個Elasticsearch節(jié)點組成录淡。
Node:節(jié)點,組成Elasticsearch集群的服務(wù)單元油坝,同一個集群內(nèi)節(jié)點的名字不能重復(fù)嫉戚。通常在一個節(jié)點上分配一個或者多個分片。
Shards:分片免钻,當(dāng)索引上的數(shù)據(jù)量太大的時候彼水,我們通常會將一個索引上的數(shù)據(jù)進行水平拆分,拆分出來的每個數(shù)據(jù)塊叫作一個分片极舔。在一個多分片的索引中寫入數(shù)據(jù)時凤覆,通過路由來確定具體寫入那一個分片中,所以在創(chuàng)建索引時需要指定分片的數(shù)量拆魏,并且分片的數(shù)量一旦確定就不能更改盯桦。分片后的索引帶來了規(guī)模上(數(shù)據(jù)水平切分)和性能上(并行執(zhí)行)的提升慈俯。每個分片都是Lucene中的一個索引文件,每個分片必須有一個主分片和零到多個副本分片拥峦。
Replicas:備份也叫作副本贴膘,是指對主分片的備份。主分片和備份分片都可以對外提供查詢服務(wù)略号,寫操作時先在主分片上完成刑峡,然后分發(fā)到備份上。當(dāng)主分片不可用時玄柠,會在備份的分片中選舉出一個作為主分片突梦,所以備份不僅可以提升系統(tǒng)的高可用性能,還可以提升搜索時的并發(fā)性能羽利。但是若副本太多的話宫患,在寫操作時會增加數(shù)據(jù)同步的負擔(dān)。
Index:索引这弧,由一個和多個分片組成娃闲,通過索引的名字在集群內(nèi)進行唯一標(biāo)識。
Type:類別匾浪,指索引內(nèi)部的邏輯分區(qū)皇帮,通過Type的名字在索引內(nèi)進行唯一標(biāo)識。在查詢時如果沒有該值户矢,則表示在整個索引中查詢玲献。
Document:文檔,索引中的每一條數(shù)據(jù)叫作一個文檔梯浪,類似于關(guān)系型數(shù)據(jù)庫中的一條數(shù)據(jù)捌年,通過_id在Type內(nèi)進行唯一標(biāo)識。
Settings:對集群中索引的定義挂洛,比如一個索引默認的分片數(shù)礼预、副本數(shù)等信息。
Mapping:類似于關(guān)系型數(shù)據(jù)庫中的表結(jié)構(gòu)信息虏劲,用于定義索引中字段(Field)的存儲類型托酸、分詞方式、是否存儲等信息柒巫。Elasticsearch中的mapping是可以動態(tài)識別的励堡。如果沒有特殊需求,則不需要手動創(chuàng)建mapping堡掏,因為Elasticsearch會自動根據(jù)數(shù)據(jù)格式識別它的類型应结,但是當(dāng)需要對某些字段添加特殊屬性(比如:定義使用其他分詞器、是否分詞、是否存儲等)時鹅龄,就需要手動設(shè)置mapping了揩慕。一個索引的mapping一旦創(chuàng)建,若已經(jīng)存儲了數(shù)據(jù)扮休,就不可修改了迎卤。
Analyzer:字段的分詞方式的定義。一個analyzer通常由一個tokenizer玷坠、零到多個Filter組成蜗搔。比如默認的標(biāo)準(zhǔn)Analyzer包含一個標(biāo)準(zhǔn)的tokenizer和三個filter:Standard Token Filter、Lower Case Token Filter八堡、Stop Token Filter碍扔。
Elasticsearch的節(jié)點的分類如下。
主節(jié)點(Master Node):也叫作主節(jié)點秕重,主節(jié)點負責(zé)創(chuàng)建索引、刪除索引厉膀、分配分片溶耘、追蹤集群中的節(jié)點狀態(tài)等工作。Elasticsearch中的主節(jié)點的工作量相對較輕服鹅。用戶的請求可以發(fā)往任何一個節(jié)點凳兵,并由該節(jié)點負責(zé)分發(fā)請求、收集結(jié)果等操作企软,而并不需要經(jīng)過主節(jié)點轉(zhuǎn)發(fā)庐扫。通過在配置文件中設(shè)置node.master =true來設(shè)置該節(jié)點成為候選主節(jié)點(但該節(jié)點并不一定是主節(jié)點,主節(jié)點是集群在候選節(jié)點中選舉出來的)仗哨,在Elasticsearch集群中只有候選節(jié)點才有選舉權(quán)和被選舉權(quán)形庭。其他節(jié)點是不參與選舉工作的。
數(shù)據(jù)節(jié)點(Data Node):數(shù)據(jù)節(jié)點厌漂,負責(zé)數(shù)據(jù)的存儲和相關(guān)具體操作萨醒,比如索引數(shù)據(jù)的創(chuàng)建、修改苇倡、刪除富纸、搜索、聚合旨椒。所以晓褪,數(shù)據(jù)節(jié)點對機器配置要求比較高,首先需要有足夠的磁盤空間來存儲數(shù)據(jù)综慎,其次數(shù)據(jù)操作對系統(tǒng)CPU涣仿、Memory和I/O的性能消耗都很大。通常隨著集群的擴大寥粹,需要增加更多的數(shù)據(jù)節(jié)點來提高可用性变过。通過在配置文件中設(shè)置node.data=true來設(shè)置該節(jié)點成為數(shù)據(jù)節(jié)點埃元。
客戶端節(jié)點(Client Node):就是既不做候選主節(jié)點也不做數(shù)據(jù)節(jié)點的節(jié)點,只負責(zé)請求的分發(fā)媚狰、匯總等岛杀,也就是下面要說到的協(xié)調(diào)節(jié)點的角色。其實任何一個節(jié)點都可以完成這樣的工作崭孤,單獨增加這樣的節(jié)點更多地是為了提高并發(fā)性类嗤。
可在配置文件中設(shè)置該節(jié)點成為數(shù)據(jù)節(jié)點:
node.master =false
node.data=true
- 部落節(jié)點(TribeNode):部落節(jié)點可以跨越多個集群,它可以接收每個集群的狀態(tài)辨宠,然后合并成一個全局集群的狀態(tài)遗锣,它可以讀寫所有集群節(jié)點上的數(shù)據(jù),在配置文件中通過如下設(shè)置使節(jié)點成為部落節(jié)點:
tribe:
one:
cluster.name:
cluster_one
two:
cluster.name: cluster_two
因為Tribe Node要在Elasticsearch 7.0以后移除嗤形,所以不建議使用精偿。
- 協(xié)調(diào)節(jié)點(CoordinatingNode):協(xié)調(diào)節(jié)點,是一種角色赋兵,而不是真實的Elasticsearch的節(jié)點笔咽,我們沒有辦法通過配置項來配置哪個節(jié)點為協(xié)調(diào)節(jié)點。集群中的任何節(jié)點都可以充當(dāng)協(xié)調(diào)節(jié)點的角色霹期。當(dāng)一個節(jié)點A收到用戶的查詢請求后叶组,會把查詢語句分發(fā)到其他的節(jié)點,然后合并各個節(jié)點返回的查詢結(jié)果历造,最后返回一個完整的數(shù)據(jù)集給用戶甩十。在這個過程中,節(jié)點A扮演的就是協(xié)調(diào)節(jié)點的角色吭产。由此可見侣监,協(xié)調(diào)節(jié)點會對CPU、Memory和I/O要求比較高臣淤。
集群的狀態(tài)有Green达吞、Yellow和Red三種,如下所述荒典。
Green:綠色酪劫,健康。所有的主分片和副本分片都可正常工作寺董,集群100%健康覆糟。
Yellow:黃色,預(yù)警遮咖。所有的主分片都可以正常工作滩字,但至少有一個副本分片是不能正常工作的。此時集群可以正常工作,但是集群的高可用性在某種程度上被弱化麦箍。
Red:紅色漓藕,集群不可正常使用。集群中至少有一個分片的主分片及它的全部副本分片都不可正常工作挟裂。這時雖然集群的查詢操作還可以進行享钞,但是也只能返回部分數(shù)據(jù)(其他正常分片的數(shù)據(jù)可以返回),而分配到這個分片上的寫入請求將會報錯诀蓉,最終會導(dǎo)致數(shù)據(jù)的丟失栗竖。
3C和腦裂
1. 共識性(Consensus)
共識性是分布式系統(tǒng)中最基礎(chǔ)也最主要的一個組件,在分布式系統(tǒng)中的所有節(jié)點必須對給定的數(shù)據(jù)或者節(jié)點的狀態(tài)達成共識渠啤。雖然現(xiàn)在有很成熟的共識算法如Raft狐肢、Paxos等,也有比較成熟的開源軟件如ZooKeeper沥曹。但是Elasticsearch并沒有使用它們份名,而是自己實現(xiàn)共識系統(tǒng)zen discovery。Elasticsearch之父Shay Banon解釋了其中主要的原因:“zendiscovery是Elasticsearch的一個核心的基礎(chǔ)組件妓美,zen discovery不僅能夠?qū)崿F(xiàn)共識系統(tǒng)的選擇工作同窘,還能夠很方便地監(jiān)控集群的讀寫狀態(tài)是否健康。當(dāng)然部脚,我們也不保證其后期會使用ZooKeeper代替現(xiàn)在的zen discovery”。zendiscovery模塊以“八卦傳播”(Gossip)的形式實現(xiàn)了單播(Unicast):單播不同于多播(Multicast)和廣播(Broadcast)裤纹。節(jié)點間的通信方式是一對一的委刘。
2.并發(fā)(Concurrency)
Elasticsearch是一個分布式系統(tǒng)。寫請求在發(fā)送到主分片時锡移,同時會以并行的形式發(fā)送到備份分片,但是這些請求的送達時間可能是無序的漆际。在這種情況下淆珊,Elasticsearch用樂觀并發(fā)控制(Optimistic ConcurrencyControl)來保證新版本的數(shù)據(jù)不會被舊版本的數(shù)據(jù)覆蓋。
樂觀并發(fā)控制是一種樂觀鎖奸汇,另一種常用的樂觀鎖即多版本并發(fā)控制(Multi-VersionConcurrency Control)施符,它們的主要區(qū)別如下。
樂觀并發(fā)控制(OCC):是一種用來解決寫-寫沖突的無鎖并發(fā)控制擂找,認為事務(wù)間的競爭不激烈時戳吝,就先進行修改,在提交事務(wù)前檢查數(shù)據(jù)有沒有變化贯涎,如果沒有就提交听哭,如果有就放棄并重試。樂觀并發(fā)控制類似于自選鎖,適用于低數(shù)據(jù)競爭且寫沖突比較少的環(huán)境陆盘。
多版本并發(fā)控制(MVCC):是一種用來解決讀-寫沖突的無鎖并發(fā)控制普筹,也就是為事務(wù)分配單向增長的時間戳,為每個修改保存一個版本隘马,版本與事務(wù)時間戳關(guān)聯(lián)太防,讀操作只讀該事務(wù)開始前的數(shù)據(jù)庫的快照。這樣在讀操作不用阻塞寫操作且寫操作不用阻塞讀操作的同時祟霍,避免了臟讀和不可重復(fù)讀杏头。
3.一致性(Consistency)
Elasticsearch集群保證寫一致性的方式是在寫入前先檢查有多少個分片可供寫入,如果達到寫入條件沸呐,則進行寫操作醇王,否則,Elasticsearch會等待更多的分片出現(xiàn)崭添,默認為一分鐘寓娩。
有如下三種設(shè)置來判斷是否允許寫操作。
One:只要主分片可用呼渣,就可以進行寫操作棘伴。
All:只有當(dāng)主分片和所有副本都可用時,才允許寫操作屁置。
Quorum:是Elasticsearch的默認選項焊夸。當(dāng)有大部分的分片可用時才允許寫操作。
其中蓝角,對“大部分”的計算公式為int((primary + number_of_replicas) / 2 ) + 1阱穗。
Elasticsearch集群保證讀寫一致性的方式是,為了保證搜索請求的返回結(jié)果是最新版本的文檔使鹅,備份可以被設(shè)置為sync(默認值)揪阶,寫操作在主分片和備份分片同時完成后才會返回寫請求的結(jié)果。這樣患朱,無論搜索請求至哪個分片都會返回最新的文檔鲁僚。但是如果我們的應(yīng)用對寫要求很高,就可以通過設(shè)置replication=async來提升寫的效率裁厅,如果設(shè)置replication=async冰沙,則只要主分片的寫完成,就會返回寫成功执虹。
4.腦裂
在Elasticsearch集群中主節(jié)點通過ping命令來檢查集群中的其他節(jié)點是否處于可用狀態(tài)倦淀,同時非主節(jié)點也會通過ping命令來檢查主節(jié)點是否處于可用狀態(tài)。當(dāng)集群網(wǎng)絡(luò)不穩(wěn)定時声畏,有可能會發(fā)生一個節(jié)點ping不通Master節(jié)點撞叽,則會認為Master節(jié)點發(fā)生了故障姻成,然后重新選出一個Master節(jié)點,這就會導(dǎo)致在一個集群內(nèi)出現(xiàn)多個Master節(jié)點愿棋。當(dāng)在一個集群中有多個Master節(jié)點時科展,就有可能會導(dǎo)致數(shù)據(jù)丟失。我們稱這種現(xiàn)象為腦裂糠雨。在5.4.7節(jié)會介紹如何避免腦裂的發(fā)生才睹。
事務(wù)日志
我們在5.1節(jié)了解到,Lucene為了加快寫索引的速度甘邀,采用了延遲寫入的策略琅攘。雖然這種策略提高了寫入的效率,但其最大的弊端是松邪,如果數(shù)據(jù)在內(nèi)存中還沒有持久化到磁盤上時發(fā)生了類似斷電等不可控情況坞琴,就可能丟失數(shù)據(jù)。為了避免丟失數(shù)據(jù)逗抑,Elasticsearch添加了事務(wù)日志(Translog)剧辐,事務(wù)日志記錄了所有還沒有被持久化到磁盤的數(shù)據(jù)。
Elasticsearch寫索引的具體過程如下邮府。
首先荧关,當(dāng)有數(shù)據(jù)寫入時,為了提升寫入的速度褂傀,并沒有把數(shù)據(jù)直接寫在磁盤上忍啤,而是先寫入到內(nèi)存中,但是為了防止數(shù)據(jù)的丟失仙辟,會追加一份數(shù)據(jù)到事務(wù)日志里同波。因為內(nèi)存中的數(shù)據(jù)還會繼續(xù)寫入,所以內(nèi)存中的數(shù)據(jù)并不是以段的形式存儲的是檢索不到的欺嗤。總之卫枝,Elasticsearch是一個準(zhǔn)實時的搜索引擎煎饼,而不是一個實時的搜索引擎。此時的狀態(tài)如圖1所示校赤。
然后吆玖,當(dāng)?shù)竭_默認的時間(1秒鐘)或者內(nèi)存的數(shù)據(jù)到達一定量時,會觸發(fā)一次刷新(Refresh)马篮。刷新的主要步驟如下沾乘。
(1)將內(nèi)存中的數(shù)據(jù)刷新到一個新的段中,但是該段并沒有持久化到硬盤中浑测,而是緩存在操作系統(tǒng)的文件緩存系統(tǒng)中翅阵。雖然數(shù)據(jù)還在內(nèi)存中歪玲,但是內(nèi)存里的數(shù)據(jù)和文件緩存系統(tǒng)里的數(shù)據(jù)有以下區(qū)別。
內(nèi)存使用的是JVM的內(nèi)存掷匠,而文件緩存系統(tǒng)使用的是操作系統(tǒng)的內(nèi)存滥崩。
內(nèi)存的數(shù)據(jù)不是以段的形式存儲的,并且可以繼續(xù)向內(nèi)存里寫數(shù)據(jù)讹语。文件緩存系統(tǒng)中的數(shù)據(jù)是以段的形式存儲的钙皮,所以只能讀,不能寫顽决。
內(nèi)存中的數(shù)據(jù)是搜索不到的短条,文件緩存系統(tǒng)中的數(shù)據(jù)是可以搜索的。
(2)打開保存在文件緩存系統(tǒng)中的段才菠,使其可被搜索茸时。
(3)清空內(nèi)存,準(zhǔn)備接收新的數(shù)據(jù)鸠儿。日志不做清空處理屹蚊。
此時的狀態(tài)如圖2所示。
其次进每,數(shù)據(jù)繼續(xù)寫入汹粤,同時寫入內(nèi)存和日志,如圖3所示田晚。
最后嘱兼,刷新(Flush)。當(dāng)日志數(shù)據(jù)的大小超過512MB或者時間超過30分鐘時贤徒,需要觸發(fā)一次刷新芹壕。刷新的主要步驟如下。
(1)在文件緩存系統(tǒng)中創(chuàng)建一個新的段接奈,并把將內(nèi)存中的數(shù)據(jù)寫入踢涌,使其可被搜索。
(2)清空內(nèi)存序宦,準(zhǔn)備接收新的數(shù)據(jù)睁壁。
(3)將文件系統(tǒng)緩存中的數(shù)據(jù)通過fsync函數(shù)刷新到硬盤中。
(4)生成提交點互捌。
(5)刪除舊的日志潘明,創(chuàng)建一個空的日志。
此時的狀態(tài)如圖4所示秕噪。
由上面索引創(chuàng)建的過程可知钳降,內(nèi)存里面的數(shù)據(jù)并沒有直接被刷新(Flush)到硬盤中,而是被刷新(Refresh)到了文件緩存系統(tǒng)中腌巾,這主要是因為持久化數(shù)據(jù)十分耗費資源遂填,頻繁地調(diào)用會使寫入的性能急劇下降铲觉,所以Elasticsearch,為了提高寫入的效率城菊,利用了文件緩存系統(tǒng)和內(nèi)存來加速寫入時的性能备燃,并使用日志來防止數(shù)據(jù)的丟失。
在需要重啟時凌唬,Elasticsearch并齐,不僅要根據(jù)提交點去加載已經(jīng)持久化過的段,還需要根據(jù)Translog里的記錄客税,把未持久化的數(shù)據(jù)重新持久化到磁盤上况褪。
根據(jù)上面對Elasticsearch,寫操作流程的介紹更耻,我們可以整理出一個索引數(shù)據(jù)所要經(jīng)歷的幾個階段测垛,以及每個階段的數(shù)據(jù)的存儲方式和作用。如圖5所示秧均。
在集群中寫索引
假設(shè)我們有如圖6所示(圖片來自官網(wǎng))的一個集群食侮,該集群由三個節(jié)點組成(Node 1、Node 2和Node 3)目胡,包含一個由兩個主分片和每個主分片有兩個副本分片組成的索引锯七。其中,標(biāo)星號的Node 1是Master節(jié)點誉己,負責(zé)管理整個集群的狀態(tài)眉尸;p0和p1是主分片;r0和r1是副本分片巨双。為了達到高可用噪猾,Master節(jié)點會避免將主分片和副本放在同一個節(jié)點上。
將數(shù)據(jù)分片是為了提高可處理數(shù)據(jù)的容量和易于進行水平擴展筑累,為分片做副本是為了提高集群的穩(wěn)定性和提高并發(fā)量袱蜡。在主分片掛掉后,會從副本分片中選舉出一個升級為主分片慢宗,當(dāng)副本升級為主分片后坪蚁,由于少了一個副本分片,所以集群狀態(tài)會從green改變?yōu)閥ellow婆廊,但是此時集群仍然可用迅细。在一個集群中有一個分片的主分片和副本分片都掛掉后巫橄,集群狀態(tài)會由yellow改變?yōu)閞ed淘邻,集群狀態(tài)為red時集群不可正常使用。
由上面的步驟可知湘换,副本分片越多宾舅,集群的可用性就越高统阿,但是由于每個分片都相當(dāng)于一個Lucene的索引文件,會占用一定的文件句柄筹我、內(nèi)存及CPU扶平,并且分片間的數(shù)據(jù)同步也會占用一定的網(wǎng)絡(luò)帶寬,所以蔬蕊,索引的分片數(shù)和副本數(shù)也并不是越多越好结澄。
寫索引時只能寫在主分片上,然后同步到副本上岸夯,那么麻献,一條數(shù)據(jù)應(yīng)該被寫在哪個分片上呢?如圖5-19所示猜扮,如何知道一條數(shù)據(jù)應(yīng)該被寫在p0上還是p1上呢勉吻?答案就是路由(routing),路由的公式如下:
其中旅赢,routing是一個可選擇的值齿桃,默認是文檔的_id(文檔的唯一主鍵,文檔在創(chuàng)建時煮盼,如果文檔的_id已經(jīng)存在短纵,則進行更新,如果不存在則創(chuàng)建)孕似。后面會介紹如何通過自定義routing參數(shù)使查詢落在一個分片中踩娘,而不用查詢所有的分片,從而提升查詢的性能喉祭。routing通過hash函數(shù)生成一個數(shù)字养渴,將這個數(shù)字除以number_of_primary_shards(分片的數(shù)量)后得到余數(shù)。這個分布在0到number_of_primary_shards-1之間的余數(shù)泛烙,就是我們所尋求的文檔所在分片的位置理卑。這也就說明了分片數(shù)一旦定下來就不能再改變的原因,因為分片數(shù)改變后蔽氨,所有之前的路由值都會變得無效藐唠,前期創(chuàng)建的文檔也就找不到了。
由于在Elasticsearch集群中每個節(jié)點都知道集群中的文檔的存放位置(通過路由公式定位)鹉究,所以每個節(jié)點都有處理讀寫請求的能力宇立。在一個寫請求被發(fā)送到集群中的一個節(jié)點后,此時自赔,該節(jié)點被稱為協(xié)調(diào)節(jié)點(Coordinating Node)妈嘹,協(xié)調(diào)節(jié)點會根據(jù)路由公式計算出需要寫到哪個分片上,再將請求轉(zhuǎn)發(fā)到該分片的主分片節(jié)點上绍妨。寫操作的流程如下(見圖7润脸,圖片來自官網(wǎng))柬脸。
(1)客戶端向Node 1(協(xié)調(diào)節(jié)點)發(fā)送寫請求。
(2)Node 1通過文檔的_id(默認是_id毙驯,但不表示一定是_id)確定文檔屬于哪個分片(在本例中是編號為0的分片)倒堕。請求會被轉(zhuǎn)發(fā)到主分片所在的節(jié)點Node3上。
(3)Node 3在主分片上執(zhí)行請求爆价,如果成功垦巴,則將請求并行轉(zhuǎn)發(fā)到Node1和Node 2的副本分片上。一旦所有的副本分片都報告成功(默認)铭段,則Node 3將向協(xié)調(diào)節(jié)點報告成功魂那,協(xié)調(diào)節(jié)點向客戶端報告成功。
集群中的查詢流程
根據(jù)routing字段進行的單個文檔的查詢稠项,在Elasticsearch集群上可以在主分片或者副本分片上進行涯雅。查詢字段剛好是routing的分片字段如“_id”的查詢流程如下(見圖8,圖片來自官網(wǎng))展运。
(1)客戶端向集群發(fā)送查詢請求活逆,集群再隨機選擇一個節(jié)點作為協(xié)調(diào)節(jié)點(Node1),負責(zé)處理這次查詢?nèi)蝿?wù)拗胜。
(2)Node1使用文檔的routing id 來計算要查詢的文檔在哪個分片上(在本例中落在了0分片上)蔗候。分片0的副本分片存在所有的三個節(jié)點上。在這種情況下埂软,協(xié)調(diào)節(jié)點可以把請求轉(zhuǎn)發(fā)到任意節(jié)點锈遥。本例將請求轉(zhuǎn)發(fā)到Node 2上。
(3)Node 2執(zhí)行查找勘畔,并將查找結(jié)果返回給協(xié)調(diào)節(jié)點Node 1所灸,Node1再將文檔返回給客戶端。
在處理讀取請求時炫七,協(xié)調(diào)結(jié)點在每次請求時都會通過輪詢所有的副本分片來達到負載均衡爬立。
如果是普通的查詢,在查詢前并不知道數(shù)據(jù)在哪個分片上万哪,這時就需要查詢所有的分片侠驯,然后匯總所有的數(shù)據(jù),再把滿足條件的數(shù)據(jù)返回給客戶端奕巍。這種查詢相比于根據(jù)routing id查詢吟策,要復(fù)雜很多,并且性能要差很多的止。這種查詢的詳細流程如下
(1)客戶端發(fā)送一個查詢請求到任意節(jié)點檩坚,在本例中為Node 3,Node 3會創(chuàng)建一個大小為from + size的空優(yōu)先隊列。
(2)Node 3 將查詢請求轉(zhuǎn)發(fā)到索引的每個主分片或副本分片中效床。每個分片在本地執(zhí)行查詢并添加結(jié)果到大小為 from + size 的本地有序優(yōu)先隊列中。
(3)在默認情況下每個分片返回各自優(yōu)先隊列中所有文檔的id和得分score給協(xié)調(diào)節(jié)點权谁,協(xié)調(diào)節(jié)點合并這些值到自己的優(yōu)先隊列中來產(chǎn)生一個全局排序后的結(jié)果列表剩檀。
當(dāng)一個搜索請求被發(fā)送到某個節(jié)點時,這個節(jié)點就變成了協(xié)調(diào)節(jié)點(Node 1)旺芽。 協(xié)調(diào)節(jié)點的任務(wù)是廣播查詢請求到所有相關(guān)分片(主分片或者副本分片)沪猴,并將它們的響應(yīng)結(jié)果整合成全局排序后的結(jié)果集合,由上面步驟3所示采章,默認返回給協(xié)調(diào)節(jié)點的并不是所有的數(shù)據(jù),而是只有文檔的id和得分score,因為我們最后只返回給用戶size條數(shù)據(jù)橡羞,所以這樣做的好處是可以節(jié)省很多帶寬溯香,特別是from很大時。協(xié)調(diào)節(jié)點對收集回來的數(shù)據(jù)進行排序后抵怎,找到要返回的size條數(shù)據(jù)的id奋救,再根據(jù)id查詢要返回的數(shù)據(jù),比如title反惕、content等尝艘。取回數(shù)據(jù)的流程如下(見圖10,圖片來自官網(wǎng))姿染。
(1)Node 3進行二次排序來找出要返回的文檔id背亥,并向相關(guān)的分片提交多個獲得文檔詳情的請求。
(2)每個分片加載文檔悬赏,并將文檔返回給Node 3狡汉。
(3)一旦所有的文檔都被取回了,Node 3就返回結(jié)果給客戶端闽颇。
協(xié)調(diào)節(jié)點收集各個分片查詢出來的數(shù)據(jù)轴猎,再進行二次排序,然后選擇需要被取回的文檔进萄。例如捻脖,如果我們的查詢指定了{ "from": 20, "size": 10 },那么我們需要在每個分片中查詢出來得分最高的20+10條數(shù)據(jù)中鼠,協(xié)調(diào)節(jié)點在收集到30×n(n為分片數(shù))條數(shù)據(jù)后再進行排序可婶,排序位置在0-20的結(jié)果會被丟棄,只有從第21個開始的10個結(jié)果需要被取回援雇。這些文檔可能來自多個甚至全部分片矛渴。
由上面的搜索策略可以知道,在查詢時深翻(DeepPagination)并不是一種好方法。因為深翻時具温,from會很大蚕涤,這時的排序過程可能會變得非常沉重,會占用大量的CPU铣猩、內(nèi)存和帶寬揖铜。因為這個原因,所以強烈建議慎重使用深翻达皿。
分片可以減少每個片上的數(shù)據(jù)量天吓,加快查詢的速度,但是在查詢時峦椰,協(xié)調(diào)節(jié)點要在收集數(shù)(from+size)×n條數(shù)據(jù)后再做一次全局排序龄寞,若這個數(shù)據(jù)量很大,則也會占用大量的CPU汤功、內(nèi)存物邑、帶寬等,并且分片查詢的速度取決于最慢的分片查詢的速度滔金,所以分片數(shù)并不是越多越好拂封。