Elasticsearch 数据迁移与任务状态相关 API

Reindex API

说明

本文所述命令,皆基于 ES_V5.4.2 版本。

本文根据 ES 官方文档进行翻译、总结而出:

https://www.elastic.co/guide/en/elasticsearch/reference/5.4/docs-reindex.html

https://www.elastic.co/guide/en/elasticsearch/reference/5.4/tasks.html

基本形式

POST _reindex
{
  "source": {
    "index": "source_index"
  },
  "dest": {
    "index": "target_index"
  }
}

将文档从 source_index 索引复制到 target_index 索引中。

注意:

​ Reindex 不会复制源索引的设置,应在执行 reindex 操作之前,提前设置好目标索引的映射、分片数等。

类似 _update_by_query ,执行 reindex 时,会在操作时间点生成一个快照,复制文档时均是快照中文档状态,因此在快照时间点之后,对源索引进行的数据写入、更新、删除等操作,均不会同步变更到目标索引中。

如果是复制文档到新索引,那么不会出现文档版本冲突的问题,否则需考虑版本冲突问题,可设置 version_type 或 op_type 参数:

POST _reindex
{
  "source": {
    "index": "source_index"
  },
  "dest": {
    "index": "target_index",
    "version_type": "external"
  }
}

参数说明:

  • version_type

    • internal:忽略文档版本,如果目标索引中恰巧有相同主键的文档,则直接覆盖;(默认)
    • external:保留源索引中文档版本,创建目标索引中没有的文档,并更新目标索引中比源索引中版本旧的文档
  • op_type

    • create:仅在目标索引中创建丢失的文档,所有现有文档将导致版本冲突

    默认情况下,版本冲突会导致 reindex 终止,可通过 “conflicts”: “proceed” 参数忽略冲突:

    POST _reindex
    {
      "conflicts": "proceed",
      "source": {
        "index": "source_index"
      },
      "dest": {
        "index": "target_index",
        "op_type": "create"
      }
    }
    

进阶形式

多索引多类型

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": ["source_index_1","source_index_2"],
    "type": ["source_index_1_type","source_index_2_type"]
  },
  "dest": {
    "index": "target_index_together"
  }
}

将 source_index_1/source_index_1_type 和 source_index_2/source_index_2_type 的文档复制到 target_index_together 索引中。

如果两个索引中存在主键相同的文档,因迭代顺序无法确定,亦无法预测目标索引中哪个文档将继续存在。

此种场景,一般不存在。

增加迁移条件

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index",
    "query": {
      "match_all": {}
    }
  },
  "dest": {
    "index": "target_index"
  }
}

只将源索引中符合 query 条件的文档复制到目标索引中。

迁移固定文档数

POST _reindex
{
  "conflicts": "proceed",
  "size": 10000,
  "source": {
    "index": "source_index",
    "sort": {
      "xxx": "desc"
    }
  },
  "dest": {
    "index": "target_index"
  }
}

按照 xxx 字段倒序排序,将源索引中前 1w 条数据复制到目标索引中。

迁移特定字段

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index",
    "_source": ["field_1","field_2"]
  },
  "dest": {
    "index": "target_index"
  }
}

将源索引中特定字段的数据复制到目标索引中。

迁移过程中修改文档

reindex 操作支持通过脚本修改文档,不同于 _update_by_query 的是,允许通过脚本修改文档元数据。

eg:增加源索引中 field_value 字段值等于 value 的源文档的版本号,并删除此字段,然后再把数据复制到目标索引:

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index"
  },
  "dest": {
    "index": "target_index",
    "version_type": "external"
  },
  "script": {
    "inline": "if (ctx._source.field_value == 'value') {ctx._version++; ctx._source.remove('field_value')}",
    "lang": "painless"
  }
}

类似 _update_by_query ,可以通过设置 ctx.op 更改在目标索引上执行的操作,接受参数:

  • noop
    • ctx.op = “noop”:if your script decides that the document doesn’t have to be indexed in the destination index. This no operation will be reported in the noop counter in the response body.
  • delete
    • ctx.op = “delete”: if your script decides that the document must be deleted from the destination index. The deletion will be reported in the deleted counter in the response body.

可以改变:

  • _id
  • _type
  • _index
  • _version
  • _routing
  • _parent

将 version 设置为 null 或从 ctx 映射中清除它,就像不在索引请求中发送版本一样,会导致在目标索引中覆盖该文档,无论目标上的版本或在 reindex 请求中使用的 version type 类型如何。

默认情况下,reindex 操作一个带路由的文档时,除非使用脚本更改了路由,否则会保留该路由。可以在 dest 请求上设置路由来更改此设置:

  • keep
    • 为文档路由设置为源索引中文档的路由。(默认)
  • discard
    • 将文档路由设置为 null
  • =
    • 将文档路由设置为
# 从源索引中拷贝 FIELD 值为 test 的文档复制到目标索引中,并设置路由为 test
POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index",
    "query": {
      "match": {
        "FIELD": "test"
      }
    }
  },
  "dest": {
    "index": "target_index",
    "routing": "=test"
  }
}

