elasticsearch之Document APIs【Delete By Query API】

来源:互联网 发布:读书无用论 知乎 编辑:程序博客网 时间:2024/06/05 06:43

环境

虚拟机:centos7
操作系统:win7
elasticsearch:5.5

Delete By Query API

_delete_by_query 的简单用法,就是在查询匹配到的每个文档上执行删除。例如:

POST twitter/_delete_by_query{  "query": { ①    "match": {      "message": "some message"    }  }}

①:查询必须是有效的键值对,query是键,这和Search API是同样的方式。在search apiq参数和上面效果是一样的。

结果:

{  "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根据默认策略对被拒绝的请求进行重试(最多10次)。达到最大重试次数后,会造成_delete_by_query请求中止,并且会在failures字段中响应 所有的故障。已经删除的仍会执行。换句话说,该过程没有回滚,只有中断。
在第一个请求失败引起中断,失败的批处理请求的所有故障信息都会记录在failures元素中;并返回回去。因此,会有不少失败的请求。
如果你想计算有多少个版本冲突,而不是中止,可以在URL中设置为conflicts=proceed或者在请求体中设置"conflicts": "proceed"

回到api格式中,你可以在一个单一的类型(即:表)中限制_delete_by_query
下面仅仅只是删除索引(即:数据库)twitter中类型(即:表)tweet的所有数据:

POST twitter/tweet/_delete_by_query?conflicts=proceed{  "query": {    "match_all": {}  }}

一次删除多个索引(即:数据库)中的多个类型(即表)中的数据,也是可以的。例如:

POST twitter,blog/tweet,post/_delete_by_query{  "query": {    "match_all": {}  }}

如果你提供了routing,接着这个路由会被复制给scroll query,根据匹配到的路由值,来决定哪个分片来处理:

POST twitter/_delete_by_query?routing=1{  "query": {    "range" : {        "age" : {           "gte" : 10        }    }  }}

默认情况下,_delete_by_query自上而下批量1000条数据,你也可以在URL中使用参数scroll_size

POST twitter/_delete_by_query?scroll_size=5000{  "query": {    "term": {      "user": "kimchy"    }  }}

URL Parameters(url 参数)

除了标准参数像prettyDelete By Query API也支持refreshwait_for_completionwait_for_active_shardstimeout

发送带refresh参数的请求一旦完成,在delete by queryapi中涉及到的所有分片都将会刷新。这不同于Delete API中的refresh参数,其是在收到删除请求时就刷新分片。

如果请求中包含wait_for_completion=false,那么elasticsearch将会执行预检查启动请求,并返回一个可被Tasks APIs使用的task,以取消或者得到task状态。elasticsearch也将会在.tasks/task/${taskId}路径中创建一个文档来记录这个task。你可以根据自己的情况来选择保留还是删除它;当你删除后,elasticsearch会回收利用它的空间。

在处理请求之前,wait_for_active_shards控制需要多少个副本分片必须处于活动状态。详情这里。timeout用于控制每个写请求等待不可用分片变成可用分片的时间。两者都能在Bulk API中正常工作。

requests_per_second可以设置任何正的十进制数字(1.4、6、1000等等)并且可以限制delete-by-query发出的每秒请求数量或者将其设置为-1来禁用这种限制。这种限制会在批处理之间等待,以便于其能操作scroll timeout。这个等待时间与完成批处理之间的时间和requests_per_second * requests_in_the_batch时间是有区别的。由于批处理不会分解成多个请求,而如此大的批处理将会造成elasticsearch创建多个请求并且会在开始下个集合(批处理)之前等待一会,这是bursty而不是smooth。默认为-1

Response body(响应体)

响应体的json格式如下:

{  "took" : 639,  "deleted": 0,  "batches": 1,  "version_conflicts": 2,  "retries": 0,  "throttled_millis": 0,  "failures" : [ ]}
参数 描述 took 从整个操作开始到结束花费的时间,单位是毫秒 deleted 成功删除文档的数量 batches 通过delete by query返回滚动响应的数量(我的看法:符合delete by query条件的文档数量) version_conflicts delete by queryapi命中的冲突版本的数量(即在执行过程中,发生了多少次冲突) retries 在delete by query api响应一个完整队列,重试的次数 throttled_millis 根据requests_per_second,请求睡眠多少毫秒 failures 是个数组,表示失败的所有索引(插入);如果它不为空的话,那么请求会因为故障而中止。可以参考如何防止版本冲突而中止操作。

Works with the Task API

你可以使用Task API来获取任何一个正在运行的delete-by-query请求的状态。

GET _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操作的数量。你可以通过加入的updatedcreateddeleted字段来预估进度。但它们之和等于total字段时,请求将结束。

使用task id可以直接查找此task

GET /_tasks/taskId:1

这个api的优点是它整合了wait_for_completion=false来透明的返回已完成任务的状态。如果此任务完成并且设置为wait_for_completion=false,那么其将返回results或者error字段。这个特性的代价就是当设置wait_for_completion=false时,会在.tasks/task/${taskId}中创建一个文档。当然你也可以删除这个文档。

Works with the Cancel Task API

任何一个Delete By Query都可以使用Task Cancel API来取消掉:

POST _tasks/task_id:1/_cancel

可以使用上面的task api来找到task_id
取消应该尽快发生,但是也可能需要几秒钟,上面的task 状态 api将会进行列出task直到它被唤醒并取消自己。

Rethrottling

requests_per_second的值可以在使用_rethrottle参数的正在运行的delete by queryapi上进行更改:

POST _delete_by_query/task_id:1/_rethrottle?requests_per_second=-1

使用上面的tasks API来查找task_id

就像在_delete_by_query中设置一样,requests_per_second可以设置-1来禁止这种限制或者任何一个10进制数字,像1.7或者12来限制到这种级别。加速查询的Rethrottling会立即生效,但是缓慢查询的Rethrottling将会在完成当前批处理后生效。这是为了防止scroll timeouts

Manually slicing

Delete-by-query支持Sliced Scroll,其可以使你相对容易的手动并行化进程:

POST twitter/_delete_by_query{  "slice": {    "id": 0,    "max": 2  },  "query": {    "range": {      "likes": {        "lt": 10      }    }  }}POST twitter/_delete_by_query{  "slice": {    "id": 1,    "max": 2  },  "query": {    "range": {      "likes": {        "lt": 10      }    }  }}

你可以通过以下方式进行验证:

GET _refreshPOST twitter/_search?size=0&filter_path=hits.total{  "query": {    "range": {      "likes": {        "lt": 10      }    }  }}

像下面这样只有一个total是合理的:

{  "hits": {    "total": 0  }}

Automatic slicing

你也可以使用Sliced Scrolldelete-by-query api自动并行化,以在_uid上切片:

POST twitter/_delete_by_query?refresh&slices=5{  "query": {    "range": {      "likes": {        "lt": 10      }    }  }}

你可以通过以下来验证:

POST twitter/_search?size=0&filter_path=hits.total{  "query": {    "range": {      "likes": {        "lt": 10      }    }  }}

像下面的total是一个合理的结果:

{  "hits": {    "total": 0  }}

添加slices_delete_by_query将会自动执行上面部分中使用手动处理的部分,创建子请求这意味着有些怪事:

  1. 你可以在Tasks APIs中看到这些请求。这些子请求是使用了slices请求任务的子任务。
  2. 为此请求(使用了slices)获取任务状态仅仅包含已完成切片的状态。
  3. 这些子请求都是独立寻址的,例如:取消和rethrottling.
  4. Rethrottling the request with slices will rethrottle the unfinished sub-request proportionally.
  5. 取消slices请求将会取消每个子请求。
  6. 由于slices的性质,每个子请求并不会得到完全均匀的文档结果。所有的文档都将要处理,但是有些slices(切片)会大些,有些会小些。希望大的slices(切片)有更均匀的分配。
  7. slices请求中像requests_per_secondsize参数,按比例分配给每个子请求。结合上面的关于分配的不均匀性,你应该得出结论:在包含slices_delete_by_query请求中使用size参数可能不会得到正确大小的文档结果。
  8. 每个子请求都会获得一个略微不同的源索引快照,尽管这些请求都是大致相同的时间。

Picking the number of slices

这里我们有些关于slices数量的建议(如果是手动并行的话,那么在slice api就是max参数):

  1. 不要使用大数字。比如500,将会创建相当大规模的CPU震荡。
    这里说明下震荡(thrashing)的意思:
    cpu大部分时间都在进行换页,而真正工作时间却很短的现象称之为thrashing (震荡)
  2. 从查询性能角度来看,在源索引中使用多个分片是更高效的。
  3. 从查询性能角度来看,在源索引中使用和分片相同的数量是更高效的。
  4. 索引性能应该在可利用slices之间进行线性扩展。
  5. 索引(插入)或查询性能是否占主导地位取决于诸多因素,比如:重新索引文档和集群进行重新索引。

参考地址:
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html