索引的遷移
API介紹
ES提供了_reindexAPI用來進行索引的遷移。其最簡單的用法如下:
POST _reindex
{
"source": {"index": "index-1"},
"dest": {"index": "index-2"}
}
可選參數(shù)
-
source.index用來指定源索引,可以是一個或者多個,如果是多個索引,當有相同的id存在時,其行為受
dest.version_type和dest.op_type控制。 dest.version_type
可選值為internal或者external。默認值為internal,在這種情況下,ES會將源索引中的文檔直接導入目標索引,而不管目標索引中是否存在相同id的文檔。如果上訴請求改為如下:
POST _reindex
{
"source": {
"index": "index-1"
},
"dest": {
"index": "index-2",
"version_type": "external"
}
}
如果把目標索引的version_type設(shè)成external,會在目標索引中保留源索引中文檔的版本號,需要保證源索引中相同id的文檔擁有更高的版本號。
在保留之前2個索引的情況下進行導入設(shè)置,會得到下面錯誤:
"failures": [
{
"index": "index-2",
"type": "_doc",
"id": "1",
"cause": {
"type": "version_conflict_engine_exception",
"reason": "[1]: version conflict, current version [2] is higher or equal to the one provided [1]",
"index_uuid": "ktaIPtpbS6SM9OYDGZKifQ",
"shard": "0",
"index": "tue-2"
},
"status": 409
}
]
-
dest.op_type該參數(shù)的可選值為
create,其效果是只會創(chuàng)建目標索引中不存在的文檔。如果同時指定,
version_type為external,并且將op_type指定為create,其執(zhí)行的效果是,版本高的更新成功了,同時,版本相同或者更低的則返回錯誤:"type": "version_conflict_engine_exception", "reason": "[1]: version conflict, current version [4] is higher or equal to the one provided [4]",可以看出,其作用的是
version_type,我嘗試調(diào)整兩個設(shè)置的順序,得到的同樣的結(jié)果。 -
conflicts如果在請求中指定,
conflicts:"proceed",返回的結(jié)果中將只會告知創(chuàng)建的個數(shù),更新的個數(shù),失敗的個數(shù)而不會包含具體的原因。官方文檔原文聲稱,如果設(shè)置此參數(shù),_reindex過程會在遇到版本沖突時繼續(xù)執(zhí)行,并返回沖突的個數(shù)。而實際上,經(jīng)過測試,如果不設(shè)置此參數(shù),處理仍然會繼續(xù),目前觀察到的區(qū)別,只有返回結(jié)果中是否包含沖突的原因
failure字段。When "conflicts": "proceed" is set in the request body, the _reindex process will continue on version conflicts and return a count of version conflicts encountered.failures (array) Array of failures if there were any unrecoverable errors during the process. If this is non-empty then the request aborted because of those failures. Reindex is implemented using batches and any failure causes the entire process to **abort** but all failures in the current batch are collected into the array. You can use the conflicts option to prevent reindex from aborting on version conflicts.官網(wǎng)上對失敗的解釋是,如果遇到失敗的情況,請求會退出。但是實際上發(fā)現(xiàn),除了沖突的條目以外,其他條目都是成功。
PS: 經(jīng)過測試,我發(fā)現(xiàn)其實自己是沒有注意到上面描述中的batches。默認reindex過程中的bulk是100,即每次寫100條,這100條中如果有一條錯誤的話,其余99條任然可以寫入。但是下一個100條就不會繼續(xù)執(zhí)行了。由于我的測試數(shù)據(jù)比較少,一個bulk就能把所有數(shù)據(jù)寫完,于是就產(chǎn)生了之前的疑惑。如果我把source中的size字段設(shè)成1,那么執(zhí)行過程中發(fā)生了一次沖突后,后續(xù)的流程就結(jié)束了。
POST _reindex
{
"conflicts": "proceed",
"source": {
"index": "index-1"
},
"dest": {
"index": "index-2",
"version_type": "external"
}
}
{
"took" : 22,
"timed_out" : false,
"total" : 6,
"updated" : 1,
"created" : 0,
"deleted" : 0,
"batches" : 1,
"version_conflicts" : 5,
"noops" : 0,
"retries" : {
"bulk" : 0,
"search" : 0
},
"throttled_millis" : 0,
"requests_per_second" : -1.0,
"throttled_until_millis" : 0,
"failures" : [ ]
}
-
size用來控制導入文檔的總數(shù)。
-
source.size該參數(shù)用來指定導入時,每批的文檔數(shù),默認是100。
POST _reindex { "conflicts": "proceed", "source": { "index": "index-1", "size": 2 }, "dest": { "index": "index-2", "version_type": "external" } }在index-1中,一共有6個文檔,導入的結(jié)果:
{ "took" : 26, "timed_out" : false, "total" : 6, "updated" : 0, "created" : 0, "deleted" : 0, "batches" : 3, "version_conflicts" : 6, "noops" : 0, "retries" : { "bulk" : 0, "search" : 0 }, "throttled_millis" : 0, "requests_per_second" : -1.0, "throttled_until_millis" : 0, "failures" : [ ] }可見,導入的批次變成了3次。
-
source.query使用該參數(shù)可以限定導入的條件:
POST _reindex { "conflicts": "proceed", "source": { "index": "index-1", "size": 2, "query": { "term": { "name": { "value": "sophie" } } } }, "dest": { "index": "index-2", "version_type": "external" } }我設(shè)置了導入條件,只有
name是sophie的可以被導入,而結(jié)果告訴我們只有一條記錄被導入到新的索引中。{ "took" : 247, "timed_out" : false, "total" : 1, "updated" : 0, "created" : 1, "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" : [ ] } -
source._source這個字段能夠在reindex過程中只將選中的字段提取出來導入到目標索引中去。
分片并行執(zhí)行
_reindexAPI可以利用ES的sliced scroll來并行執(zhí)行索引遷移功能。
有兩種方式可以進行:
source.slice:
POST _reindex
{
"source": {
"index": "tue-1",
"slice": {
"id": 1,
"max": 5
}
},
"dest": {
"version_type": "external",
"index": "tue-2"
}
}
可以在請求體中指定slice_id和要切片數(shù)目。
還有一種方式是在請求行做文章
POST _reindex?slices=auto
這里slices參數(shù)可以設(shè)置成auto或者具體的切片數(shù)目。如果設(shè)成auto,ES會自動根據(jù)shard數(shù)目進行切分。
需要注意的是,在手動配置的情況下,如果shard數(shù)太過于龐大,比如說一個索引有500個shard,選擇過大的切片數(shù),同樣會對性能有損耗,而如果選擇的slices超過shard的數(shù)據(jù),同樣也沒有意義。
從遠程索引導入
ES支持從一個遠程的索引導入數(shù)據(jù),需要對source中的remote對象進行配置。
POST _reindex
{
"source": {
"remote": {
"host": "http://otherhost:9200",
"username": "user",
"password": "pass"
},
"index": "source",
"query": {
"match": {
"test": "data"
}
}
},
"dest": {
"index": "dest"
}
}
其中host是必須,username和password是可選的。
在安全方面要注意的是,如果使用了基礎(chǔ)的auth方案,需要使用https加密ES節(jié)點本身的對外流量,否則用戶名和密碼會以明文的形式在網(wǎng)絡(luò)中傳輸。
遠程導入方案不支持切片(slice)處理。
在遷移過程中使用script
最近在一個群里,有群友問:"存在幾千萬個電話號碼,要按照最后8位進行聚類存到不同的索引。"當時,我提出了,使用_reindex+painless 腳本來實現(xiàn)。其實我當時沒有把握,只是覺得painless可以修改文檔的metadata,而且語法和java比較類似,應該可以做一些比較復雜的控制。后來晚上決心自己嘗試一下,于是自己在kibana上寫了一個簡單的demo,發(fā)現(xiàn)確實是可以的。這里記錄一下過程。
demo設(shè)計非常簡單,源索引只有一個數(shù)值型成員"num",reindex中使用painless將其中的數(shù)據(jù)按照num大小分到不同的目標索引中。
數(shù)據(jù)準備:
POST _bulk
{ "create" : { "_index" : "src_index", "_id": 1} }
{ "num" : 1 }
{ "create" : { "_index" : "src_index", "_id": 2} }
{ "num" : 2 }
{ "create" : { "_index" : "src_index", "_id": 3} }
{ "num" : 3 }
{ "create" : { "_index" : "src_index", "_id": 4} }
{ "num" : 4 }
{ "create" : { "_index" : "src_index", "_id": 5} }
{ "num" : 5 }
{ "create" : { "_index" : "src_index", "_id": 6} }
{ "num" : 6 }
{ "create" : { "_index" : "src_index", "_id": 7} }
{ "num" : 7 }
{ "create" : { "_index" : "src_index", "_id": 8} }
{ "num" : 8 }
reindex:
POST _reindex
{
"source": {
"index": "src_index"
}
,
"dest": {
"index": "dst_index"
},
"script": {
"lang": "painless",
"source": """
if (ctx._source.num < 5)
{
ctx._index = "dst_index_5";
}
else
{
ctx._index = "dst_index_10";
}
"""
}
}
執(zhí)行后發(fā)現(xiàn),數(shù)據(jù)確實被分配到dst_index_5和dst_index_10兩個索引中去了。
總結(jié)
本文以ES官方文檔為基礎(chǔ)(https://www.elastic.co/guide/en/elasticsearch/reference/7.2/docs-reindex.html),總結(jié)了一下基本用法。可能有一些疏漏。其中在reindex過程中使用painless script的內(nèi)容沒有涉及。