修改迁移时每批次的文档数量

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index",
    "size": 2000
  },
  "dest": {
    "index": "target_index"
  }
}

reindex 时,默认每批次复制文档数量为 1000(和 scroll 每批次默认 size 一样),可以在 source 中设置 size 改变每批次复制文档数量。

修改字段名称

将文档从源索引复制到目标索引,同时修改源索引中 old_field 字段名称为 new_field 后,再写入目标索引:

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index"
  },
  "dest": {
    "index": "target_index"
  },
  "script":{
    "inline":"ctx._source.new_field = ctx._source.remove(\"old_field\")"
  }
}

多并发迁移

Reindex 支持 Sliced Scroll,分两种方式:

  • Manual slicing(手动切片)
  • Automatic slicing(自动切片)

这里我们只介绍更为方便的自动切片,手动切片可自行去 ES 官网研读。

将文档从源索引复制到目标索引,5 个并发同时 reindex:

POST _reindex?slices=5
{
  "conflicts": "proceed",
  "source": {
    "index": "source_index"
  },
  "dest": {
    "index": "target_index"
  }
}

注意:

​ slices 后面的数字,建议与源索引的主分片数保持一致,这样 reindex 速度最快。

​ ES 官方对 slices 的一些建议:

  • 不要使用较大的数字,500 有可能导致 CPU 崩溃;
  • 从查询性能角度看,在源索引中使用分片数量的倍数更有效;
  • 从查询性能角度看,使用与源索引中分片数量一样多的 slices 最有效。

从远程集群 reindex

reindex 操作支持从远程 ES 集群进行文档复制:

POST _reindex
{
  "source": {
    "remote": {
      "host": "http://otherhost:9200",
      "username": "user",
      "password": "pass"
    },
    "index": "source",
    "query": {
      "match_all": {}
    }
  },
  "dest": {
    "index": "dest"
  }
}

host 参数必须包含 scheme,host,port(eg:https://hostsname:http_port);用户名和密码是可选参数,如果存在,reindex 将使用 basic auth 连接远程 ES 节点,使用 basic auth 时务必使用 https,否则密码将以纯文本发送。

其中,源集群(远程集群)必须在 elasticsearch.yml 配置文件中添加白名单:

reindex.remote.whitelist: ["host1:http_port,host2:http_port,127.0.10.*:http_port"]

Scheme is ignored by the whitelist - only host and port are used.

使用跨集群迁移的方式,可以从任何版本的远程集群复制数据,在当前集群重新索引文档。

从远程服务器重新索引时使用堆上缓冲区,该缓冲区默认最大 100MB,如果远程索引包含非常大的文档时,默认每批次 size 为 1000,可能会报异常【Remote responded with a chunk that was too large.Use a smaller batch size.】,此时需要设置更小的批 size。

还可以使用 socket_timeout 设置远程连接的套接字读取超时,使用 connect_timeout 设置连接超时。两者默认都是 30s。

POST _reindex
{
  "source": {
    "remote": {
      "host": "http://otherhost:9200",
      "socket_timeout": "1m",
      "connect_timeout": "10s",
      "size": 100
    },
    "index": "source",
    "query": {
      "match_all": {}
    }
  },
  "dest": {
    "index": "dest"
  }
}

每批次迁移 5-15MB 数据时效率最高,与文档数量无关;

跨集群迁移,不支持设置多个 remote host,也不支持多并发迁移。

URL 参数

除了 pretty 等标准参数外,Reindex API 还支持 refresh,wait_for_completion,wait_for_active_shards,timeout,requests_per_second 。

发送 refresh url 参数时,会刷新请求写入的所有索引。与 Index API 的 refresh 参数不同,后者只刷新接收到新数据的 shard。

Task API

GET Task

当需要复制的文档数量很庞大时(30s 无法结束时就会返回超时信息),需要从集群中获取当前正在执行的 reindex 任务:

GET _tasks?actions=*reindex
GET _tasks?detailed=true&actions=*reindex

detailed=true:表示返回详细信息,包括 reindex 操作开始的时间、持续时间、复制流向、已复制文档数等

使用任务 ID 直接查找任务:

GET _tasks/taskId:number

使用任务 ID 直接查找任务时,可以与 wait_for_completion=false 集成。如果任务已完成,并且对其设置了 wait_for_completion=false,则返回结果或错误信息。而此功能的代价则是将任务信息存储在 ES 系统索引 .tasks/task/${taskId} 中,由操作者来决定何时删除此文件。

The advantage of this API is that it integrates with wait_for_completion=false to transparently return the status of completed tasks. If the task is completed and wait_for_completion=falsewas set on it them it’ll come back with a results or an error field. The cost of this feature is the document that wait_for_completion=false creates at .tasks/task/${taskId}. It is up to you to delete that document.

POST _reindex?wait_for_completion=false
{
  "conflicts":"proceed",
  "source":{
    "index":"intest01"
  },
  "dest":{
    "index":"intest08"
  }
}
返回:
{
  "task": "9f3iS6DOQEifLfECqTM9Fw:546249049"
}
使用任务 ID 直接查找任务:
GET _tasks/9f3iS6DOQEifLfECqTM9Fw:546249049
返回:
{
  "completed": true,
  "task": {
    "node": "9f3iS6DOQEifLfECqTM9Fw",
    "id": 546249049,
    "type": "transport",
    "action": "indices:data/write/reindex",
    "status": {
      "total": 5,
      "updated": 5,
      "created": 0,
      "deleted": 0,
      "batches": 1,
      "version_conflicts": 0,
      "noops": 0,
      "retries": {
        "bulk": 0,
        "search": 0
      },
      "throttled_millis": 0,
      "requests_per_second": -1,
      "throttled_until_millis": 0
    },
    "description": "reindex from [intest01] to [intest08]",
    "start_time_in_millis": 1590110224192,
    "running_time_in_nanos": 33214557,
    "cancellable": true
  },
  "response": {
    "took": 33,
    "timed_out": false,
    "total": 5,
    "updated": 5,
    "created": 0,
    "deleted": 0,
    "batches": 1,
    "version_conflicts": 0,
    "noops": 0,
    "retries": {
      "bulk": 0,
      "search": 0
    },
    "throttled_millis": 0,
    "requests_per_second": -1,
    "throttled_until_millis": 0,
    "failures": []
  }
}

注意:

​ 此时有个大坑,官方文档中如此描述是存在问题的:

stackoverflow 对此的讨论:https://stackoverflow.com/questions/49439374/get-status-of-a-task-elasticsearch-for-a-long-running-update-query/50807976#50807976

​ The cost of this feature is the document that wait_for_completion=false creates at .tasks/task/${taskId}. It is up to you to delete that document.

​ 当时试了 n 种方式,包括 Post _tasks/taskId:number/_cancel 取消任务,均无法删除上述操作中返回的任务(“task”: “9f3iS6DOQEifLfECqTM9Fw:546249049”)。而且通过 GET _cat/tasks?v 命令,也可以清晰的看到存在此任务:

action                         task_id                          parent_task_id                   type      start_time    timestamp running_time ip           node
cluster:monitor/tasks/lists    9f3iS6DOQEifLfECqTM9Fw:546282163 -                                transport * *  *  * *
cluster:monitor/tasks/lists[n] 9f3iS6DOQEifLfECqTM9Fw:546282164 9f3iS6DOQEifLfECqTM9Fw:546282163 direct    * *  *  * *
cluster:monitor/tasks/lists[n] JlmEFzeISZur8HYnmVnBRw:259590685 9f3iS6DOQEifLfECqTM9Fw:546282163 netty     * *  *  * *
cluster:monitor/tasks/lists[n] i3Vgp2ntSj-5sl3VuQROJw:244517045 9f3iS6DOQEifLfECqTM9Fw:546282163 netty     * *  *  * *

​ 后来通过直接查询系统索引 GET .tasks/_search ,才发现 ${taskId} 实际指的是 task_id:number(9f3iS6DOQEifLfECqTM9Fw:546249049),而非 546249049 。

​ 删除任务:DELETE .tasks/task/9f3iS6DOQEifLfECqTM9Fw:546249049

使用 cat 命令获取任务列表:

GET _cat/tasks
GET _cat/tasks?detailed

Cancel Task

取消某个正在运行的任务:

POST _tasks/node_id:task_id/_cancel

注意:

​ 不是所有正在运行的任务,都可以取消,reindex 和 delete_by_query 可以取消,像 merge 操作就不可取消。在获取到正在运行的任务信息时,其中参数 “cancellable”: true 为 true 才表示此任务可以取消。

​ 另外,多并发 reindex 时,会返回很多个 task,其中有一个 parent task,如果取消了 parent task,则此次 reindex 的所有 task 都会取消,如果只想取消单个 task 以降低 reindex 的速度,则要注意不要选中了 parent task(任务信息中有相关参数描述此 task_id 是否是 parent task)。

查看当前所有迁移任务
GET _tasks?detailed=true&actions=*reindex

取消单个迁移任务
POST _tasks/node_id:task_id/_cancel

取消节点 node_id1, node_id2 上的所有迁移任务
POST _tasks/_cancel?nodes=node_id1,node_id2&actions=*reindex

取消全部的迁移任务
POST _tasks/_cancel?actions=*reindex

取消全部的删除数据任务
POST _tasks/_cancel?actions=*delete/byquery

Wait for a specific task to complete

阻塞 10s,直到 node_id:task_id 任务完成:

GET _tasks/node_id:task_id?wait_for_completion=true&timeout=10s

阻塞 10s,直到所有的 reindex 任务完成:

GET _tasks?actions=*reindex&wait_for_completion=true&timeout=10s

你可能感兴趣的:(ElasticSearch)