es服務端是准確知道每個document分布在哪個shard上;
search一個比較復雜的執行模式,因為我們不知道那些document會被匹配到,任何一個shard上都有可能,所以一個search請求必須查詢一個索引或多個索引里面的所有shard才能完整的查詢到我們想要的結果。
找到所有匹配的結果是查詢的第一步,來自多個shard上的數據集在分頁返回到客戶端的之前會被合並到一個排序后的list列表,由於需要經過一步取top N的操作,所以search需要進過兩個階段才能完成,分別是query和fetch。
(一)query(查詢階段)
當一個search請求發出的時候,這個query會被廣播到索引里面的每一個shard(主shard或副本shard),每個shard會在本地執行查詢請求后會生成一個命中文檔的優先級隊列。
這個隊列是一個排序好的top N數據的列表,它的size等於from+size的和,也就是說如果你的from是10,size是10,那么這個隊列的size就是20,所以這也是為什么深度分頁不能用from+size這種方式,因為from越大,性能就越低。
流程:
1,客戶端發送一個search請求到某個Node上,然后這個Node會創建一個優先級隊列它的大小=from+size
2,接着這個Node轉發這個search請求到索引里面每一個主shard或者副本shard上,每個shard會在本地查詢然后添加結果到本地的排序好的優先級隊列里面。
3,每個shard返回docId和所有參與排序字段的值例如_score到優先級隊列里面,然后再返回給coordinating節點也就是這個Node,然后這個Node負責將所有shard里面的數據給合並到一個全局的排序的列表。
上面提到一個術語叫coordinating node,這個節點是當search請求隨機負載的發送到一個節點上,然后這個節點就會成為一個coordinating node,它的職責是廣播search請求到所有相關的shard上,然后合並他們的響應結果到一個全局的排序列表中然后進行第二個fetch階段,注意這個結果集僅僅包含docId和所有排序的字段值,search請求可以被主shard或者副本shard處理,這也是為什么我們說增加副本的個數就能增加搜索吞吐量的原因,coordinating節點將會通過round-robin的方式自動負載均衡。
(二)fetch(讀取階段)
query階段標識了那些文檔滿足了該次的search請求,但是我們仍然需要檢索回document整條數據,這個階段稱為fetch
流程:
1,coordinating 節點標識了那些document需要被拉取出來,並發送一個批量的mutil get請求到相關的shard上
2,每個shard加載相關document,如果需要他們將會被返回到coordinating 節點上
3,一旦所有的document被拉取回來,coordinating節點將會返回結果集到客戶端上。
這里需要注意,coordinating節點拉取的時候只拉取需要被拉取的數據,比如from=90,size=10,那么fetch只會讀取需要被讀取的10條數據,這10條數據可能在一個shard上,也可能在多個shard上所以 coordinating節點會構建一個multi-get請求並發送到每一個shard上,每個shard會根據需要從_source字段里面獲取數據,一旦所有的數據返回,coordinating節點會組裝數據進入單個response里面然后將其返回給最終的client。
總結:
本文介紹了es的分布式search的查詢流程分為query和fetch兩個階段,在query階段會從所有的shard上讀取相關document的docId及相關的排序字段值,並最終在coordinating節點上收集所有的結果數進入一個全局的排序列表后,然后獲取根據from+size指定page頁的數據,獲取這些docId后再構建一個multi-get請求發送相關的shard上從_source里面獲取需要加載的數據,最終再返回給client端,至此整個search請求流程執行完畢