elasticsearch文檔檢索流程

這裏以es多節點集羣部署來做說明。單節點與之類似。基於es 7.1版本。

集羣模式下,對於document的檢索稱爲 Distribute document search。在簡單的三節點集羣中,假設一個index有兩個primary shard,每個shard有2個replicate。如下圖:其中,P0,P1爲兩個primary shard,NODE1和NODE2上的R0爲P0的兩個replicate,NODE2和NODE3上的R1爲P1的兩個replicate。P0+R0+R0爲一個replicate group,P1+R1+R1爲一個replicate group。按照es的存儲原則,一個節點上至多存儲同一個replicate group中的一個shard。

我們知道,es的底層是lucene,而lucene是一個全文搜索引擎,顯而易見的,es很重要的功能就是全文檢索,附帶數據存儲功能。當集羣接收到來自客戶端的文檔檢索請求,es除了要返回符合條件的內容,還要對返回的內容進行打分,按照搜索引擎的工作形式,將打分從高(也既從es看來最符合用戶要求的數據)到低的順序返回給客戶端。es將這兩項工作分成了兩步來完成:query+fetch。

首先,集羣中的某個節點,接收到來自客戶端的request,request可能包含了某個條件,並且包含了分頁參數from和size,假設是這樣的:

GET ac_blog/_search?preference=123456789
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "title": "entry"
        }}
      ]
    }
  },
  "from":100,
  "size":10
}

要從ac_blog這個index獲取title(text類型)包含entry的文檔,獲取排序100--109的document。這裏留意一下,es裏,from的下標是從0開始的。

要完成這個request,在query的步驟裏,es將請求分發到各個replicate group中的一個shard裏,至於是哪個shard,es是採用round-robin的方式選擇shard的(在7.1版本,還會額外考慮allocation awareness 和adaptive replica selection),確保各個shard的負載均衡。shard在自己內部根據條件檢索到對應的文檔,並按照_score由高到低排序,獲取從0到109共110個文檔的_id以及_score返回給node3。爲哈是110個而不是10個,這個後面解釋。如下圖所示:

1,request到達node3,此時node3作爲coordinator;
2,node3分發請求到node1的P1分片和NODE2的R0分片;
3,P1和R0在分片內部檢索數據,並返回id與score給node3.

此時,NODE3拿到了220個文檔,然後從這220個文檔裏,按照score從高到低排序,取100-109排序的文檔,其餘丟棄。然後根據文檔id構造_mget請求,到對應的分片獲取document的完整內容,並返回給客戶端。這是fetch階段。如下圖所示:

1,node3將選中的文檔id發回對應分片;
2,分片返回完整document內容;
3,node3將文檔內容返回給客戶端.

到此,整個檢索過程就結束了。但是其中有一些細節需要詳細描述。

首先,query階段,分片爲何返回110個文檔,而不是10個文檔給coordinator?這是因爲分片是在各自的內部進行排序的,這只是局部的順序,並不是整個index的全局排序,因此需要將前110個文檔返回給coordinator,由coordinator進行全局排序,確定最終的結果。這也是整個檢索過程中較爲耗時的地方。假如請求的是from=100000,size=10呢?假如有10個replicate group呢?資源耗費巨大。

其次就是排序結果的Bouncing result問題,如果兩個文檔score相同,那麼根據es的round-robin方法,第一次請求可能是從shardA獲取,第二次可能從shardB獲取,兩次獲取的文檔排序順序可能不同。按照官方文檔的描述,可以通過設置preference來使兩次請求均從相同shard獲取數據來避免Bouncing result問題。那麼,通過這個問題,我們可以猜測下,在score相同的情況下,es可能是通過文檔的物理存儲順序來決定排序先後的。因爲在寫入文檔時,數據是從primary shard並行寫入到各個shard copy,在請求併發的情況下,各個shard copy寫入文檔的順序可能不同;也可能在merge segment的時候,改變了文檔的物理存儲順序。更進一步,如果在score相同的情況下,使用文檔id進行排序,應該能避免這個問題。好,這個是解決分片內排序的問題,個人私想,如果在分片間score相同,應如何解決?根據分片id嗎?

注意上面的qsl,在url後面後綴了preference=123456789,這個就是用來確保兩次分頁查詢獲得相同的文檔順序。es可能在這個string上進行了hash。在7.1版本上,preference還有其他取值:

_only_local:操作僅僅在本地節點的shard上進行
_local:操作優先在本地節點的shard上進行,如果不可用,則在其他shard上進行
_prefer_nodes:abc,xyz:操作優先在指定的節點id上的shard進行,如果指定的多個節點均滿足的話,隨機選擇節點。
 _shards:2,3:操作在指定的分片id上進行,可以與其他參數結合使用,但_shards必須在第一個指定:_shards:2,3|_local
_only_nodes:abc*,x*yz,...:操作僅在指定的節點id上的shard進行,如果指定的多個節點均滿足的話,隨機選擇節點
Custom (string) value:任意string。儘量兩次request在相同的的shard上進行,如果滿足不了的話,可能在其他shard上進行

以上選項,只有_local能確保僅在本地shard執行,其他選項均爲best effort型,不保證一定按照要求進行。

 

qsl中同樣可以設置timeout參數,需要注意的是,這裏的timeout僅指單個shard檢索數據並返會到coordinator的過程。如果shard處理時間超過timeout指定值,則會返回部分結果或者空列表到coordinator。此時,客戶端收到的響應碼是成功的,但是在響應結果中可以查看time_out確定是否有shard有超時的情況。timeout是best-effort的,因此可能會有timeout後仍未返回的情況發生,官方文檔解釋如下:

It’s important to know that the timeout is still a best-effort operation; it’s possible for the query to surpass the allotted timeout. There are two reasons for this behavior:

  1. Timeout checks are performed on a per-document basis. However, some query types have a significant amount of work that must be performed before documents are evaluated. This "setup" phase does not consult the timeout, and so very long setup times can cause the overall latency to shoot past the timeout.
  2. Because the time is once per document, a very long query can execute on a single document and it won’t timeout until the next document is evaluated. This also means poorly written scripts (e.g. ones with infinite loops) will be allowed to execute forever.

如果個別shard運行較慢,則會拖慢整個請求響應。

其他可指定的參數包括routing(可參考這裏elasticsearch文檔_routine字段的使用)、search_type。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章