本節首先簡要介紹Elasticsearch的數據複製模型,然後會對CRUD API進行詳細說明:
單文檔API
多文檔API
所有CRUD API都是單索引API。
index
參數接受單個索引名稱或指向單個索引的alias
。
讀寫文檔
介紹
在Elasticsearch中,每個索引都會被分成分片,每個分片會有多個副本分片。這些副本稱爲複製組(replication group),在添加或刪除文檔時會保持同步。如果我們同步,從一個副本中讀取將導致與從另一個副本中讀取的結果截然不同的結果。保持副本同步和讀取數據提供服務的過程稱爲數據複製模型。
Elasticsearch的是數據複製模型是基於主從模型的,並在微軟研究院的PacificA論文中有更好的描述。主分片是從複製組中複製的一個副本。其他副本則被稱爲副本分片。主節點是所有索引操作的主要入口點,它負責驗證它們並確保它們是正確的。一旦索引操作被主節點接受,主節點還負責將該操作複製到其他副本。
本節的目的是對Elasticsearch複製模型進行更深的概述,並討論它對寫操作和讀操作之間的各種交互的影響。
基礎的寫模型
Elasticsearch中的每個索引操作首先使用路由(通常基於文檔ID)解析到一個複製組。一旦確定了複製組,操作將在內部轉發到該組的當前主分片。主分片負責驗證操作並將其轉發到其他副本。由於副本可以脫機,所以不需要主分片複製到所有副本。相反,Elasticsearch維護一個應該接收操作的分片副本列表。這個列表稱爲同步副本,由主節點維護,因此必須將所有操作複製到這個副本列表中的每個副本。顧名思義,分片副本保證已經處理了所有已向用戶確認的索引和刪除操作。
主分片遵循以下基本流程:
- 驗證傳入操作並在結構無效時拒絕它,換言之,需要傳入符合結構的參數(例如:在需要一個數字的對象字段中)
- 在本地執行操作,即索引或刪除相關文檔。這還將驗證字段的內容,不符要求會拒絕(例如:關鍵字值太長,無法在Lucene中進行索引)。
- 將操作轉發到當前同步複製集中的每個副本,如果有多個副本,則並行執行。
- 一旦所有副本都成功地執行了操作並響應了主副本,主副本就會向客戶端確認請求成功完成。
失敗處理
在索引期間有很多事情可以導致出錯——磁盤可能會損壞,節點可能會相互斷開連接,或者某些配置錯誤可能導致副本上的操作失敗,儘管它在主服務器上成功。 這些很少見,但主要必須迴應它們。
在主節點本身發生故障的情況下,託管主節點的節點將向主請求發送有關它的消息。 索引操作將等待(默認情況下最多1分鐘),主節點將其中一個副本提升爲新的主節點。 然後,該操作將被轉發到新的主節點處理。 請注意,主節點還會監控節點的運行狀況,並可能決定主動降級主節點。 當通過網絡問題將擁有主節點的節點與羣集隔離時,通常會發生這種情況。 有關詳細信息,請參見此處
一旦在主節點上成功執行了操作,在副本分片上執行該操作時,主節點必須處理潛在的問題。 這可能是由副本上的實際故障或由於網絡問題導致操作無法到達副本(或阻止副本響應)引起的。 所有這些都具有相同的最終結果:作爲同步副本列表的一部分的副本錯過了即將被確認的操作。 爲了避免違反規則,主節點向主請求發送消息,請求從同步副本列表中刪除有問題的分片。 只有在主請求確認刪除了分片後,主請求才會確認操作。 請注意,主節點還將指示另一個節點開始構建新的分片副本,以便將系統恢復到正常狀態。
在將操作轉發到副本時,主節點將使用副本來驗證它仍然是活動的主節點。如果主節點由於網絡分區(或長時間GC)而被隔離,它可能會在意識到已降級之前繼續處理傳入索引操作。來自舊主節點的操作將被副本拒絕。當主節點收到來自副本的響應,拒絕了它的請求(因爲它不再是主節點),那麼主節點就會向主服務器發出請求,並知道它已經被替換了。然後將操作路由到新的主節點。
如果沒有副本會發生什麼?
我們只使用數據的單一副本運行,物理硬件問題可能會導致數據丟失。
基礎的讀模型
Elasticsearch中的讀取可以是ID非常輕量級的查找,也可以是具有複雜聚合的大量搜索請求,這些聚合會佔用非常珍貴的CPU能力。 主從模型的一個優點是它使所有分片副本保持一致(除了正在進行的操作)。 因此,單個同步副本足以提供讀取請求。
當節點接收到讀請求時,該節點負責將其轉發給包含相關分片的節點、整理並響應客戶端。我們將該節點稱爲請求的協調節點。基本流程如下:
- 將讀請求解析到相關分片。注意,由於大多數搜索將被髮送到一個或多個索引,因此它們通常需要從多個分片中讀取,每個分片表示數據的不同子集。
- 從分片複製組中選擇每個相關分片的活動副本。這可以是主分片的,也可以是副本。默認情況下,Elasticsearch只是在分片副本之間進行循環。
- 將分片級別的讀請求發送到所選副本。
- 整合結果並做出響應。注意,在通過ID查找的情況下,一定是隻有一個結果,可以跳過這一步。
(請求)分片失敗
當分片無法響應讀請求時,協調節點將請求發送到相同複製組中的另一個分片副本。重複失敗會導致沒有可用的分片副本。
爲了確保快速響應,如果一個或多個分片失敗,以下api將響應部分結果:
包含部分結果的響應仍然提供一個200 OK HTTP狀態碼。分片故障由響應頭的timed_out
和_shards
字段表示。
一些簡單的含義
這些基本流程中的每一個都決定了Elasticsearch作爲讀寫系統的行爲。此外,由於讀寫請求可以併發執行,所以這兩個基本流彼此交互。這有一些固有的含義:
高效讀
在正常操作下,對每個相關複製組執行一次讀取操作。 只有在失敗條件下,同一個分片的多個副本纔會執行相同的搜索。
讀未應答
由於主節點的第一個索引在本地然後複製請求,因此併發讀取可能在確認之前已經看到了更改。
由於主節點的第一個索引是本地索引,然後複製請求,所以併發讀取可能在確認更改之前就已經看到了更改。
默認兩個分片
這樣可以提高容錯,同時保留兩個數據副本。
失敗
以下情況可能導致失敗:
單個分片可以降低索引速度
由於主節點在每次操作期間都要等待同步副本列表中的所有副本,因此一個慢速副本可能會減慢整個複製組的速度。這是我們爲上面提到的讀取效率所付出的代價。當然,一個單一的慢分片也會減慢已經被路由到它的搜索。
髒讀
一個獨立的主節點暴露寫入,寫入是不會確認的。這是因爲獨立主節點只有在向其副本發送請求或向主節點發出請求時纔會意識到它是隔離的。此時,操作已經被索引到主節點中,並且可以併發讀取。Elasticsearch每秒鐘(默認情況下)ping一次主節點,如果不知道主節點,則拒絕索引操作,從而降低了這種風險。
冰山一角
本文檔提供了更高級的Elasticsearch如何處理數據的預覽,當然,表面之下ES做了很多事情。比如,集羣狀態發佈,主節點選舉等在保證系統正常運行方面發揮了不小的作用。這個文檔沒有涵蓋已知的和重要的bug(關閉的和打開的),爲了幫助人們知道並瞭解,我們在我們的網站上保持一個專門的頁面。我們強烈建議閱讀它。
Index API
添加或更新JSON文檔到指定索引,使該文檔可搜索,下面的例子演示了插入一個文檔到JSON文檔到twitter索引中,並且id爲1:
curl -X PUT "localhost:9200/twitter/_doc/1" -H 'Content-Type: application/json' -d'
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
'
上面索引的操作結果是:
{
"_shards" : {
"total" : 2,
"failed" : 0,
"successful" : 2
},
"_index" : "twitter",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"result" : "created"
}
_shard
提供了關於索引操作的複製過程的信息。
total
表示多少分片需要被執行該索引操作(包含主分片和副本分片)。
successful
表示所有分片中有多少成功的。
failed
表示有多少分片是執行失敗的。
如果索引操作成功,則successful
至少爲1。
當一個索引操作返回成功(默認情況下,只有主分片是必須的,但是此行爲是可以改變的 ),副本分片可能沒有全部開始。在上面的例子中,
total
將等於number_of_replicas
設置的總分片數,successful
將等於已開始的執行的分片數(主+副),如果沒有失敗,則failed
是0.
自動創建索引
如果索引不存在,創建索引操作將會創建一個索引,並應用預配置index模板。如果映射不存在,創建索引操作還會動態創建索引。默認的,如果需要,新字段和對象將會自動的添加到映射定義。有關映射定義的更多信息,請查看映射部分;有關手動更新映射的信息,請參閱Put Mapping API。
自動創建索引是通過action.auto_create_index
設置來控制的,這個設置默認是true
,意味着索引總是會自動創建。只有匹配特定模式的索引才允許自動創建索引,方法是將此設置的值更改爲這些模式的逗號分隔列表。還可以明確的在列表中使用+
或者-
來指定允許或者禁止,要徹底的禁用可以將其設置爲false
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"persistent": {
##只允許自動創建twitter,index10或者以ind開頭的索引,禁止創建以index1開頭的索引
"action.auto_create_index": "twitter,index10,-index1*,+ind*"
}
}
'
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"persistent": {
##完全禁用自動創建索引
"action.auto_create_index": "false"
}
}
'
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"persistent": {
##允許自動創建所有索引
"action.auto_create_index": "true"
}
}
'
操作類型
索引操作還接受op_type
參數,用於強制create
操作,允許省略。當使用了create
,在索引中如果索引中已存在該ID的文檔,索引操作將失敗。
下面有一個例子使用了op_type
參數:
curl -X PUT "localhost:9200/twitter/_doc/1?op_type=create" -H 'Content-Type: application/json' -d'
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
'
指定選項爲create
使用下面的URI:
curl -X PUT "localhost:9200/twitter/_create/1" -H 'Content-Type: application/json' -d'
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
'
自動生成ID
索引操作沒有指定id也可以被執行,下面的例子ID將會自動生成。此外,op_type
將會自動的被設置爲create
,請看下面的例子(注意,是POST而不是PUT):
curl -X POST "localhost:9200/twitter/_doc/" -H 'Content-Type: application/json' -d'
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
'
上面索引操作的結果是:
{
"_shards" : {
"total" : 2,
"failed" : 0,
"successful" : 2
},
"_index" : "twitter",
"_type" : "_doc",
"_id" : "W0tpsmIBdwcYyG50zbta",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"result": "created"
}
樂觀併發控制
索引操作可以是有條件的,只有在爲文檔的最後一次修改分配了if_seq_no
和if_primary_term
參數指定的序列號和主要術語時才能執行索引操作。 如果檢測到不匹配,則操作將導致VersionConflictException和狀態代碼409.有關詳細信息,請參閱樂觀併發控制。
路由
默認情況下,分片放置? 還是路由? 通過使用文檔的id值的哈希來控制。 爲了更明確地控制,可以使用路由參數在每個操作的基礎上直接指定饋入路由器使用的散列函數的值。 例如:
curl -X POST "localhost:9200/twitter/_doc?routing=kimchy" -H 'Content-Type: application/json' -d'
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
'
在上面的例子中,_doc文檔是被路由到routing
提供的參數“kimchy”中,當明確的設置映射,__route字段用來直接的索引操作從自身文檔中來提取路由值,這是以額外的文檔解析傳遞(非常小)的代價實現的。如果routing
映射被定義,並且設置爲required
,如果沒有路由值提供或者可以提取,這個索引操作將失敗。
分發
索引操作根據其路由指向主分片,並在包含此分片的實際節點上執行。主分片完成操作後,如果需要,更新將分發到可應用的副本。
等待活動分片
爲了提高系統寫入的容錯性,索引操作可以配置爲在繼續操作之前等待一定數量的活動分片副本。如果必要數量的活動分片副本不可用,則寫入操作必須等待並重試,直到必要的分片副本已啓動或發生超時。默認的,寫入操作在處理之前,會等待主分片啓動(例如:wait_for_active_shards=1
)。這個默認值可以通過index.write.wait_for_active_shards
來動態的修改此設置。要更改每個操作的此行爲,可以使用wait_for_active_shards
請求參數。
有效值可以是all
也可以是在索引中配置副本節點的每個分片的總數的任意正整數(即number_of_replicas + 1
),指定負數,或者大於該分片副本數的值將引發錯誤。
例如,假設我們有三個節點形成的集羣,A,B和C,創建一個索引index
副本數量設置爲3(產生4個分片副本,比節點多一個副本)。如果我們嘗試一個索引操作,默認的在處理之前,操作只會確保每個分片的主副本可用。這意味着,即使B和C掛掉了,A託管了主分片副本,索引操作仍將僅使用一個數據副本。 如果wait_for_active_shards
被該請求設置爲3(和所有3個節點是正常的),索引操作在處理之前將需要3個活動分片副本,應該滿足這個需求,因爲集羣中有3個活動節點,每個節點都持有切分的副本。但是,如果我們將wait_for_active_shards
設置爲all
(或者設置爲4,兩者是一樣的),索引操作將不會進行,因爲索引中我們並沒有每個活動分片的4個副本。除非集羣中出現一個新節點來承載分片的第四個副本,否則操作將超時。
需要注意的是,這個設置極大地降低了寫操作未寫入到所需分片副本數量的機會,但它並沒有完全消除這種可能性,因爲這個檢查發生在寫操作開始之前。在執行寫操作之後,仍然有可能在任意數量的碎片副本上覆制失敗,但在主副本上仍然成功。寫操作的響應的_shards部分顯示覆製成功/失敗的shard副本的數量。
{
"_shards" : {
"total" : 2,
"failed" : 0,
"successful" : 2
}
}
刷新
控制何時可以搜索到此請求所做的更改。詳情請查看刷新。
Noop更新
當使用索引API更新文檔時,即使文檔沒有更改,也總是會創建文檔的新版本。如果您不能接受這種結果,請使用_update
API,並將detect_noop
設置爲true
。此選項在索引API上不可用,因爲索引API不會獲取舊源,也無法將其與新源進行比較。
關於何時不接受noop更新,沒有一條硬性規定。 它是許多因素的組合,例如您的數據源發送實際noops更新的頻率以及Elasticsearch在接收更新的分片上運行的每秒查詢數。
超時
在執行索引操作時,分配給執行索引操作的主分片可能不可用。其中的一些原因可能是主分片目前正在從網關中恢復或正在進行重新定位。默認情況下,索引操作將等待主分片可用最多1分鐘,然後失敗並響應錯誤。可以使用timeout參數顯式地指定它等待的時間。下面是一個設置爲5分鐘的例子:
curl -X PUT "localhost:9200/twitter/_doc/1?timeout=5m" -H 'Content-Type: application/json' -d'
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
'
版本控制
每個索引文檔都有一個版本號,默認的,每一次更新或者刪除內部版本使用從1自增的數字,當然也可以使用一個外部的值(例如,維護在數據庫中的版本號),要啓用這個功能,設置version_type
爲external
,這個值必須是一個數字,大於或等於0,小於9.2e+18。
當使用外部的版本類型,系統將檢查傳遞給索引請求的版本號是否大於當前存儲文檔的版本,如果是大於,則文檔將被索引,並且生成一個新的版本號來使用。如果提供的值是小於或者等於存儲的文檔版本號,會發生版本衝突,並且索引操作將失敗,例如:
curl -X PUT "localhost:9200/twitter/_doc/1?version=2&version_type=external" -H 'Content-Type: application/json' -d'
{
"message" : "elasticsearch now has versioning support, double cool!"
}
'
版本控制是完全實時的,並且不受搜索操作的近實時方面的影響。如果沒有提供版本,接下來的執行操作不會進行版本檢查。
上面的操作時成功的,因爲提供了版本號爲2,並大於當前的版本號1,如果文檔已經被更新,版本號設置爲2或者更高,索引命令將失敗並導致衝突(409 http狀態碼)。
一個很好的副作用是,只要使用源數據庫的版本號,就不需要維護由於更改源數據庫而執行的異步索引操作的嚴格順序。如果使用外部版本控制,即使使用來自數據庫的數據更新Elasticsearch索引的簡單情況也會簡化,因爲如果索引操作由於某種原因出現順序錯誤,則只使用最新版本。
版本類型
除了上面解釋的external
版本類型之外,Elasticsearch還支持針對特定用例的其他類型。下面是不同版本類型及其語義的概述。
internal
只有當給定的版本與存儲的文檔版本相同時,才索引文檔。
external
or external_gt
只有當給定的版本嚴格高於存儲文檔的版本,或者沒有現有文檔時,纔對文檔進行索引。給定的版本將作爲新版本使用,並與新文檔一起存儲。所提供的版本必須是非負的long number。
external_gte
只有當給定的版本等於或高於存儲文檔的版本時,纔對文檔進行索引。如果沒有現有文檔,操作也將成功。給定的版本將作爲新版本使用,並與新文檔一起存儲。所提供的版本必須是非負的long number。
external_gte版本類型用於特殊的用例,應該謹慎使用。如果使用不當,可能會導致數據丟失。還有另一個選項force,它是不推薦的,因爲它可能導致主f分片和複製分片出現不一致。
GET API
get API允許根據索引的id從索引中獲取JSON文檔。下面的例子展示了從一個索引叫做twitter的並且id等於0的索引中獲取JSON文檔。
curl -X GET "localhost:9200/twitter/_doc/0"
上面的get操作結果是:
{
"_index" : "twitter",
"_type" : "_doc",
"_id" : "0",
"_version" : 1,
"_seq_no" : 10,
"_primary_term" : 1,
"found": true,
"_source" : {
"user" : "kimchy",
"date" : "2009-11-15T14:12:12",
"likes": 0,
"message" : "trying out Elasticsearch"
}
}
上面的結果包括我們希望檢索的文檔的index、id和version,如果可以找到文檔的實際_source,則包含它(如響應中的found字段所示)。
該API還可以使用head請求來檢查文檔是否存在,例如:
curl -I "localhost:9200/twitter/_doc/0"
實時
默認的,get API是實時的,它不受index的刷新率影響(當數據在搜索中可見時),如果文檔已經更新完成但還沒有刷新,get API將發出一個刷新調用,使文檔可見,這也將使上次刷新後其他文檔發生變化。爲了禁用實時GET,可以將realtime
參數設置爲false
。
Source過濾
默認情況下,get操作會返回_source
字段的內容,除非你使用了stroed_fields
參數或者_source
字段是被禁用的。你可以使用_source
參數來關閉_source
檢索。
curl -X GET "localhost:9200/twitter/_doc/0?_source=false"
如果你只需要從完整的_source
中的一個或者兩個字段,您可以使用_source_include
和_source_exclude
參數來包含或過濾掉您需要的部分。這對於部分檢索可以節省網絡開銷的大型文檔尤其有用。這兩個參數都採用逗號分隔的字段列表或通配符表達式。例如:
curl -X GET "localhost:9200/twitter/_doc/0?_source_includes=*.id&_source_excludes=entities"
如果你只指定包含,你可以使用一個更短的符號:
curl -X GET "localhost:9200/twitter/_doc/0?_source=*.id,retweeted"
存儲字段
get操作允許指定一組存儲字段,這些字段將通過傳遞stored_fields
參數返回。如果未存儲所請求的字段,則將忽略它們。例如:
curl -X PUT "localhost:9200/twitter" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"counter": {
"type": "integer",
"store": false
},
"tags": {
"type": "keyword",
"store": true
}
}
}
}
'
現在沒問可以添加一個文檔:
curl -X PUT "localhost:9200/twitter/_doc/1" -H 'Content-Type: application/json' -d'
{
"counter" : 1,
"tags" : ["red"]
}
'
接下來嘗試檢索它:
curl -X GET "localhost:9200/twitter/_doc/1?stored_fields=tags,counter"
上面的結果是:
{
"_index": "twitter",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no" : 22,
"_primary_term" : 1,
"found": true,
"fields": {
"tags": [
"red"
]
}
}
從文檔本身獲取的字段值總是作爲數組返回。由於未存儲counter
字段,因此在嘗試獲取stored_field
時,get請求會忽略它。
也可以檢索像_routing
字段這樣的元數據字段:
curl -X PUT "localhost:9200/twitter/_doc/2?routing=user1" -H 'Content-Type: application/json' -d'
{
"counter" : 1,
"tags" : ["white"]
}
'
curl -X GET "localhost:9200/twitter/_doc/2?routing=user1&stored_fields=tags,counter"
上面的操作結果是:
{
"_index": "twitter",
"_type": "_doc",
"_id": "2",
"_version": 1,
"_seq_no" : 13,
"_primary_term" : 1,
"_routing": "user1",
"found": true,
"fields": {
"tags": [
"white"
]
}
}
此外,只能通過stored_field
選項返回leaf字段。 因此無法返回對象字段,此類請求將失敗。
直接獲取_source
使用/ {index} / _ source / {id}
只獲取文檔的_source
字段,而不包含任何其他內容。 例如:
curl -X GET "localhost:9200/twitter/_source/1"
你還可以使用source過濾參數來控制哪些_source
部分將會被返回:
curl -X GET "localhost:9200/twitter/_source/1/?_source_includes=*.id&_source_excludes=entities"
注意,_source
還有一個HEAD變量,用於有效地測試文檔_source
是否存在。如果在映射中禁用現有文檔的_source
,則該文檔將沒有_source。
curl -I "localhost:9200/twitter/_source/1"
路由
使用控制路由的能力進行索引時,爲了獲取文檔,還應提供路由值。 例如:
curl -X GET "localhost:9200/twitter/_doc/2?routing=user1"
上面的將獲得id爲2的tweet,根據用戶路由。注意,在沒有正確路由的情況下發出get將導致無法獲取文檔。
偏好
控制哪個分片副本執行get請求的偏好。默認情況下,操作是在分片副本間是隨機的。
preference
能被設置成以下值:
_local
如果可能,該操作更喜歡在本地分配的分片上執行。
自定義值
將使用自定義值來保證相同的分片將用於相同的自定義值。 當在不同的刷新狀態下命中不同的分片時,這可以幫助“jumping values”。 示例值可以是Web會話ID或用戶名。
刷新
可以將refresh
參數設置爲true,以便在get
操作之前刷新相關分片並使其可搜索。將其設置爲true應該在仔細考慮並驗證這不會給系統帶來沉重的負載(並降低索引速度)之後執行。
分佈式
get操作被散列到一個特定的shard id
中,然後被重定向到該shard id
中的一個副本,並返回結果。副本是主分片及其在該shard id組中的副本。
版本支持
只有當文檔的當前版本等於指定的版本時,纔可以使用version參數檢索文檔。這種行爲對於所有版本類型都是相同的,除了總是檢索文檔的版本類型FORCE
之外。注意,不推薦使用FORCE
版本類型。
在內部,Elasticsearch將舊文檔標記爲已刪除,並添加了一個全新的文檔。舊版本的文檔不會立即消失,儘管您無法訪問它。當您繼續索引更多數據時,Elasticsearch將清理後臺已刪除的文檔。
Delete API
delete API允許從指定的索引中用索引id刪除JSON文檔。下面的例子展示了刪除一個json文檔從索引叫做twitter並且ID爲1:
curl -X DELETE "localhost:9200/twitter/_doc/1"
上面的操作結果是:
{
"_shards" : {
"total" : 2,
"failed" : 0,
"successful" : 2
},
"_index" : "twitter",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"_primary_term": 1,
"_seq_no": 5,
"result": "deleted"
}
樂觀併發控制
刪除操作可以是有條件的,只有在爲文檔的最後一次修改分配了if_seq_no
和if_primary_term
參數指定的序列號和主要術語時才能執行。 如果檢測到不匹配,則操作將導致VersionConflictException
和狀態代碼409.有關詳細信息,請參閱樂觀併發控制。
版本控制
索引的每個文檔都是版本化的。 刪除文檔時,可以指定版本以確保我們嘗試刪除的相關文檔實際上已被刪除,並且在此期間沒有更改。 對文檔執行的每個寫入操作(包括刪除)都會導致其版本遞增。 刪除文檔的版本號在刪除後仍可短時間使用,以便控制併發操作。 已刪除文檔的版本保持可用的時間長度由index.gc_deletes
索引設置確定,默認爲60秒。
路由
當索引使用控制路由的能力時,爲了刪除文檔,還應該提供路由值。例如:
curl -X DELETE "localhost:9200/twitter/_doc/1?routing=kimchy"
以上將刪除id爲1的tweet ,根據用戶進行路由。 請注意,在沒有正確路由的情況下發出刪除將導致文檔不被刪除。
當_routing
映射根據需要設置且未指定路由值時,delete API將拋出RoutingMissingException
並拒絕該請求。
自動創建索引
如果使用外部版本控制變體(variant),則刪除操作會自動創建索引(如果之前尚未創建)(請查看create index API以手動創建索引)。
分佈式
刪除操作將散列爲特定的分片ID。 然後它被重定向到該id組中的主分片,並複製(如果需要)到該id組內的分片副本。
等待活動分片
在發出刪除請求時,可以設置wait_for_active_shards
參數,以要求在開始處理刪除請求之前激活最少數量的分片副本。有關詳細信息和使用示例,請參見此處。
刷新
控制何時可以搜索到此請求所做的更改。詳情參見?refresh
超時
執行刪除操作時,分配用於執行刪除操作的主分片可能不可用。 造成這種情況的一些原因可能是主分片當前正在從倉庫恢復或正在進行重定位。 默認情況下,刪除操作將在主分片上等待最多1分鐘,然後失敗並響應錯誤。 timeout參數可用於顯式指定等待的時間。 以下是將其設置爲5分鐘的示例:
curl -X DELETE "localhost:9200/twitter/_doc/1?timeout=5m"
通過Query API來刪除
_delete_by_query
的最簡單用法是對查詢匹配到的文檔執行刪除操作。這是API:
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
#查詢必須傳遞一個值給`query`,方法與SearchAPI相同,你也可以使用q參數。
"query": {
"match": {
"message": "some message"
}
}
}
'
響應結果爲:
{
"took" : 147,
"timed_out": false,
"deleted": 119,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"total": 119,
"failures" : [ ]
}
當_delete_by_query
啓動並刪除使用內部版本控制找到的內容時,它會得到一個索引的快照,這意味着,如果在捕獲快照和處理刪除請求之間的文檔發生更改,您將會得到版本衝突。只有文檔匹配的時候,纔會刪除文檔。
因爲``internal
版本控制不支持0作爲有效版本號,版本等於0的文檔不能使用_delete_by_query來刪除,並且請求會失敗。
在執行_delete_by_query
期間,多個搜索請求依次執行,以便找到所有要刪除的匹配文檔。每次找到一批文檔時,都會執行相應的批量請求來刪除這些文檔。方式搜索或者刪除被拒絕,_delete_by_query
依賴一個默認策略來重試被拒絕的請求(最多10次)。到達最大的重試限制,將導致_delete_by_query
終止,失敗信息將在響應中返回。已經執行的操作仍然保持不變。換言之,這個過程是不可逆的,只能終止。在第一次失敗導致終止,失敗的批量請求返回的所有故障都在failure元素中返回,因此,有可能存在相當多的失敗實體。
如果你想計算版本衝突,而不是終止原因,可以在url中設置conflicts=proceed
或者在請求體中添加"conflicts": "proceed"
回到API格式,這將從twitter索引中刪除tweet:
curl -X POST "localhost:9200/twitter/_delete_by_query?conflicts=proceed" -H 'Content-Type: application/json' -d'
{
"query": {
"match_all": {}
}
}
'
也可以同時刪除多個索引的文檔,就像搜索API:
curl -X POST "localhost:9200/twitter,blog/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"query": {
"match_all": {}
}
}
'
如果提供路由,則將路由複製到滾動查詢,將處理過程分配給匹配該路由值的分片:
curl -X POST "localhost:9200/twitter/_delete_by_query?routing=1" -H 'Content-Type: application/json' -d'
{
"query": {
"range" : {
"age" : {
"gte" : 10
}
}
}
}
'
默認情況下,_delete_by_query
使用1000的滾動批次。您可以使用scroll_size URL
參數更改批量大小:
curl -X POST "localhost:9200/twitter/_delete_by_query?scroll_size=5000" -H 'Content-Type: application/json' -d'
{
"query": {
"term": {
"user": "kimchy"
}
}
}
'
URL參數
除了像pretty
這樣的標準參數之外,查詢API的刪除還支持refresh
,wait_for_completion
,wait_for_active_shards
,timeout
和scroll
。
發送refresh
將在請求完成後刷新查詢刪除中涉及的所有分片。 這與delete API的refresh
參數不同,後者只會刷新收到刪除請求的分片。 與delete API不同,它不支持wait_for
。
如果請求包含wait_for_completion = false
,則Elasticsearch將執行一些預檢查,啓動請求,然後返回可與Tasks API一起使用的任務,以取消或獲取任務的狀態。 Elasticsearch還將在.tasks / task / $ {taskId}
中創建此任務的記錄作爲文檔。 這是你的保留或刪除你認爲合適的。 完成後將會刪除它,以便Elasticsearch可以回收它使用的空間。
wait_for_active_shards
控制在繼續請求之前必須激活分片的副本數。 timeout
指示每個寫入請求等待不可用分片可用的時間。 兩者都完全適用於Bulk API中的工作方式。 由於_delete_by_query
使用滾動搜索,您還可以指定scroll
參數來控制search context
保持活動的時間,例如?scroll=10m
。 默認情況下,它是5分鐘。
requests_per_second
可以設置爲任何正十進制數(1.4,6,1000等),並通過在等待時間內填充每個批次來限制查詢刪除發出批量刪除操作的速率。 可以通過將requests_per_second
設置爲-1來禁用限制。
節流是通過在批之間等待來完成的,這樣就可以給_delete_by_query
內部使用的滾動設置一個考慮填充的超時。填充時間是批大小除以requests_per_second
和寫入時間之間的差額。默認情況下批處理大小爲1000,所以如果requests_per_second
被設置爲500:
target_time = 1000 / 500 per second = 2 seconds
wait_time = target_time - write_time = 2 seconds - .5 seconds = 1.5 seconds
由於批處理是作爲單個_bulk
請求發出的,因此較大的批處理將導致Elasticsearch創建許多請求,然後等待一段時間再啓動下一個請求集。這是突發的而不是平滑的。默認值是-1。
響應體
{
"took" : 147,
"timed_out": false,
"total": 119,
"deleted": 119,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"failures" : [ ]
}
took
整個操作從開始到結束的毫秒數。
time_out
如果在delete by query執行期間執行的任何請求超時,則將此標誌設置爲true。
total
成功執行的文檔總數
deleted
成功刪除的文檔總數
batches
通過查詢刪除回滾的滾動響應數。
version_conflicts
按查詢刪除所命中的版本衝突數。
noops
對於delete by query,這個字段總是等於零。它的存在只是爲了讓delete by query、update by query和reindex api返回具有相同結構的響應。
retries
按查詢刪除所嘗試的重試次數。bulk是重試的批量操作的數量,search是重試的搜索操作的數量。
throttled_millis
符合requests_per_second
的請求毫秒數
requests_per_second
在delete by query期間每秒有效執行的請求數。
throttled_until_millis
在_delete_by_query
響應中,此字段應始終等於零。 它只在使用Task API時有意義,它表示下一次(自epoch以來的毫秒數),爲了符合requests_per_second
,將再次執行受限制的請求。
failures
如果流程中存在任何不可恢復的錯誤,則會出現一系列故障。如果這不是空的,那麼請求就會因爲這些失敗而中止。使用batch實現Delete by query,任何失敗都會導致整個進程中止,但是當前batch中的所有失敗都會收集到數組中。您可以使用conflicts
選項來防止reindex在版本衝突上中止。
使用Task API
你可以使用Task API來獲取運行delete by query請求的狀態:
curl -X GET "localhost:9200/_tasks?detailed=true&actions=*/delete/byquery"
響應結果:
{
"nodes" : {
"r1A2WoRbTwKZ516z6NEs5A" : {
"name" : "r1A2WoR",
"transport_address" : "127.0.0.1:9300",
"host" : "127.0.0.1",
"ip" : "127.0.0.1:9300",
"attributes" : {
"testattr" : "test",
"portsfile" : "true"
},
"tasks" : {
"r1A2WoRbTwKZ516z6NEs5A:36619" : {
"node" : "r1A2WoRbTwKZ516z6NEs5A",
"id" : 36619,
"type" : "transport",
"action" : "indices:data/write/delete/byquery",
"status" : {
"total" : 6154,
"updated" : 0,
"created" : 0,
"deleted" : 3500,
"batches" : 36,
"version_conflicts" : 0,
"noops" : 0,
"retries": 0,
"throttled_millis": 0
},
"description" : ""
}
}
}
}
}
該對象包含實際狀態。 它就像響應JSON一樣,增加了total
字段。 total
是reindex
期望執行的操作總數。 您可以通過添加updated
,created
和deleted
的字段來估計進度。 當它們的總和等於total
字段時,請求將結束。
通過Task id可以直接查看Task:
curl -X GET "localhost:9200/_tasks/r1A2WoRbTwKZ516z6NEs5A:36619"
此API的優點是它與wait_for_completion = false
集成,透明地返回已完成任務的狀態。 如果任務完成並且在其上設置了wait_for_completion = false
,那麼它將返回result
或error
字段。 此功能的代價是wait_for_completion = false
在.tasks / task / $ {taskId}
創建的文檔。 您可以刪除該文檔。
使用Cancel Tash API
所有的delete by query能通過task cancel API被關閉:
curl -X POST "localhost:9200/_tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel"
Task ID可以通過task API來找到。
取消操作應該快速的發生,但是有時可能會幾秒。上面的任務狀態API將繼續按查詢任務列出delete,直到該任務檢查它是否已被取消並自行終止。
Rethrottling
可以使用_rethrottle API
通過查詢在正在運行的刪除時更改requests_per_second
的值:
curl -X POST "localhost:9200/_delete_by_query/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second=-1"
通過Task id可以直接查看Task.
就像在查詢API的刪除中設置它一樣,requests_per_second
可以是-1來禁用限制,也可以是1.7或12之類的任何十進制數來限制到該級別。 加速查詢的Rethrottling會立即生效,但重新啓動會減慢查詢速度,這將在完成當前批處理後生效。 這可以防止滾動超時。
切分請求
按查詢刪除支持分割滾動以並行化刪除過程。 這種並行化可以提高效率,並提供一種方便的方法將請求分解爲更小的部分。
手動切分
通過對每個請求提供一個切分id和切分的總數來手動的切分delete by query
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 0,
"max": 2
},
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 1,
"max": 2
},
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
你可以驗證任務:
curl -X GET "localhost:9200/_refresh"
curl -X POST "localhost:9200/twitter/_search?size=0&filter_path=hits.total" -H 'Content-Type: application/json' -d'
{
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
結果是這樣一個合理的total
數:
{
"hits": {
"total" : {
"value": 0,
"relation": "eq"
}
}
}
自動切分
您還可以讓delete-by-query使用切片滾動自動並行化以在_id上切片。 使用切片指定要使用的切片數:
curl -X POST "localhost:9200/twitter/_delete_by_query?refresh&slices=5" -H 'Content-Type: application/json' -d'
{
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
你可以驗證任務:
curl -X POST "localhost:9200/twitter/_search?size=0&filter_path=hits.total" -H 'Content-Type: application/json' -d'
{
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
結果是這樣一個合理的total
數:
{
"hits": {
"total" : {
"value": 0,
"relation": "eq"
}
}
}
將slices
設置爲auto
將允許Elasticsearch選擇要使用的切分數。 此設置將使用每個分片一個切片,達到一定限制。 如果有多個源索引,它將根據具有最小分片數的索引選擇切片數。
將slices
添加到_delete_by_query只會自動執行上一節中使用的手動過程,創建子請求,這意味着它有一點奇怪:
- 你可以在Task API中查看這些請求。這些子請求切分請求的任務的“子”任務。
- 爲切分的請求獲取任務狀態只包含已完成片的狀態。
- 這些子請求可以單獨尋址,用於取消和重新節流等操作。
- 用
slices
重新節流請求將按比例重新節流未完成的子請求。 - 用
slices
取消請求將取消每個子請求。 - 由於
slices
的性質,每個子請求不會得到均勻的處理分佈。所有文檔都會被處理,但是子請求的處理可能比子請求處理更多。期望更大的片具有更均勻的分佈。 - 帶
slices
的請求上的requests_per_second
和size
等參數按比例分佈到每個子請求。結合上面關於分佈不均勻的觀點,您應該得出結論:使用帶slices
的size
可能不會導致確切大小的文檔被刪除。 - 每個子請求都獲得源索引的稍微不同的快照,儘管這些快照幾乎是同時獲取的。
選擇切分的數量
如果自動切片,將slices
設置爲auto將爲大多數索引選擇一個合理的數字。如果您正在手動切片或以其他方式調優自動切片,請使用以下指南。
當slices
數等於索引中的分片數時,查詢性能最有效。 如果該數字很大(例如,500),請選擇較小的數字,因爲太多的slices
會損害性能。 設置高於分片數量的slices
通常不會提高效率並增加開銷。
刪除性能隨着slices
的數量在可用資源之間線性擴展。
查詢或刪除性能是否在運行時占主導地位取決於重新索引的文檔和集羣資源。
Update API
update API允許基於提供的腳本更新文檔。該操作從索引中獲取文檔(與切分並列),運行腳本(使用可選的腳本語言和參數),索引返回結果(還允許刪除或忽略該操作)。它使用版本控制來確保在“get”和“reindex”期間沒有發生更新。
注意,這個操作仍然意味着文檔的完全重索引,它只是刪除了一些網絡往返,並減少了get和index之間版本衝突的機會。需要啓用_source字段才能使此功能正常工作。
讓我們索引一個簡單的文檔:
curl -X PUT "localhost:9200/test/_doc/1" -H 'Content-Type: application/json' -d'
{
"counter" : 1,
"tags" : ["red"]
}
'
腳本更新
現在我們可以執行一個腳本來增加計數:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
}
}
'
我們可以添加一個標籤到標籤列表:(如果標籤存在,它仍會被添加,因爲這是一個列表)
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "ctx._source.tags.add(params.tag)",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
'
我們也可以移除一個標籤從標籤列表。注意,用於刪除標記的Painless 函數將希望刪除的元素的數組索引作爲參數,因此需要更多的邏輯來定位它,避免運行時錯誤。注意,如果標籤在列表中出現超過一次,則只會刪除一次:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
'
除了_source
之外,通過ctx
map可以獲得以下變量:_index
,_type
,_id
,_version
,_routing
和_now
(當前時間戳)。
也可以添加一個新字段到文檔:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : "ctx._source.new_field = \u0027value_of_new_field\u0027"
}
'
或者從文檔中移除一個字段:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : "ctx._source.remove(\u0027new_field\u0027)"
}
'
甚至我們可以改變已執行的操作。這個例子如果tags
字段包含green
,則會刪除文檔,否則不會有任何操作。(noop
)
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = \u0027delete\u0027 } else { ctx.op = \u0027none\u0027 }",
"lang": "painless",
"params" : {
"tag" : "green"
}
}
}
'
使用部分文檔更新
update API也支持傳入部分文檔,它將被合併到現有的文檔中(簡單的遞歸合併、對象的內部合併、替換核心“key/value”和數組)。要完全替換現有文檔,應該使用IndexAPI。以下部分更新向現有文檔添加了一個新字段:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"doc" : {
"name" : "new_name"
}
}
'
如果doc
和script
都被指定了,doc
將會被忽略。最好是將部分文檔的字段對放在腳本中。
檢測等待更新
如果doc
被指定,並且值將與現有的_source
合併。默認情況下,不改變任何東西的更新檢測它們不改變任何東西,並返回“result”:“noop”,如下所示:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"doc" : {
"name" : "new_name"
}
}
'
如果在發送請求之前name
爲new_name
,則忽略整個更新請求。 如果忽略請求,響應中的result元素將返回noop。
{
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
},
"_index": "test",
"_type": "_doc",
"_id": "1",
"_version": 7,
"result": "noop"
}
你可以通過"detect_noop": false
來禁用這個行爲:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"doc" : {
"name" : "new_name"
},
"detect_noop": false
}
'
Upserts
如果文檔不存在,upsert元素的內容將作爲新文檔插入。如果文檔確實存在,則執行腳本:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
},
"upsert" : {
"counter" : 1
}
}
'
scripted_upsert
如果您希望腳本運行,無論文檔是否存在 - 即腳本處理初始化文檔而不是upsert元素 - 將scripted_upsert
設置爲true
:
curl -X POST "localhost:9200/sessions/_update/dh3sgudg8gsrgl" -H 'Content-Type: application/json' -d'
{
"scripted_upsert":true,
"script" : {
"id": "my_web_session_summariser",
"params" : {
"pageViewEvent" : {
"url":"foo.com/bar",
"response":404,
"time":"2014-01-01 12:32"
}
}
},
"upsert" : {}
}
'
doc_as_upsert
將doc_as_upsert
設置爲true
將使用doc的內容作爲upsert值,而不是發送部分doc加上upsert文檔:
curl -X POST "localhost:9200/test/_update/1" -H 'Content-Type: application/json' -d'
{
"doc" : {
"name" : "new_name"
},
"doc_as_upsert" : true
}
'
參數
update操作支持下面幾個參數:
retry_on_conflict
在更新的get和indexing 階段之間,另一個進程可能已經更新了相同的文檔。默認情況下,更新將失敗,出現版本衝突異常。retry_on_conflict
參數控制在最終拋出異常之前重試更新的次數。
routing
路由用於將更新請求路由到正確的分片,並在更新的文檔不存在時爲upsert請求設置路由。 不能用於更新現有文檔的路由。
timeout
分片變成可用狀態的超時等待時間。
wait_for_active_shards
在繼續更新操作之前需要處於活動狀態的分片副本數。
refresh
控制何時此請求所做的更改對搜索可見。
_source
允許控制是否以及如何在響應中返回更新的源。 默認情況下,不會返回更新的源。
version
更新API在內部使用Elasticsearch版本控制支持,以確保在更新期間文檔不會更改。 您可以使用version參數指定僅在文檔版本與指定版本匹配時才更新文檔。
更新API不支持內部版本以外的版本控制
更新API不支持外部(版本類型external和external_gte)或強制(版本類型強制)版本控制,因爲它會導致Elasticsearch版本號與外部系統不同步。 請改用index API。
if_seq_no
和 if_primary_term
更新操作可以是有條件的,只有在爲文檔的最後一次修改分配了if_seq_no
和if_primary_term
參數指定的值時才能執行。 如果檢測到不匹配,則操作將導致VersionConflictException
和狀態代碼409。
通過Query API來更新
_update_by_query
的最簡單用法只是對索引中的每個文檔執行更新而不更改源。 這對於獲取新屬性或其他一些在線映射更改很有用。 下面是API:
curl -X POST "localhost:9200/twitter/_update_by_query?conflicts=proceed"
返回的結果爲:
{
"took" : 147,
"timed_out": false,
"updated": 120,
"deleted": 0,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"total": 120,
"failures" : [ ]
}
_update_by_query
在索引啓動時獲取索引的快照,並使用內部版本控制索引它的內容。這意味着如果文檔在拍攝快照的時間和處理索引請求之間發生更改,則會出現版本衝突。如果版本匹配,文檔會被更新,並且版本號會增加。
因爲
internal
版本控制不支持0作爲有效的版本號,文檔的版本號等於0不能使用_update_by_query
來更新,這樣的請求會失敗。
由於_update_by_query
終止而導致的查詢和更新失敗,會在響應體裏返回一個failures
字段。已經執行的更新仍然有效。換句話說,這個過程沒有回滾,只能中止。當第一個故障導致中止時,失敗的批量請求返回的所有故障都將在failure
元素中返回,因此,可能存在相當多的失敗實體。
如果只想簡單的計算版本衝突,並且不是由於_update_by_query
引起的中止,你可以設置在url中設置conflicts=proceed
,或者在請求體中"conflicts": "proceed"
。第一個示例之所以這樣做,是因爲它只是試圖獲取一個在線映射更改,而版本衝突僅僅意味着在_update_by_query
的開始時間和它試圖更新文檔的時間之間更新了衝突文檔。這是有用的,因爲更新將獲得在線映射更新。
再回到API,這將從twitter
索引更新到tweets:
curl -X POST "localhost:9200/twitter/_update_by_query?conflicts=proceed"
你還可以使用Query DSL來對_update_by_query
進行限制。下面的例子將會爲用戶kimchy
更新twitter
中的所有文檔:
curl -X POST "localhost:9200/twitter/_update_by_query?conflicts=proceed" -H 'Content-Type: application/json' -d'
{
#查詢必須傳入一個query的key,或者也可以傳入一個q,就像SearchApi一樣
"query": {
"term": {
"user": "kimchy"
}
}
}
'
到目前爲止,我們只更新了文檔,而沒有更改它們的source。這確實對添加新屬性很有用。_update_by_query支持更新文檔的腳本。這將增加kimchy所有tweet上的like字段:
curl -X POST "localhost:9200/twitter/_update_by_query" -H 'Content-Type: application/json' -d'
{
"script": {
"source": "ctx._source.likes++",
"lang": "painless"
},
"query": {
"term": {
"user": "kimchy"
}
}
}
'
就像在Update API中一樣,您可以設置ctx.op
更改執行的操作:
noop |
如果你的腳本不作任何更改,可以設置ctx.op=“noop”。這將導致_update_by_query 從其更新中省略該文檔。noop操作將在響應體中的noop 計數器中展示。 |
---|---|
delete |
如果你的腳本必須刪除,可以設置ctx.op="delete" 。刪除操作將在響應體中的deleted 計數器中展示。 |
將ctx.op設置爲其他任何內容都是錯誤的。 在ctx中設置任何其他字段是錯誤的。
請注意,我們停止指定conflict = proceed
。 在這種情況下,我們希望版本衝突中止該過程,以便我們可以處理失敗。
此API不允許您改變它關聯的文檔,只需修改它們的source。 這是故意的! 我們沒有規定從原始位置刪除文檔。
也可以在多個索引上同時完成這一切,就像搜索API:
curl -X POST "localhost:9200/twitter,blog/_update_by_query"
如果提供routing
,則將路由複製到滾動查詢,將過程限制爲匹配該路由值的分片:
curl -X POST "localhost:9200/twitter/_update_by_query?routing=1"
默認情況下,_update_by_query
使用滾動批次爲1000。您可以使用scroll_size
URL參數更改批大小:
curl -X POST "localhost:9200/twitter/_update_by_query?scroll_size=100"
_update_by_query也可以通過指定管道來使用Ingest節點功能:
curl -X PUT "localhost:9200/_ingest/pipeline/set-foo" -H 'Content-Type: application/json' -d'
{
"description" : "sets foo",
"processors" : [ {
"set" : {
"field": "foo",
"value": "bar"
}
} ]
}
'
curl -X POST "localhost:9200/twitter/_update_by_query?pipeline=set-foo"
URL參數
除了像pretty
這樣的標準參數之外,查詢API的刪除還支持refresh
,wait_for_completion
,wait_for_active_shards
,timeout
和scroll
。
發送refresh
將在請求完成後刷新查詢刪除中涉及的所有分片。 這與delete API的refresh
參數不同,後者只會刷新收到刪除請求的分片。 與delete API不同,它不支持wait_for
。
如果請求包含wait_for_completion = false
,則Elasticsearch將執行一些預檢查,啓動請求,然後返回可與Tasks API一起使用的任務,以取消或獲取任務的狀態。 Elasticsearch還將在.tasks / task / $ {taskId}
中創建此任務的記錄作爲文檔。 這是你的保留或刪除你認爲合適的。 完成後將會刪除它,以便Elasticsearch可以回收它使用的空間。
wait_for_active_shards
控制在繼續請求之前必須激活分片的副本數。 timeout
指示每個寫入請求等待不可用分片可用的時間。 兩者都完全適用於Bulk API中的工作方式。 由於_delete_by_query
使用滾動搜索,您還可以指定scroll
參數來控制search context
保持活動的時間,例如?scroll=10m
。 默認情況下,它是5分鐘。
requests_per_second
可以設置爲任何正十進制數(1.4,6,1000等),並通過在等待時間內填充每個批次來限制查詢刪除發出批量刪除操作的速率。 可以通過將requests_per_second
設置爲-1來禁用限制。
節流是通過在批之間等待來完成的,這樣就可以給_delete_by_query
內部使用的滾動設置一個考慮填充的超時。填充時間是批大小除以requests_per_second
和寫入時間之間的差額。默認情況下批處理大小爲1000,所以如果requests_per_second
被設置爲500:
target_time = 1000 / 500 per second = 2 seconds
wait_time = target_time - write_time = 2 seconds - .5 seconds = 1.5 seconds
由於批處理是作爲單個_bulk
請求發出的,因此較大的批處理將導致Elasticsearch創建許多請求,然後等待一段時間再啓動下一個請求集。這是突發的而不是平滑的。默認值是-1。
響應體
響應結果就像下面的那樣:
{
"took" : 147,
"timed_out": false,
"total": 5,
"updated": 5,
"deleted": 0,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"failures" : [ ]
}
took
整個操作從開始到結束的毫秒數。
time_out
如果在update by query執行期間執行的任何請求超時,則將此標誌設置爲true。
total
成功執行的文檔總數
updated
成功更新的文檔總數
deleted
成功刪除的文檔總數
batches
通過查詢更新回滾的滾動響應數。
version_conflicts
按查詢更新所命中的版本衝突數。
noops
由於用於update by query的腳本返回了ctx.op的noop值而被忽略的文檔數量。
retries
按查詢更新所嘗試的重試次數。bulk是重試的批量操作的數量,search是重試的搜索操作的數量。
throttled_millis
符合requests_per_second
的請求毫秒數
requests_per_second
在update by query期間每秒有效執行的請求數。
throttled_until_millis
在_update_by_query
響應中,此字段應始終等於零。 它只在使用Task API時有意義,它表示下一次(自epoch以來的毫秒數),爲了符合requests_per_second
,將再次執行受限制的請求。
failures
如果流程中存在任何不可恢復的錯誤,則會出現一系列故障。如果這不是空的,那麼請求就會因爲這些失敗而中止。使用batch實現update by query,任何失敗都會導致整個進程中止,但是當前batch中的所有失敗都會收集到數組中。您可以使用conflicts
選項來防止reindex在版本衝突上中止。
使用Task API
你可以使用Task API來獲取運行delete by query請求的狀態:
curl -X GET "localhost:9200/_tasks?detailed=true&actions=*/delete/byquery"
響應結果:
{
"nodes" : {
"r1A2WoRbTwKZ516z6NEs5A" : {
"name" : "r1A2WoR",
"transport_address" : "127.0.0.1:9300",
"host" : "127.0.0.1",
"ip" : "127.0.0.1:9300",
"attributes" : {
"testattr" : "test",
"portsfile" : "true"
},
"tasks" : {
"r1A2WoRbTwKZ516z6NEs5A:36619" : {
"node" : "r1A2WoRbTwKZ516z6NEs5A",
"id" : 36619,
"type" : "transport",
"action" : "indices:data/write/delete/byquery",
"status" : {
"total" : 6154,
"updated" : 0,
"created" : 0,
"deleted" : 3500,
"batches" : 36,
"version_conflicts" : 0,
"noops" : 0,
"retries": 0,
"throttled_millis": 0
},
"description" : ""
}
}
}
}
}
該對象包含實際狀態。 它就像響應JSON一樣,增加了total
字段。 total
是reindex
期望執行的操作總數。 您可以通過添加updated
,created
和deleted
的字段來估計進度。 當它們的總和等於total
字段時,請求將結束。
通過Task id可以直接查看Task:
curl -X GET "localhost:9200/_tasks/r1A2WoRbTwKZ516z6NEs5A:36619"
此API的優點是它與wait_for_completion = false
集成,透明地返回已完成任務的狀態。 如果任務完成並且在其上設置了wait_for_completion = false
,那麼它將返回result
或error
字段。 此功能的代價是wait_for_completion = false
在.tasks / task / $ {taskId}
創建的文檔。 您可以刪除該文檔。
使用Cancel Tash API
所有的update by query能通過task cancel API被關閉:
curl -X POST "localhost:9200/_tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel"
Task ID可以通過task API來找到。
取消操作應該快速的發生,但是有時可能會幾秒。上面的任務狀態API將繼續按查詢任務列出delete,直到該任務檢查它是否已被取消並自行終止。
Rethrottling
可以使用_rethrottle API
通過查詢在正在運行的刪除時更改requests_per_second
的值:
curl -X POST "localhost:9200/_update_by_query/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second=-1"
通過Task id可以直接查看Task.
就像在查詢API的刪除中設置它一樣,requests_per_second
可以是-1來禁用限制,也可以是1.7或12之類的任何十進制數來限制到該級別。 加速查詢的Rethrottling會立即生效,但重新啓動會減慢查詢速度,這將在完成當前批處理後生效。 這可以防止滾動超時。
切分請求
按查詢更新支持分割滾動以並行化更新過程。 這種並行化可以提高效率,並提供一種方便的方法將請求分解爲更小的部分。
手動切分
通過對每個請求提供一個切分id和切分的總數來手動的切分update by query
curl -X POST "localhost:9200/twitter/_update_by_query" -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 0,
"max": 2
},
"script": {
"source": "ctx._source[\u0027extra\u0027] = \u0027test\u0027"
}
}
'
curl -X POST "localhost:9200/twitter/_update_by_query" -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 1,
"max": 2
},
"script": {
"source": "ctx._source[\u0027extra\u0027] = \u0027test\u0027"
}
}
'
你可以驗證任務:
curl -X GET "localhost:9200/_refresh"
curl -X POST "localhost:9200/twitter/_search?size=0&q=extra:test&filter_path=hits.total"
結果是這樣一個合理的total
數:
{
"hits": {
"total": {
"value": 120,
"relation": "eq"
}
}
}
自動切分
您還可以讓update-by-query使用切片滾動自動並行化以在_id上切片。 使用切片指定要使用的切片數:
curl -X POST "localhost:9200/twitter/_update_by_query?refresh&slices=5" -H 'Content-Type: application/json' -d'
{
"script": {
"source": "ctx._source[\u0027extra\u0027] = \u0027test\u0027"
}
}
'
你可以驗證任務:
curl -X POST "localhost:9200/twitter/_search?size=0&q=extra:test&filter_path=hits.total"
結果是這樣一個合理的total
數:
{
"hits": {
"total": {
"value": 120,
"relation": "eq"
}
}
}
將slices
設置爲auto
將允許Elasticsearch選擇要使用的切分數。 此設置將使用每個分片一個切片,達到一定限制。 如果有多個源索引,它將根據具有最小分片數的索引選擇切片數。
將slices
添加到_delete_by_query只會自動執行上一節中使用的手動過程,創建子請求,這意味着它有一點奇怪:
- 你可以在Task API中查看這些請求。這些子請求切分請求的任務的“子”任務。
- 爲切分的請求獲取任務狀態只包含已完成片的狀態。
- 這些子請求可以單獨尋址,用於取消和重新節流等操作。
- 用
slices
重新節流請求將按比例重新節流未完成的子請求。 - 用
slices
取消請求將取消每個子請求。 - 由於
slices
的性質,每個子請求不會得到均勻的處理分佈。所有文檔都會被處理,但是子請求的處理可能比子請求處理更多。期望更大的片具有更均勻的分佈。 - 帶
slices
的請求上的requests_per_second
和size
等參數按比例分佈到每個子請求。結合上面關於分佈不均勻的觀點,您應該得出結論:使用帶slices
的size
可能不會導致確切大小的文檔被刪除。 - 每個子請求都獲得源索引的稍微不同的快照,儘管這些快照幾乎是同時獲取的。
選擇切分的數量
如果自動切片,將slices
設置爲auto將爲大多數索引選擇一個合理的數字。如果您正在手動切片或以其他方式調優自動切片,請使用以下指南。
當slices
數等於索引中的分片數時,查詢性能最有效。 如果該數字很大(例如,500),請選擇較小的數字,因爲太多的slices
會損害性能。 設置高於分片數量的slices
通常不會提高效率並增加開銷。
更新性能隨着slices
的數量在可用資源之間線性擴展。
查詢或更新性能是否在運行時占主導地位取決於重新索引的文檔和集羣資源。
獲取新屬性
假設您創建了一個沒有動態映射的索引,用數據填充它,然後添加一個映射值來從數據中獲取更多字段:
curl -X PUT "localhost:9200/test" -H 'Content-Type: application/json' -d'
{
"mappings": {
#意味着新字段不會被索引,只會存儲在_source中
"dynamic": false,
"properties": {
"text": {"type": "text"}
}
}
}
'
curl -X POST "localhost:9200/test/_doc?refresh" -H 'Content-Type: application/json' -d'
{
"text": "words words",
"flag": "bar"
}
'
curl -X POST "localhost:9200/test/_doc?refresh" -H 'Content-Type: application/json' -d'
{
"text": "words words",
"flag": "foo"
}
'
# 這個更新映射到新添加的flag字段,要獲取新字段,您必須使用它重新索引所有文檔。
curl -X PUT "localhost:9200/test/_mapping" -H 'Content-Type: application/json' -d'
{
"properties": {
"text": {"type": "text"},
"flag": {"type": "text", "analyzer": "keyword"}
}
}
'
使用flag字段搜索不會找到內容:
curl -X POST "localhost:9200/test/_search?filter_path=hits.total" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"flag": "foo"
}
}
}
'
響應結果:
{
"hits" : {
"total": {
"value": 0,
"relation": "eq"
}
}
}
但是你可以發出一個_update_by_query
請求來獲取新映射:
curl -X POST "localhost:9200/test/_update_by_query?refresh&conflicts=proceed"
curl -X POST "localhost:9200/test/_search?filter_path=hits.total" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"flag": "foo"
}
}
}
'
{
"hits" : {
"total": {
"value": 1,
"relation": "eq"
}
}
}
在向多字段添加字段時,您可以執行完全相同的操作。
Multi GET API
Multi get API基於索引,類型,(可選)和id(或者路由)返回多個文檔。響應包括一個docs
數組,其中包含所有獲取的文檔,按照multi-get請求對應的順序排列(如果某個特定get出現失敗,則在響應中包含一個包含此錯誤的對象)。成功get的結構在結構上類似於get API提供的文檔。
下面是例子:
curl -X GET "localhost:9200/_mget" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1"
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2"
}
]
}
'
mget
端點也可以用於索引(在這種情況下,在請求體中不需要它):
curl -X GET "localhost:9200/test/_mget" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_type" : "_doc",
"_id" : "1"
},
{
"_type" : "_doc",
"_id" : "2"
}
]
}
'
用於類型:
curl -X GET "localhost:9200/test/_doc/_mget" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_id" : "1"
},
{
"_id" : "2"
}
]
}
'
在這個例子中,ids
元素可以直接用來簡化請求:
curl -X GET "localhost:9200/test/_doc/_mget" -H 'Content-Type: application/json' -d'
{
"ids" : ["1", "2"]
}
'
過濾Source
默認的,_source
會被每個文檔返回(如果存儲),類似get API,可以使用_source
參數只檢索_source
的一部分(或者根本不檢索)。你還可以使用url參數,_source
,_source_includes
,_source_excludes
,來指定默認值。當沒有針對每個文檔的指令時,會使用默認值。
例如:
curl -X GET "localhost:9200/_mget" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_source" : false
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2",
"_source" : ["field3", "field4"]
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "3",
"_source" : {
"include": ["user"],
"exclude": ["user.location"]
}
}
]
}
'
字段
可以爲每個要獲取的文檔指定要檢索的特定存儲字段,類似於get API的stored_fields
參數。例如:
curl -X GET "localhost:9200/_mget" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"stored_fields" : ["field1", "field2"]
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2",
"stored_fields" : ["field3", "field4"]
}
]
}
'
或者,您可以在查詢字符串中指定stored_fields
參數作爲應用於所有文檔的默認值。
curl -X GET "localhost:9200/test/_doc/_mget?stored_fields=field1,field2" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
# 返回field1和field2
"_id" : "1"
},
{
# 返回field3和field4
"_id" : "2",
"stored_fields" : ["field3", "field4"]
}
]
}
'
路由
你還可以指定一個路由值來作爲參數:
curl -X GET "localhost:9200/_mget?routing=key1" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"routing" : "key2"
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2"
}
]
}
'
在此示例中,將從對應於路由密鑰key1的分片中提取文檔test / _doc / 2,但是將從對應於路由密鑰key2的分片中提取文檔test / _doc / 1。
在本例中,文檔test / _doc / 2
將從與路由鍵key1
對應的切分中獲取,但是文檔`test / _doc / 1將從與路由鍵key2對應的切分中獲取。
部分響應
爲確保快速響應,如果一個或多個分片失敗,多重獲取API將響應部分結果。 有關更多信息,請參閱Shard故障。
Bulk API
批量API可以在一個API調用中執行許多索引/刪除操作,這可以大大提高索引速度。
/_bulk
是其路徑,並且使用換行符來分割JSON結構。
action_and_meta_data\n
optional_source\n
action_and_meta_data\n
optional_source\n
....
action_and_meta_data\n
optional_source\n
注意:
最後一行數據必須以換行符\ n結尾。 每個換行符前面都有一個回車符\ r \ n。 向此路徑發送請求時,Content-Type標頭應設置爲application / x-ndjson。
總共有4個動作,分別是index
,create
,delete
,update
,index
和create
期望下一行的source,並且與標準index API的op_type
參數具有相同的語義(即,如果已存在具有相同索引的文檔,則create將失敗,而index將根據需要添加或替換文檔)。 delete
不期望下一行中的source,並且具有與標準刪除API相同的語義。 update
期望在下一行指定部分doc,upsert和script及其選項。
如果你提供了text文件輸入到curl
,你必須使用--data-binary
標識而不是普通的-d
,後面不允許保留新行,例如:
$ cat requests
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
$ curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary "@requests"; echo
{"took":7, "errors": false, "items":[{"index":{"_index":"test","_type":"_doc","_id":"1","_version":1,"result":"created","forced_refresh":false}}]}
因爲這個格式使用的是字面意義的\n
‘s作爲分隔符,請確保JSON操作和源代碼沒有被很好地打印出來。下面是一個正確的批量命令序列的例子:
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
'
這個批量操作的結果是:
{
"took": 30,
"errors": false,
"items": [
{
"index": {
"_index": "test",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 201,
"_seq_no" : 0,
"_primary_term": 1
}
},
{
"delete": {
"_index": "test",
"_type": "_doc",
"_id": "2",
"_version": 1,
"result": "not_found",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 404,
"_seq_no" : 1,
"_primary_term" : 2
}
},
{
"create": {
"_index": "test",
"_type": "_doc",
"_id": "3",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 201,
"_seq_no" : 2,
"_primary_term" : 3
}
},
{
"update": {
"_index": "test",
"_type": "_doc",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 200,
"_seq_no" : 3,
"_primary_term" : 4
}
}
]
}
路徑是/_bulk
和/{index}/_bulk
。提供索引時,默認情況下,索引將用於未顯式提供索引的Bulk。
格式說明,這裏的想法是讓處理過程儘可能快。由於一些操作將被重定向到其他節點上的其他切分,因此只有action_meta_data
在接收節點端被解析。
使用此協議的客戶端庫應該嘗試在客戶端嘗試類似的操作,並儘可能減少緩衝。
批量操作的結果是一個大的JSON結果數據,每個操作結果的順序和請求中的順序是一致的。 單個操作的失敗不會影響剩餘的操作。
在單個批量調用中沒有要執行的操作的“correct”數量。您應該嘗試不同的設置,以找到適合您特定工作負載的最佳大小。
如果使用HTTP API,請確保客戶機不發送HTTP chunks,因爲這會降低速度。
樂觀併發控制
批量API調用中的每個index
和delete
操作可以在其各自的操作和元數據行中添加if_seq_no
和if_primary_term
參數。 if_seq_no
和if_primary_term
參數根據對現有文檔的最後修改來控制操作的執行方式。 有關更多詳細信息,請參閱樂觀併發控制。
版本控制
每個bulk項都可以使用version
字段引入版本值。 它會根據_version
映射自動跟蹤index/delete操作的行爲。 它還支持version_type
。
路由
每個bulk項都可以使用routing
字段來引入路由值,它自動遵循基於_routing
映射的index/delete操作的行爲。
等待活動分區
在進行批量調用時,可以將wait_for_active_shards
參數設置爲在開始處理批量請求之前需要激活最少數量的碎片副本。
刷新
控制何時此請求所做的更改對搜索可見。
只有收到bulk請求的分片纔會受到刷新的影響。 想象一下
_bulk?refresh = wait_for
請求,其中包含三個文檔,這些文檔恰好被路由到具有五個分片的索引中的不同分片。 請求只會等待這三個分片刷新。 構成索引的其他兩個分片根本不參與_bulk請求。
更新
使用update
操作時,retry_on_conflict
可用作操作本身的字段(不在額外的有效payload line中),以指定在版本衝突的情況下應重試更新的次數。
update
操作有效內容支持以下選項:doc(部分文檔),upsert,doc_as_upsert,script,params(用於腳本),lang(用於腳本)和_source。 更新操作的示例:
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
{ "update" : {"_id" : "1", "_index" : "index1", "retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"} }
{ "update" : { "_id" : "0", "_index" : "index1", "retry_on_conflict" : 3} }
{ "script" : { "source": "ctx._source.counter += params.param1", "lang" : "painless", "params" : {"param1" : 1}}, "upsert" : {"counter" : 1}}
{ "update" : {"_id" : "2", "_index" : "index1", "retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"}, "doc_as_upsert" : true }
{ "update" : {"_id" : "3", "_index" : "index1", "_source" : true} }
{ "doc" : {"field" : "value"} }
{ "update" : {"_id" : "4", "_index" : "index1"} }
{ "doc" : {"field" : "value"}, "_source": true}
'
部分響應
爲確保快速響應,如果一個或多個分片失敗,多重獲取API將響應部分結果。 有關更多信息,請參閱Shard故障。
Reindex API
Reindex要求爲source index中的所有文檔啓用
_source
。
Reindex不會嘗試設置目標索引,並且不會拷貝source index 中的設置。您應該在運行
_reindex
操作之前設置目標索引,包括設置映射,分片數量,副本等
Reindex的最基本形式只是將文檔從一個索引複製到另一個索引。下面的例子將拷貝文檔twitter
索引到new_twitter
索引中:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter"
}
}
'
返回結果如下:
{
"took" : 147,
"timed_out": false,
"created": 120,
"updated": 0,
"deleted": 0,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"total": 120,
"failures" : [ ]
}
就像_update_by_query
,reindex
獲取一個source index 的快照,但是目標必須是一個不同的索引,所以不會出現版本衝突。可以像索引API一樣配置dest
元素來控制樂觀併發控制。僅僅需要省略version_type
(如上所屬)或者設置它爲internal
。都會導致Elasticsearch將文檔盲目的轉儲到目標中,覆蓋任何碰巧具有相同類型和id的文檔:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter",
"version_type": "internal"
}
}
'
設置version_type
爲external
將導致Elasticsearch保存源文件的版本,創建缺失的文檔,並更新目標索引中比源索引中版本更舊的文檔:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter",
"version_type": "external"
}
}
'
設置op_type
爲create
將導致_reindex
在目標索引中只創建丟失的文檔,所有已存在的文檔將會導致版本衝突。
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter",
"op_type": "create"
}
}
'
默認的,版本衝突會中止_reindex
。當版本衝突時,conflicts
請求體參數可以指導_reindex
來處理下一個文檔。需要重要注意的時,通過conflicts
參數處理其他錯誤類型是不受影響的,當在請求體中設置 "conflicts": "proceed"
,_reindex
將繼續處理版本衝突,並返回所遇到的版本衝突計數:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"conflicts": "proceed",
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter",
"op_type": "create"
}
}
'
您可以通過向source
添加查詢來限制文檔。這將只複製由kimchy
發出的tweet
到new_twitter
:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter",
"query": {
"term": {
"user": "kimchy"
}
}
},
"dest": {
"index": "new_twitter"
}
}
'
index
在source
是一個列表,在一個請求中,允許你從大量sources中拷貝。下面的例子將從twitter
和blog
文檔中拷貝文檔:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": ["twitter", "blog"]
},
"dest": {
"index": "all_together"
}
}
'
Reindex API不會處理ID衝突,因此最後編寫的文檔將“win”,但順序通常不可預測,因此依賴此行爲並不是一個好主意。 而是使用腳本確保ID是唯一的。
通過設置size
來限制處理文檔的數量這,將會從twitter
拷貝一個單獨的文檔到new _twitter
:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"size": 1,
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter"
}
}
'
如果你想要twitter索引中的特定文檔集,你需要使用sort。排序使滾動效率降低,但在某些情況下,它是值得的。 如果可能,請選擇更具選擇性的查詢來進行大小和排序。 這會將10000個文件從twitter複製到new_twitter:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"size": 10000,
"source": {
"index": "twitter",
"sort": { "date": "desc" }
},
"dest": {
"index": "new_twitter"
}
}
'
source
部分支持搜索請求中支持的所有元素。 例如,可以使用source
過濾重新索引原始文檔中的一部分字段,如下所示:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter",
"_source": ["user", "_doc"]
},
"dest": {
"index": "new_twitter"
}
}
'
像_update_by_query
, _reindex
支持腳本修改文檔,和_update_by_query
不同,它允許修改文檔的metadata,這個例子顛覆了源文檔的版本:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter",
"version_type": "external"
},
"script": {
"source": "if (ctx._source.foo == \u0027bar\u0027) {ctx._version++; ctx._source.remove(\u0027foo\u0027)}",
"lang": "painless"
}
}
'
就像在_update_by_query
中一樣,您可以設置ctx.op
來更改在目標索引上執行的操作:
noop
如果腳本決定不需要在目標索引中索引文檔,則設置ctx.op = “noop”。此no操作將在響應體中的noop計數器中報告。
delete
如果腳本決定必須從目標索引中刪除文檔,則設置ctx.op = “delete”。刪除操作將在響應主體的已刪除計數器中報告。
設置ctx.op爲其他,或者在CTX中設置任何其他字段,將會返回錯誤。
你可以改變:
_id
_index
_version
_routing
將_version
設置爲null
或將它從ctx
映射中清除,就像沒有在索引請求中發送版本一樣;它會導致在目標索引中覆蓋文檔,而不管目標上的版本或在_reindex
請求中使用的版本類型。
默認情況下,如果_reindex
看到帶有路由的文檔,則除非腳本更改了路由,否則將保留路由。 您可以在dest請求上設置路由以更改此設置:
keep
將針對每個匹配發送的批量請求的路由設置爲匹配上的路由。 這是默認值。
discard
將爲每個匹配發送的批量請求上的路由設置爲null。
=<some text>
將爲每個匹配發送的批量請求上的路由設置爲=之後的所有文本。
例如,您可以使用以下請求將所有文檔從具有公司名稱cat
的源索引複製到路由設置爲cat的dest索引中。
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "source",
"query": {
"match": {
"company": "cat"
}
}
},
"dest": {
"index": "dest",
"routing": "=cat"
}
}
'
默認情況下,reindex使用的滾動批數爲1000。你可以在source
元素中設置size
字段來改變批數大小。
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "source",
"size": 100
},
"dest": {
"index": "dest",
"routing": "=cat"
}
}
'
Reindex還可以使用通過指定pipeline
來使用Ingest node特點,像下面這樣:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "source"
},
"dest": {
"index": "dest",
"pipeline": "some_ingest_pipeline"
}
}
'
從遠程Reindex
Reindex支持從遠程Elasticsearch集羣reindexing
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"remote": {
"host": "http://otherhost:9200",
"username": "user",
"password": "pass"
},
"index": "source",
"query": {
"match": {
"test": "data"
}
}
},
"dest": {
"index": "dest"
}
}
'
host
參數必須包含協議,主機,端口(例如:http:/otherHost:9200
)和可選路徑(例如:http:/otherHost:9200
)。username
和password
是可選的,如果填寫了username和password,那麼_reindex
將會使用他們作爲連接到遠程Elasticsearch集羣的基礎驗證。使用基本身份驗證時務必使用https,否則密碼將以純文本格式發送。有一系列設置可用來配置https連接的行爲。
遠程主機必須在elasticsearch.yml
中使用reindex.remote.whitelist
屬性指定白名單。可以將其設置爲逗號分隔的允許遠程主機和端口組合列表(例如: otherhost:9200, another:9200, 127.0.10.*:9200, localhost:*
),協議可以忽略,僅主機和端口是必須的例如:
reindex.remote.whitelist: "otherhost:9200, another:9200, 127.0.10.*:9200, localhost:*"
必須在節點上配置白名單來配合reindex。
要啓用發送到舊版本Elasticsearch的查詢,無需驗證或修改即可將查詢參數直接發送到遠程主機。
從遠程集羣節點Reindexing不支持手動或者自動切分。
從遠程服務器Reindex使用堆上緩衝區,默認最大大小爲100mb。如果遠程索引包含非常大的文檔,則你需要更小的批數size,下面的例子設置批數大小爲10
這是非常非常小的。
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"remote": {
"host": "http://otherhost:9200"
},
"index": "source",
"size": 10,
"query": {
"match": {
"test": "data"
}
}
},
"dest": {
"index": "dest"
}
}
'
還可以使用socket_timeout
字段設置遠程連接上的套接字讀取超時,使用connect_timeout
字段設置連接超時。兩者默認都是30秒。下面這個例子,設置套接字讀取超時爲1分鐘,連接超時設置爲10秒。
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"remote": {
"host": "http://otherhost:9200",
"socket_timeout": "1m",
"connect_timeout": "10s"
},
"index": "source",
"query": {
"match": {
"test": "data"
}
}
},
"dest": {
"index": "dest"
}
}
'
配置SSL參數
從遠程Reindex支持SSL設置,這些設置必須在elasticsearch.yml
中配置,除了安全設置,安全設置添加在Elasticsearch KeyStore中。配置SSL不會在_reindex
請求體中:
支持下面的設置:
reindex.ssl.certificate_authorities
應受信任的PEM編碼證書文件的路徑列表。你不能同時指定reindex.ssl.certificate_authorities
和reindex.ssl.truststore.path
.
reindex.ssl.truststore.path
Java keyStore文件的路徑,包含受信任的證書。這個信任庫可以是JKS或者PKCS#12格式。你不能同時指定reindex.ssl.certificate_authorities
和reindex.ssl.truststore.path
.
reindex.ssl.truststore.password
信任庫的密碼(reindex.ssl.truststore.path
),這個設置不能用於reindex.ssl.truststore.secure_password
reindex.ssl.truststore.secure_password
(Source)
信任庫的密碼(reindex.ssl.truststore.path
),這個設置不能用於reindex.ssl.truststore.secure_password
reindex.ssl.truststore.type
信任庫的類型 (reindex.ssl.truststore.path
),必須是jks
或者PKCS12
其中的一個。如果信任庫路徑以".p12", “.pfx” or "pkcs12"結尾,這個設置默認是PKCS12
,否則默認爲JKS
reindex.ssl.verification_mode
指示用於防止中間人攻擊和僞造證書的驗證類型。其中一個是full
驗證主機名和證書路徑,certificate
驗證正路路徑,但是不驗證主機名,null
不進行驗證,在生產環境中建議使用null,默認爲full
。
reindex.ssl.certificate
指定用於HTTP客戶端身份驗證(如果遠程集羣需要)的PEM編碼證書(或證書鏈)的路徑,此設置需要reindex.ssl.key
也被設置。您不能同時指定reindex.ssl.certificate
和 reindex.ssl.keystore.path
。
reindex.ssl.key
指定證書用於客戶端驗證PEM解碼私鑰關聯的路徑 (reindex.ssl.certificate
)。你不能同時指定reindex.ssl.key
和reindex.ssl.keystore.path
。
reindex.ssl.key_passphrase
如果PEM解碼私鑰是加密的,指定解密PEM解碼私鑰的密碼。不能用於reindex.ssl.secure_key_passphrase
。
reindex.ssl.secure_key_passphrase
(安全)
如果PEM解碼私鑰是加密的,指定解密PEM解碼私鑰的密碼。不能用於reindex.ssl.key_passphrase
。
reindex.ssl.keystore.path
指定用於HTTP客戶端驗證的keyStore包含私鑰和證書的路徑(如果遠程集羣需要)。keyStore可以使用JKS
和PKCS#12
格式。你不能同時指定reindex.ssl.key
和reindex.ssl.keystore.path
。
reindex.ssl.keystore.type
keyStore的類型 (reindex.ssl.keystore.path
)。必須是jks
或者PKCS12
其中的一個。如果信任庫路徑以".p12", “.pfx” or "pkcs12"結尾,這個設置默認是PKCS12
,否則默認爲JKS
reindex.ssl.keystore.password
keystore的密碼(reindex.ssl.keystore.path
),這個設置不能用於reindex.ssl.keystore.secure_password
reindex.ssl.keystore.secure_password
(安全)
keystore的密碼(reindex.ssl.keystore.path
),這個設置不能用於reindex.ssl.keystore.password
reindex.ssl.keystore.key_password
在keystore中用於key的密碼(reindex.ssl.keystore.path
)默認爲keystore密碼。這個設置不能用於reindex.ssl.keystore.secure_key_password
。
reindex.ssl.keystore.secure_key_password
(安全)
在keystore中用於key的密碼(reindex.ssl.keystore.path
)默認爲keystore密碼。這個設置不能用於reindex.ssl.keystore.key_password
。
URL參數
除了像pretty
這樣的標準參數,ReindexAPI還支持refresh
,wait_for_completion
, wait_for_active_shards
, timeout
, scroll
, 和 requests_per_second
。
發送refresh
url參數將導致請求寫入所有已刷新的索引。這個不同於IndexAPI的refresh
參數,後者只會刷新接收新數據的碎片。也不想IndexAPI,它不支持wait_for
。
在請求中包含wait_for_completion=false
,Elasticsearch將會執行一些預檢查,開始請求並且能用TasksAPI來關閉或者得到一個Task的狀態並將其返回。。Elasticsearch還將創建此任務的記錄,作爲.tasks/task/${taskId}
的文檔。保留和刪除由你抉擇。當您完成它或者刪除它時,Elasticsearch可以回收它使用的空間。
wait_for_active_shards
在進行重新索引之前,一個碎片必須有多少個副本處於活動狀態。點擊此處查看更多。timeout
控制每個寫請求等待不可用碎片編程可用的時間。這兩種方法在BulkAPI中的工作方式完全相同。由於重新索引使用滾動搜索,您還可以指定滾動參數來控制“search context”保持活動的時間(例如?scroll=10m
)。默認值爲5m。
requests_per_second
可以被設置爲十進制的正數(1.4
, 6
, 1000
, 等.) ,限制_reindex發出批量索引操作的速率。服務節流可以將 requests_per_second
設置爲 -1
.
節流是通過在批之間等待來完成的,這樣就可以給_reindex內部使用的滾動設置一個考慮填充的超時。填充時間是batchs size
除以requests_per_second
和寫入時間之間的差額。默認情況下batch size爲1000,所以如果requests_per_second被設置爲500:
target_time = 1000 / 500 per second = 2 seconds
`padding time` = target_time - write_time = 2 seconds - .5 seconds = 1.5 seconds
因爲batch是作爲一個單獨的_bulk
請求發出的,大的batch size將導致Elasticsearch創建許多請求並且等一段時間纔會開始下一組。這是間歇性的的而不是平滑的,這個默認值爲-1
。
響應體
JSON響應看起來像下面這樣:
{
"took": 639,
"timed_out": false,
"total": 5,
"updated": 0,
"created": 5,
"deleted": 0,
"batches": 1,
"noops": 0,
"version_conflicts": 2,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": 1,
"throttled_until_millis": 0,
"failures": [ ]
}
took
整個操作花費的總時長。
time_out
如果在重新索引期間執行的任何請求超時,則將此標誌設置爲true。
total
成功處理的文檔總數。
updated
成功更新的文檔總數。
created
成功創建的文檔總數。
deleted
成功刪除的文檔總數。
batches
通過reindex滾動請求拉回的數量
noops
被忽略的文檔數。因爲用於重新索引的腳本爲ctx.op返回了一個noop值。
version_conflicts
reindex命中的版本衝突數。
retries
reindex重試也沒有成功的數量。bulk
是bulk動作重試的數量,search
是search動作重試的數量。
throttled_millis
請求休眠以符合requests_per_second的毫秒數。
requests_per_second
reindex期間請求每秒有效執行的次數。
throttled_until_millis
在_reindex
這個字段應該一直等於0,它僅僅意味着,當使用TaskAPI,它指示下一次(從epoch開始以毫秒爲單位)將再次執行節流請求,以符合requests_per_second
。
failures
如果流程中存在任何不可恢復的錯誤,則會出現一系列故障。如果這不是空的,那麼請求就會因爲這些失敗而中止。Reindex是使用batch實現的,任何失敗都會導致整個進程中止,但是當前batch中的所有失敗都會收集到數組中。您可以使用conflicts選項來防止reindex在版本衝突上中止。
使用TaskAPI
你可以使用TaskAPI來獲取所有正在運行中的reindex請求的狀態。
響應結果如下:
{
"nodes" : {
"r1A2WoRbTwKZ516z6NEs5A" : {
"name" : "r1A2WoR",
"transport_address" : "127.0.0.1:9300",
"host" : "127.0.0.1",
"ip" : "127.0.0.1:9300",
"attributes" : {
"testattr" : "test",
"portsfile" : "true"
},
"tasks" : {
"r1A2WoRbTwKZ516z6NEs5A:36619" : {
"node" : "r1A2WoRbTwKZ516z6NEs5A",
"id" : 36619,
"type" : "transport",
"action" : "indices:data/write/reindex",
#這個對象包含了真正的狀態,它與響應JSON相同,只是添加了重要的total字段。total是_reindex希望執行的操作總數。您可以通過添加updated、created的和deleted的字段來估計進度。當它們的和等於整個字段時,請求將結束。
"status" : {
"total" : 6154,
"updated" : 3500,
"created" : 0,
"deleted" : 0,
"batches" : 4,
"version_conflicts" : 0,
"noops" : 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1,
"throttled_until_millis": 0
},
"description" : "",
"start_time_in_millis": 1535149899665,
"running_time_in_nanos": 5926916792,
"cancellable": true,
"headers": {}
}
}
}
}
}
可以通過Task id來直接查看Task,下面的例子展示了取回taskid爲r1A2WoRbTwKZ516z6NEs5A:36619
:的信息:
curl -X GET "localhost:9200/_tasks/r1A2WoRbTwKZ516z6NEs5A:36619"
這個API的優勢是集成了wait_for_completion=false
並且返回了完成任務的狀態,如果task已經完成,並且 wait_for_completion=false
,它將返回一個results
或者error
字段。這個特性的代價是wait_for_completion=false
在.tasks/task/${taskId}
處創建的文檔。你可以自己決定是否刪除。
使用Cancel Task API
任何Reindex都能被Task Calcel API取消。例如:
curl -X POST "localhost:9200/_tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel"
TaskId可以使用TaskAPI來查到。
取消應該是在瞬間完成的,但是有可能會需要短暫的幾秒。Task API將跳過列出任務,直到它醒來自動取消。
Rethrottling(重新設置節流時長)
在運行reindex期間可以使用_rethrottle
API來改變requests_per_second
的值。
curl -X POST "localhost:9200/_reindex/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second=-1"
TaskId可以使用TaskAPI來查到。
就像在Reindex API上一樣設置,requests_per_second
值能被設置成-1
來禁用節流或者設置成任何正數來節流。Rethrottling 加快查詢立即生效的速度,但是它減慢查詢在完成當前批處理後生效的速度。這將防止滾動超時。
Reindex來改變字段名
_reindex
可用於構建具有重命名字段的索引的副本。假設您創建了一個包含如下文檔的索引:
curl -X POST "localhost:9200/test/_doc/1?refresh" -H 'Content-Type: application/json' -d'
{
"text": "words words",
"flag": "foo"
}
'
但是你不喜歡flag這個名字,想把flag替換爲tag。_reindex
能幫你創建另一個索引:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "test"
},
"dest": {
"index": "test2"
},
"script": {
"source": "ctx._source.tag = ctx._source.remove(\"flag\")"
}
}
'
現在重新獲取文檔:
curl -X GET "localhost:9200/test2/_doc/1"
返回結果:
{
"found": true,
"_id": "1",
"_index": "test2",
"_type": "_doc",
"_version": 1,
"_seq_no": 44,
"_primary_term": 1,
"_source": {
"text": "words words",
"tag": "foo"
}
}
切分
Reindex支持切片滾動,以並行化重新索引過程。這種並行化可以提高效率,並提供一種方便的方法將請求分解爲更小的部分。
手動切分
對每個請求通過提供一個slice id和切分總數來手動切分Reindex請求:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter",
"slice": {
"id": 0,
"max": 2
}
},
"dest": {
"index": "new_twitter"
}
}
'
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter",
"slice": {
"id": 1,
"max": 2
}
},
"dest": {
"index": "new_twitter"
}
}
'
如果你想驗證結果:
curl -X GET "localhost:9200/_refresh"
curl -X POST "localhost:9200/new_twitter/_search?size=0&filter_path=hits.total"
結果是這樣一個合理的total
:
{
"hits": {
"total" : {
"value": 120,
"relation": "eq"
}
}
}
自動切分
您還可以讓_reindex
使用切片滾動自動並行化_uid
上的切片。使用slices
指定要使用的片數:
curl -X POST "localhost:9200/_reindex?slices=5&refresh" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter"
}
}
'
你可以驗證其結果:
curl -X POST "localhost:9200/new_twitter/_search?size=0&filter_path=hits.total"
結果是這樣一個合理的total
:
{
"hits": {
"total" : {
"value": 120,
"relation": "eq"
}
}
}
設置slices
爲auto
,Elasticsearch將自動的選擇一個合理的切分數來使用,此設置將在一定限度內使用每個切塊一個切分。如果有多個源索引,它將根據分片數量最少的索引選擇切片的數量。
將slices
添加到_reindex
只是自動執行上一節中使用的手動過程,創建子請求,這意味着它有一些奇怪的地方:
-
你可以在TaskAPI中看到這些請求。這些子請求是帶有
slices
請求的Task的child
Task -
爲帶有
slices
的請求獲取任務狀態,只包含已完成slices的狀態。 -
這些子請求可以單獨尋址,用於cancelation(取消) 和rethrottling(改變節流的值)等操作。
-
帶有
slices
的Rethrottling請求將按比例rethrottle未完成的子請求。 -
關閉帶有
slices
的請求將關閉每個子請求。 -
由於切片的性質,每個子請求不會完全得到文檔部分。所有文檔都將被處理,但是有些部分可能比其他部分更大。期望更大的
slices
具有更均勻的分佈。 -
帶有
slices
的請求上的requests_per_second
和size
等參數按比例分佈到每個子請求。將這一點與上面關於分佈不均勻的觀點結合起來,我們得出結論:使用帶slices
的size
可能不會導致reindex
精確大小的文檔 -
每個子請求都獲得稍微不同的源索引的快照,儘管這些快照幾乎是同時獲取的。
帶有slices的數量
如果是自動切分請求,設置slices
爲auto
將選擇一個合理的數值來給大多數索引。如果手動切片或以其他方式調優自動切片,請繼續閱讀下方內容:
當slices
數等於分片數這時候查詢性能是最好的。如果slices
值很大,選擇一個更小的值會有損性能。將slices
的數量設置爲大於分片片的數量通常不會提高效率,而且會增加開銷。
索引性能隨着分片的數量在可用資源之間線性擴展。
查詢或索引性能是否主導運行時取決於reindex的文檔和集羣資源。
同時Reindex索引
如果你有多個索引要Reindex,通常更好的做法是一次Reindex一個,而不是使用全局模式同時獲取多個。這樣,如果有任何錯誤,您可以刪除部分完成的索引並從該索引重新開始,從而恢復該過程。它還使並行化過程變得相當簡單:將索引列表拆分爲reindex並並行運行每個列表。
一次性bash腳本似乎很好地解決了這個問題:
for index in i1 i2 i3 i4 i5; do
curl -HContent-Type:application/json -XPOST localhost:9200/_reindex?pretty -d'{
"source": {
"index": "'$index'"
},
"dest": {
"index": "'$index'-reindexed"
}
}'
done
Reindex daily索引
儘管有上面的建議,你可以使用_reindex
結合Painless來reindex daily 索引將新模板應用於現有文檔。
假設你有一下文件組成的索引:
curl -X PUT "localhost:9200/metricbeat-2016.05.30/_doc/1?refresh" -H 'Content-Type: application/json' -d'
{"system.cpu.idle.pct": 0.908}
'
curl -X PUT "localhost:9200/metricbeat-2016.05.31/_doc/1?refresh" -H 'Content-Type: application/json' -d'
{"system.cpu.idle.pct": 0.105}
'
metricbeat-*
索引的新模板已經加載到Elasticsearch中,但它只適用於新創建的索引。Painless可用於reindex現有文檔並應用新模板。
下面的腳本從索引名稱中提取日期,並創建一個附加了-1的新索引。 來自metricbeat-2016.05.31
的所有數據將reindex到metricbeat-2016.05.31-1
。
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "metricbeat-*"
},
"dest": {
"index": "metricbeat"
},
"script": {
"lang": "painless",
"source": "ctx._index = \u0027metricbeat-\u0027 + (ctx._index.substring(\u0027metricbeat-\u0027.length(), ctx._index.length())) + \u0027-1\u0027"
}
}
'
從metricbeat 索引之前的所有文檔現在都可以在*-1
中找到:
curl -X GET "localhost:9200/metricbeat-2016.05.30-1/_doc/1"
curl -X GET "localhost:9200/metricbeat-2016.05.31-1/_doc/1"
前一種方法還可以與更改字段名稱結合使用,以便僅將現有數據加載到新索引中,並在需要時重命名任何字段。
獲取索引的隨機子集
_reindex
可用於提取索引的隨機子集進行測試:
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"size": 10,
"source": {
"index": "twitter",
"query": {
"function_score" : {
"query" : { "match_all": {} },
"random_score" : {}
}
},
#_reindex默認按_doc排序,因此random_score不會有任何效果,除非您覆蓋sort to _score。
"sort": "_score"
},
"dest": {
"index": "random_twitter"
}
}
'
Term Vectors
返回特定文檔字段中術語的信息和統計信息。文檔可以存儲在索引中,也可以由用戶人工提供。默認情況下,術語向量是實時的,而不是接近實時的。這可以通過將realtime參數設置爲false來更改。
curl -X GET "localhost:9200/twitter/_termvectors/1"
您可以選擇使用url中的參數指定檢索信息的字段:
curl -X GET "localhost:9200/twitter/_termvectors/1?fields=message"
或者在請求體中添加請求字段,字段也可以使用通配符指定,方法類似於multi match query
返回值
可請求的三種類型的值:term information, term statistics 和 field statistics,默認的,返回term information, 和 field statistics的所有字段信息,但是不返回term statistics 信息。
Term information
- term frequency in the field (總是返回)
- term positions (
positions
: true) - start and end offsets (
offsets
: true) - term payloads (
payloads
: true), as base64 encoded bytes
如果在索引中請求信息沒有存儲,如果可能的話,它將被實時計算。另外,可以計算甚至不存在於索引中的文檔,而是由用戶提供的 term vectors。
開始和結束偏移量假設使用的是UTF-16編碼。如果您想使用這些偏移量來獲得生成此令牌的原始文本,則應該確保您正在獲取的子字符串也使用UTF-16編碼。
Term statistics
設置 term_statistics
爲 true
(默認爲false
) 將會返回相關信息
- total term frequency (一個term在所有文檔中出現的頻率是多少)
- document frequency (包含當前term的文檔數量)
默認情況下,那些值是不會返回的,因爲term statistics對性能有嚴重影響
Field statistics
設置 term_statistics
爲 false
(默認爲true
) 將會不會被返回
- document count (包含當前字段有多少文檔)
- sum of document frequencies (此字段中所有term的文檔頻率總和)
- sum of total term frequencies (該字段中每個項的總term頻率之和)
Term過濾
使用參數filter
,還可以根據tf-idf分數對返回的term進行篩選。
支持下列子參數:
max_num_terms |
每個字段必須返回的最大term數. 默認爲2525 . |
---|---|
min_term_freq |
忽略源文檔中小於此頻率的單詞. 默認爲1 . |
max_term_freq |
忽略源文檔中小於此頻率的單詞. 默認爲無限制。 |
min_doc_freq |
忽略至少在這麼多文檔中沒有出現的術語. 默認爲 1 . |
max_doc_freq |
忽略出現在很多文檔中的單詞.默認爲無限制。 |
min_word_length |
將被忽略的單詞的最小單詞長度.默認爲 0 . |
max_word_length |
超過該長度的單詞將被忽略. 默認爲無限制 (0 ). |
表現
Term和字段統計是不準確的。刪除的文檔不考慮在內,僅爲所請求的文檔所在的切分檢索信息。因此,term和字段統計僅作爲相對的度量有用,而絕對值在這方面沒有意義。默認情況下,在請求人工文檔的term vectors時,隨機選擇一個分片來獲取統計信息。使用路由只會命中特定的分片。
返回存儲的term vectors
首先我們存儲創建一個索引來存儲term vectors ,請求體如下:
curl -X PUT "localhost:9200/twitter" -H 'Content-Type: application/json' -d'
{ "mappings": {
"properties": {
"text": {
"type": "text",
"term_vector": "with_positions_offsets_payloads",
"store" : true,
"analyzer" : "fulltext_analyzer"
},
"fullname": {
"type": "text",
"term_vector": "with_positions_offsets_payloads",
"analyzer" : "fulltext_analyzer"
}
}
},
"settings" : {
"index" : {
"number_of_shards" : 1,
"number_of_replicas" : 0
},
"analysis": {
"analyzer": {
"fulltext_analyzer": {
"type": "custom",
"tokenizer": "whitespace",
"filter": [
"lowercase",
"type_as_payload"
]
}
}
}
}
}
'
第二步,我們添加一些文檔:
curl -X PUT "localhost:9200/twitter/_doc/1" -H 'Content-Type: application/json' -d'
{
"fullname" : "John Doe",
"text" : "twitter test test test "
}
'
curl -X PUT "localhost:9200/twitter/_doc/2" -H 'Content-Type: application/json' -d'
{
"fullname" : "Jane Doe",
"text" : "Another twitter test ..."
}
'
下面的請求返回關於text字段在文檔1
中的所有信息和統計:
curl -X GET "localhost:9200/twitter/_termvectors/1" -H 'Content-Type: application/json' -d'
{
"fields" : ["text"],
"offsets" : true,
"payloads" : true,
"positions" : true,
"term_statistics" : true,
"field_statistics" : true
}
'
響應:
{
"_id": "1",
"_index": "twitter",
"_type": "_doc",
"_version": 1,
"found": true,
"took": 6,
"term_vectors": {
"text": {
"field_statistics": {
"doc_count": 2,
"sum_doc_freq": 6,
"sum_ttf": 8
},
"terms": {
"test": {
"doc_freq": 2,
"term_freq": 3,
"tokens": [
{
"end_offset": 12,
"payload": "d29yZA==",
"position": 1,
"start_offset": 8
},
{
"end_offset": 17,
"payload": "d29yZA==",
"position": 2,
"start_offset": 13
},
{
"end_offset": 22,
"payload": "d29yZA==",
"position": 3,
"start_offset": 18
}
],
"ttf": 4
},
"twitter": {
"doc_freq": 2,
"term_freq": 1,
"tokens": [
{
"end_offset": 7,
"payload": "d29yZA==",
"position": 0,
"start_offset": 0
}
],
"ttf": 2
}
}
}
}
}
動態生成term vectors
沒有顯式存儲在索引中的Term vectors將自動動態計算。下面的請求返回文檔1中字段的所有信息和統計信息,即使這些term沒有顯式地存儲在索引中。注意,對於字段text
,不重新生成term。
curl -X GET "localhost:9200/twitter/_termvectors/1" -H 'Content-Type: application/json' -d'
{
"fields" : ["text", "some_field_without_term_vectors"],
"offsets" : true,
"positions" : true,
"term_statistics" : true,
"field_statistics" : true
}
'
人工文檔
還可以爲人工文檔生成term vectors,即索引中不存在的文檔。例如,下面的請求將返回與示例1相同的結果。使用的映射由索引決定。
如果打開動態映射(默認),將動態創建原始映射中沒有的文檔字段。
curl -X GET "localhost:9200/twitter/_termvectors" -H 'Content-Type: application/json' -d'
{
"doc" : {
"fullname" : "John Doe",
"text" : "twitter test test test"
}
}
'
Per-feild 分析器
此外,可以使用per_field_analyzer
參數提供不同於字段的分析器。這對於以任何方式生成term vectors都很有用,特別是在使用人工文檔時。當爲已經存儲term vectors的字段提供分析器時,將重新生成term vectors。
curl -X GET "localhost:9200/twitter/_termvectors" -H 'Content-Type: application/json' -d'
{
"doc" : {
"fullname" : "John Doe",
"text" : "twitter test test test"
},
"fields": ["fullname"],
"per_field_analyzer" : {
"fullname": "keyword"
}
}
'
{
"_index": "twitter",
"_type": "_doc",
"_version": 0,
"found": true,
"took": 6,
"term_vectors": {
"fullname": {
"field_statistics": {
"sum_doc_freq": 2,
"doc_count": 4,
"sum_ttf": 4
},
"terms": {
"John Doe": {
"term_freq": 1,
"tokens": [
{
"position": 0,
"start_offset": 0,
"end_offset": 8
}
]
}
}
}
}
}
Term 過濾
最後,返回的term可以根據tf-idf分數進行篩選。在下面的示例中,我們從具有給定“plot”字段值的人工文檔中獲得三個最“interesting”的關鍵字。注意,關鍵字“Tony”或任何停止詞都不是響應的一部分,因爲它們的tf-idf必須太低。
curl -X GET "localhost:9200/imdb/_termvectors" -H 'Content-Type: application/json' -d'
{
"doc": {
"plot": "When wealthy industrialist Tony Stark is forced to build an armored suit after a life-threatening incident, he ultimately decides to use its technology to fight against evil."
},
"term_statistics" : true,
"field_statistics" : true,
"positions": false,
"offsets": false,
"filter" : {
"max_num_terms" : 3,
"min_term_freq" : 1,
"min_doc_freq" : 1
}
}
'
{
"_index": "imdb",
"_type": "_doc",
"_version": 0,
"found": true,
"term_vectors": {
"plot": {
"field_statistics": {
"sum_doc_freq": 3384269,
"doc_count": 176214,
"sum_ttf": 3753460
},
"terms": {
"armored": {
"doc_freq": 27,
"ttf": 27,
"term_freq": 1,
"score": 9.74725
},
"industrialist": {
"doc_freq": 88,
"ttf": 88,
"term_freq": 1,
"score": 8.590818
},
"stark": {
"doc_freq": 44,
"ttf": 47,
"term_freq": 1,
"score": 9.272792
}
}
}
}
}
Multi termvectors API
Multi termvector API允許同時獲得多個termvector。檢索termvectors的文檔由索引和id指定。但是也可以在請求本身中人爲地提供這些文檔。
響應包括一個文檔數組,其中包含所有獲取的termvector,每個元素都具有termvectors API提供的結構。舉個例子:
curl -X POST "localhost:9200/_mtermvectors" -H 'Content-Type: application/json' -d'
{
"docs": [
{
"_index": "twitter",
"_id": "2",
"term_statistics": true
},
{
"_index": "twitter",
"_id": "1",
"fields": [
"message"
]
}
]
}
'
有關可能的參數的描述,請參見[termvectors API](#Term Vectors)。
_mtermvector路徑也可以用於索引(在這種情況下,在body中不需要它):
curl -X POST "localhost:9200/twitter/_mtermvectors" -H 'Content-Type: application/json' -d'
{
"docs": [
{
"_id": "2",
"fields": [
"message"
],
"term_statistics": true
},
{
"_id": "1"
}
]
}
'
如果所有的文檔在相同的索引,並且參數也相同,請求可以被簡化:
curl -X POST "localhost:9200/twitter/_mtermvectors" -H 'Content-Type: application/json' -d'
{
"ids" : ["1", "2"],
"parameters": {
"fields": [
"message"
],
"term_statistics": true
}
}
'
另外,就和termvectors API一樣,可以爲用戶提供的文檔生成termvectors。使用的映射由_index
確定。
curl -X POST "localhost:9200/_mtermvectors" -H 'Content-Type: application/json' -d'
{
"docs": [
{
"_index": "twitter",
"doc" : {
"user" : "John Doe",
"message" : "twitter test test test"
}
},
{
"_index": "twitter",
"doc" : {
"user" : "Jane Doe",
"message" : "Another twitter test ..."
}
}
]
}
'
?refresh
當此請求所做的更改對搜索可見時,Index,Update,Delete,Bulk API支持設置refrash
來控制。這些是允許的值:
空字符串或者true
在操作發生後立即刷新相關的主分片和副本分片(而不是整個索引),以便更新後的文檔立即出現在搜索結果中。只有在確保它不會導致性能低下(無論是從索引還是搜索的角度)之後,才應該這樣做。
wait_for
在響應之前,等待請求所做的更改被刷新爲可見。這並不強制立即刷新,而是等待刷新發生。Elasticsearch自動刷新已更改每個索引的分片。refresh_interval
,默認值爲1秒。該設置是動態的。在任何支持刷新的API上調用Refresh API或將Refresh設置爲true也會導致刷新,從而導致已經運行的帶有Refresh =wait_for的請求返回。
false
(默認值)
不採取刷新相關操作。此請求所做的更改將在請求返回後的某個時刻變得可見。
選擇要使用的設置
除非你有更好的理由使用等待改變爲可見,否則使用refresh=false
。或者,因爲這是缺省值,所以將refresh參數保留在URL之外。這是最簡單和最快的選擇。
如果您絕對必須使請求所做的更改與請求同步可見,那麼您必須在向Elasticsearch添加更多負載(true)和等待響應更長的時間(wait_for)之間進行選擇。以下幾點應有助於作出這一決定:
- 與true相比,對索引進行的更改越多,wait_for保存的工作就越多。在每個索引只更改一次索引的情況下。
refresh_interval
則不保存任何工作。 - true創建效率較低的索引構造(小段),這些構造稍後必須合併爲更高效的索引構造(更大的段)。這意味着true的代價是在index時創建小段,在search時搜索小段,在merge時生成更大的段。
- 永遠不要在一行中啓動多個refresh=wait_for請求。相反,使用refresh=wait_for將它們批處理爲單個批量請求,而Elasticsearch將並行地啓動它們,只有當它們全部完成時才返回。
- 如果刷新間隔設置爲-1,禁用自動刷新,那麼refresh=wait_for的請求將無限期等待,直到某個操作導致刷新。相反,設置索引。refresh_interval小於默認值(如200ms)將使refresh=wait_for更快地返回,但它仍然會生成效率低下的segments。
- refresh=wait_for隻影響正在運行的請求,但是,通過強制立即刷新,refresh=true將影響其他正在進行的請求。通常,如果您有一個正在運行的系統,您不希望干擾它,那麼refresh=wait_for是一個較小的修改。
refrash=wait_for可以強制刷新
當已經有索引時,如果出現refresh=wait_for
請求。max_refresh_listene
r(默認值爲1000)請求等待對該分片的刷新,然後該請求的行爲將與將refresh設置爲true一樣:它將強制刷新。換言之:當refresh=wait_for
請求返回時,它的更改對於搜索是可見的,同時防止對阻塞的請求使用未檢查的資源。如果一個請求因爲耗盡了偵聽器插槽而強制刷新,那麼它的響應將包含“forced_refresh”:true。
無論修改切分多少次,批量請求在每個切分上只佔用一個插槽。
例子
這些將創建一個文檔,並立即刷新索引,使其可見:
curl -X PUT "localhost:9200/test/_doc/1?refresh" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
curl -X PUT "localhost:9200/test/_doc/2?refresh=true" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
這將創建一個文檔,而不做任何事情,使其搜索可見:
curl -X PUT "localhost:9200/test/_doc/3" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
curl -X PUT "localhost:9200/test/_doc/4?refresh=false" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
這將創建一個文檔,並等待它成爲可見的搜索:
curl -X PUT "localhost:9200/test/_doc/4?refresh=wait_for" -H 'Content-Type: application/json' -d'
{"test": "test"}
'
樂觀併發控制
Elasticsearch是分佈式的,當文檔被創建,刪除,更新,更改,一個新的文檔版本就必須被複制到其他節點。Elasticsearch是異步的,併發的,這意味着那些複製請求是併發的被髮送出去的,可能不按順序到達目的地,elasticsearch需要一種方法開確保老的文檔版本號永遠不會覆蓋新的文檔版本號。
爲了確保老的版本號永遠不會覆蓋新的版本號,對文檔執行的每個操作都會通過主分片分配一個序列號,改序列號將會協調更改。每個操作的序列號是自增的,並且較新的操作保證具有比舊操作更高的序列號。
例如,下面的索引命令將創建一個文檔,併爲其分配一個初始序列號和主term:
curl -X PUT "localhost:9200/products/_doc/1567" -H 'Content-Type: application/json' -d'
{
"product" : "r2d2",
"details" : "A resourceful astromech droid"
}
'
您可以在響應的_seq_no
和_primary_term
字段中看到分配的序列號和主term:
{
"_shards" : {
"total" : 2,
"failed" : 0,
"successful" : 1
},
"_index" : "products",
"_type" : "_doc",
"_id" : "1567",
"_version" : 1,
"_seq_no" : 362,
"_primary_term" : 2,
"result" : "created"
}
Elasticsearch跟蹤要更改其存儲的每個文檔的最後一個操作的序號和主term。在GET API的響應中,在_seq_no
和_primary_term
字段中返回序列號和主項:
curl -X GET "localhost:9200/products/_doc/1567"
返回的結果:
{
"_index" : "products",
"_type" : "_doc",
"_id" : "1567",
"_version" : 1,
"_seq_no" : 362,
"_primary_term" : 2,
"found": true,
"_source" : {
"product" : "r2d2",
"details" : "A resourceful astromech droid"
}
}
注意:通過設置
seq_no_primary_term
參數,搜索API可以爲每次搜索命中返回_seq_no
和_primary_term
。
序列號和主term惟一地標識更改。通過記錄返回的序列號和主term,您可以確保只在檢索後沒有對文檔進行其他更改的情況下更改文檔。這是通過設置索引API或刪除API的if_seq_no
和if_primary_term
參數來實現的。
例如,下面的索引調用將確保向文檔添加一個標籤,而不會丟失對描述的任何潛在更改,或由另一個API添加另一個標籤:
curl -X PUT "localhost:9200/products/_doc/1567?if_seq_no=362&if_primary_term=2" -H 'Content-Type: application/json' -d'
{
"product" : "r2d2",
"details" : "A resourceful astromech droid",
"tags": ["droid"]
}
'