索引的遷移

索引的遷移

API介紹

ES提供了_reindexAPI用來進行索引的遷移。其最簡單的用法如下:

POST _reindex
{
  "source": {"index": "index-1"},
  "dest": {"index": "index-2"}
}

可選參數(shù)

  • source.index

    用來指定源索引,可以是一個或者多個,如果是多個索引,當有相同的id存在時,其行為受dest.version_typedest.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_typeexternal,并且將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)容沒有涉及。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容