概念解析
CURD 操作
CURD 操作都是針對具體的某個或某些文檔的操作,每個文檔的 routing 都是確認的,所以其所在分片也是可以事先確定的。該過程對應 ES 的 Document API。
- 新建(C): 指對某個文檔進行索引操作的過程。
- 檢索(R): 指從 ES 中獲取某個或多個特定文檔的過程。
- 刪除(D): 指從 ES 中刪除某個文檔讓其不再可被搜索。
- 更新(U): 指在 ES 中更新某個文檔的過程,其實質是刪除+新建的過程。
搜索
搜索操作是指通過查詢條件從 ES 中獲取匹配的文檔的過程,搜索前不知道哪個文檔會匹配查詢。該過程對應 ES 的 Search API。
路由和分片
分片
- 文檔在索引的時候,需要確定文檔存放到哪個分片上去。(通過把 _id 作為 routing 來計算 shard)
- 文檔在檢索的時候,需要確定文檔處在具體哪個分片上。(通過把 _id 作為 routing 來計算 shard)
路由
分片的確定,都是由路由來完成的,具體計算公式如下:
shard = hash(routing) % number_of_primary_shards
- routing 值是一個任意字符串,它默認是 _id 但也可以自定義。
- routing 字符串通過哈希函數生成一個數字,然后除以主切片的數量得到一個余數(remainder),余數的范圍永遠是 0 到 number_of_primary_shards - 1 ,這個數字就是特定文檔所
在的分片。 - 這也解釋了為什么主分片的數量只能在創建索引時定義且不能修改:如果主分片的數量在未來改變了,所有先前的路由值就失效了,文檔也就永遠找不到了。
文檔的新建、索引和刪除
流程
新建、索引和刪除請求都是寫(write)操作,它們必須在主分片上成功完成才能復制到相關的復制分片上。並且要等所有復制分片完成后才向請求節點返回。
在主分片和復制分片上成功新建、索引或刪除一個文檔必要的順序步驟:
- 1. 客戶端給 Node 1 發送新建、索引或刪除請求,Node 1 作為協調節點。
- 2. 協調節點使用文檔的 _id 確定文檔屬於分片 0 (通過把 _id 作為 routing 來計算 shard)。它轉發請求到 Node 3 (主分片位於這個節點上)。
- 3. Node 3 在主分片上執行請求,如果成功,它轉發請求到相應的位於 Node 1 和 Node 2 的復制節點上。當所有的復制節點報告成功, Node 3 報告成功給協調節點。
- 4. 協調節點返回結果給客戶端。
客戶端接收到成功響應的時候,文檔的修改已經被應用於主分片和所有的復制分片。
由於要主分片和復制分片都成功后才返回成功,所以寫操作是比較耗時的。
優化
replication:
replication 默認為 sync。也就是要等所有復制分片都操作完后才返回。
設置為 async 運行在主分片操作完成后即返回。
文檔的檢索
流程
檢索文檔為讀(read)操作,請求只需分片的任意一個副本返回操作結果即完成。
在主分片或復制分片上檢索一個文檔必要的順序步驟:
- 1. 客戶端給 Node1(主節點) 發送 get 請求,Node 1 作為協調節點。
- 2. 協調節點使用文檔的 _id 確定文檔屬於分片 0(通過把 _id 作為 routing 來計算 shard) 。分片 0 對應的復制分片在三個節點上都有。此時,它轉發請求到 Node 2 。
- 3. Node 2 返回執行結果給協調節點。
- 4. 協調節點返回結果給客戶端。
對於讀請求,為了平衡負載,協調節點會為每個分片的請求選擇不同的副本——它會循環所有分片副本。
文檔的更新
流程
更新過程整體流程就是 “讀” + “寫” 操作。
執行更新必要的順序步驟:
- 1. 客戶端給 Node 1 發送更新請求,Node 1 作為協調節點。
- 2. 協調節點轉發請求到主分片所在節點 Node 3(主分片) 。
- 3. Node 3 從主分片檢索出文檔,修改 _source 字段的JSON,然后在主分片上重新索引。如果有其他進程修改了文檔,它以 retry_on_conflict 設置的次數重復步驟3,都未成功則放棄。
- 4. 如果 Node 3 成功更新文檔,它同時轉發文檔的新版本到 Node 1 和 Node 2 上的復制分片以重新索引。當所有節點報告成功, Node 3 返回成功給協調節點。
- 5. 協調節點返回結果給客戶端。
批量文檔操作
批量檢索(mget)和批量新建、索引、更新、刪除(bulk)操作和單個文檔的操作過程類似。
區別在於協調節點知道所有文檔所在的分片,並將請求的文檔根據所在的分片來分組。然后同時請求需要的節點。
一旦收到所有節點的響應,協調節點再將這多個節點的響應組合成一個響應結果返回給客戶端。
流程
mget 操作
mget 操作過程基本步驟:
- 1. 客戶端發送請求到 Node 1,Node 1 作為協調節點。
- 2. 協調節點確認每一個操作請求的目標分片,並根據需要請求的目標分片重新分組。
- 2. 協調節點同時轉發每組請求到目標主分片或復制分片(檢索操作任意分片都可以)。
- 3. 一旦所有請求的分片都返回,協調節點整理結果,並返回給客戶端。
bulk操作
bulk 操作過程基本步驟:
- 1. 客戶端發送請求到 Node 1,Node 1 作為協調節點。
- 2. 協調節點確認每一個操作請求的目標主分片,並根據目標主分片重新分組。
- 2. 協調節點同時請求包含這些主分片的節點(步驟2)。
- 3. 每一個主分片一個接一個的處理每一個文檔請求——某個文檔在主分片上請求成功了,主分片將請求發送給它所有的復制分片,然后就接着處理下一個文檔請求。
- 4. 當所有的復制分片請求成功后,主分片所在節點就向協調節點報告成功。
- 5. 協調節點整理所有文檔的結果,並返回給客戶端。
搜索
文檔的 CRUD 操作一次只處理一個單獨的文檔(批量操作也是單個執行)。CRUD 操作中,文檔的唯一性由 _index , _type 和 routing (通常默認是該文檔的 _id )的組合來確定。這意味着我們可以准確知道集群中的哪個分片有這個文檔。
搜索過程,由於不知道哪個文檔會匹配查詢(文檔可能存放在集群中的任意分片上),所以搜索需要一個更復雜的模型。搜索通過查詢每一個我們感興趣的索引的分片副本,來看是否含有任何匹配的文檔。
搜索的執行過程分兩個階段,稱為查詢然后取回(query then fetch)。
查詢階段
GET /_search
{ "from": 90,
"size": 10
}
1. 客戶端發送一個 search(搜索) 請求給 Node 3 , Node 3 創建了一個長度為 from+size 的空優先級隊列。
2. Node 3 轉發這個搜索請求到索引中每個分片的原本或副本。搜索請求可以被每個分片的原本或任意副本處理。
(並非所有副本都處理同樣的請求,而是輪詢處理不同的請求,所以多副本能夠提高吞吐)
3. 每個分片在本地執行這個查詢並且結果將結果到一個大小為 from+size 的有序本地優先隊列里去。
4. 每個分片返回document的ID和它優先隊列里的所有document的排序值給協調節點 Node 3 。 Node 3 把這些值合並到自己的優先隊列里產生全局排序結果。
5. 對於多(multiple)或全部(all)索引的搜索的工作機制和這完全一致——僅僅是多了一些分片而已。
取回階段
查詢階段辨別出那些滿足搜索請求的document,但我們仍然需要取回那些document本身。這就是取回階段的工作。
1. 協調節點(請求節點)辨別出哪個document需要取回(比如只取前100項),並且向相關分片發出 GET 請求。
2. 每個分片加載document並且根據需要豐富(enrich)它們,然后再將document返回協調節點。
3. 一旦所有的document都被取回,協調節點會將結果返回給客戶端。
搜索選項
1. preference:
preference 參數允許你控制使用哪個分片或節點來處理搜索請求。她接受如下一些參數 _primary , _primary_first ,_local , _only_node:xyz , _prefer_node:xyz 和 _shards:2,3
2. timeout:
通常,協調節點會等待接收所有分片的回答。如果有一個節點遇到問題,它會拖慢整個搜索請求。timeout 參數告訴協調節點最多等待多久,就可以放棄等待而將已有結果返回。
3. routing:
指定一個或多個 routing 值來限制只搜索那些分片而不是搜索index里的全部分片。
4. search_type:
- count(計數):當不需要搜索結果只需要知道滿足查詢的document的數量時,可以使用這個查詢類型。
- query_and_fetch:搜索類型將查詢和取回階段合並成一個步驟
- dfs_query_then_fetch 和 dfs_query_and_fetch
- scan:scan(掃描) 搜索類型是和 scroll(滾屏) API連在一起使用的,可以高效地取回巨大數量的結果。它是通過禁用排序來實現的。