Elasticsearch删除数据之_delete_by_query
es参考版本:elasticsearch:5.5
_delete_by_query会删除所有query语句匹配上的⽂档,⽤法如下:
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"name": "测试删除"
}
}
}
'
返回数据格式,告诉你⽤时和删除多少数据等
{
"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的所有数据:
curl -X POST "localhost:9200/twitter/_doc/_delete_by_query?conflicts=proceed" -H 'Content-Type: application/json' -d'
{
"query": {
"match_all": {}
}
}
'
⼀次删除多个索引(即:数据库)中的多个类型(即表)中的数据,也是可以的。例如:
curl -X POST "localhost:9200/twitter,blog/_docs,post/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"query": {
"match_all": {}
}
}
'
如果你提供了routing,接着这个路由会被复制给scroll query,根据匹配到的路由值,来决定哪个分⽚来处理:
curl -X POST "localhost:9200/twitter/_delete_by_query?routing=1" -H 'Content-Type: application/json' -d'
{
"query": {
"range" : {
"age" : {
"gte" : 10
}
}
}
}
'
默认情况下,_delete_by_query⾃上⽽下批量1000条数据,你也可以在URL中使⽤参数scroll_size:
curl -X POST "localhost:9200/twitter/_delete_by_query?scroll_size=5000" -H 'Content-Type: application/json' -d'
{
"query": {
"term": {
"user": "kimchy"
}
}
}
'
URL Parameters(url 参数)
除了标准参数像pretty,Delete By Query API也⽀持refresh、wait_for_completion、wait_for_active_shards和timeout。
发送带refresh参数的请求⼀旦完成,在delete by query api中涉及到的所有分⽚都将会刷新。这不同于Delete API中的refresh参数,其是在收到删除请求时就刷新分⽚。
如果请求中包含wait_for_completion=false,那么elasticsearch将会执⾏预检查、启动请求,并返回⼀个可被Tasks APIs使⽤的task,以取消或者得到task状态。elasticsearch也将会
在.tasks/task/${taskId}路径中创建⼀个⽂档来记录这个task。你可以根据⾃⼰的情况来选择保留还是删除它;当你删除后,elasticsearch会回收利⽤它的空间。
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,
"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请求的状态。
curl -X GET "localhost:9200/_tasks?detailed=true&actions=*/delete/byquery"
响应
{
delete in"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" : {    [](/guide/en/elasticsearch/reference/current/docs-delete-by-query.ht
ml#CO38-1)![](upload-images.jianshu.io/upload_images/4097351-8117f89c35e1e6d2.png?imageMogr2/auto-orient/strip%7CimageV            "total" : 6154,
"updated" : 0,
"created" : 0,
"deleted" : 3500,
"batches" : 36,
"version_conflicts" : 0,
"noops" : 0,
"retries": 0,
"throttled_millis": 0
},
"description" : ""
}
}
}
}
}</pre>
①这个对象包含实际的状态。响应体是json格式,其中total字段是⾮常重要的。total表⽰期望执⾏reindex操作的数量。你可以通过加⼊的updated、created和deleted字段来预估
进度。但它们之和等于total字段时,请求将结束。
使⽤task id可以直接查此task。
curl -X GET "localhost:9200/_tasks/taskId:1"
这个api的优点是它整合了wait_for_completion=false来透明的返回已完成任务的状态。如果此任务完成并且设置为wait_for_completion=false,那么其将返回results或者error字
段。这个特性的代价就是当设置wait_for_completion=false时,会在.tasks/task/${taskId}中创建⼀个⽂档。当然你也可以删除这个⽂档。
curl -X POST "localhost:9200/_tasks/task_id:1/_cancel"
可以使⽤上⾯的task api来到task_id;
取消应该尽快发⽣,但是也可能需要⼏秒钟,上⾯的task 状态 api将会进⾏列出task直到它被唤醒并取消⾃⼰。
curl -X POST "localhost:9200/_delete_by_query/task_id:1/_rethrottle?requests_per_second=-1"
Rethrottling
requests_per_second的值可以在使⽤_rethrottle参数的正在运⾏的delete by queryapi上进⾏更改:
curl -X POST "localhost:9200/_delete_by_query/task_id:1/_rethrottle?requests_per_second=-1"
使⽤上⾯的tasks API来查task_id
就像在_delete_by_query中设置⼀样,requests_per_second可以设置-1来禁⽌这种限制或者任何⼀个1
0进制数字,像1.7或者12来限制到这种级别。加速查询的Rethrottling会⽴
即⽣效,但是缓慢查询的Rethrottling将会在完成当前批处理后⽣效。这是为了防⽌scroll timeouts。
Manually slicing
Delete-by-query⽀持Sliced Scroll,其可以使你相对容易的⼿动并⾏化进程:
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 0,
"max": 2
},
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 1,
"max": 2
},
"range": {
"likes": {
"lt": 10
}
}
}
}
'
你可以通过以下⽅式进⾏验证:
curl -X GET "localhost:9200/_refresh"
curl -X POST "localhost:9200/twitter/_search?size=0&filter_al" -H 'Content-Type: application/json' -d'
{
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
像下⾯这样只有⼀个total是合理的:
{
"hits": {
"total": 0
}
}
Automatic slicing
你也可以使⽤Sliced Scroll让delete-by-query api⾃动并⾏化,以在_uid上切⽚:
curl -X POST "localhost:9200/twitter/_delete_by_query?refresh&slices=5" -H 'Content-Type: application/json' -d'
{
"query": {
"range": {
"likes": {
"lt": 10
}
}
}
}
'
你可以通过以下来验证:
curl -X POST "localhost:9200/twitter/_search?size=0&filter_al" -H 'Content-Type: application/json' -d'
{
"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_second和size参数,按⽐例分配给每个⼦请求。结合上⾯的关于分配的不均匀性,你应该得出结论:在包含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. 索引(插⼊)或查询性能是否占主导地位取决于诸多因素,⽐如:重新索引⽂档和集进⾏重新索引。
参考: