本文所述命令,皆基于 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
op_type
默认情况下,版本冲突会导致 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
counter in the response body.deleted
counter in the response body.可以改变:
将 version 设置为 null 或从 ctx 映射中清除它,就像不在索引请求中发送版本一样,会导致在目标索引中覆盖该文档,无论目标上的版本或在 reindex 请求中使用的 version type 类型如何。
默认情况下,reindex 操作一个带路由的文档时,除非使用脚本更改了路由,否则会保留该路由。可以在 dest 请求上设置路由来更改此设置:
# 从源索引中拷贝 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,分两种方式:
这里我们只介绍更为方便的自动切片,手动切片可自行去 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 操作支持从远程 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,也不支持多并发迁移。
除了 pretty 等标准参数外,Reindex API 还支持 refresh,wait_for_completion,wait_for_active_shards,timeout,requests_per_second 。
发送 refresh url 参数时,会刷新请求写入的所有索引。与 Index API 的 refresh 参数不同,后者只刷新接收到新数据的 shard。
当需要复制的文档数量很庞大时(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 andwait_for_completion=false
was set on it them it’ll come back with aresults
or anerror
field. The cost of this feature is the document thatwait_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
取消某个正在运行的任务:
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
阻塞 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