【Elasticsearch从入门到精通】第15篇:Elasticsearch删除与更新API——精确操作与脚本更新
上一篇【第14篇】Elasticsearch文档检索API——GET、MGet与字段选择下一篇【第16篇】Elasticsearch批量操作API——Bulk、Reindex与跨集群索引摘要数据的删除和更新是Elasticsearch文档操作中不可或缺的环节。本文全面讲解了Elasticsearch删除与更新API的使用方法涵盖Delete API的单文档精确删除及响应码解析、Update API的三种核心用法——基于Painless脚本的灵活更新ctx._source操作、部分文档字段合并更新、upsert插入或更新的条件操作以及doc_as_upsert简写模式。同时深入介绍了Delete by Query查询删除和Update by Query查询更新的批量操作包括切片slices并行处理策略、Task API追踪长时间操作进度、取消任务和动态调整速率等高级特性。掌握这些内容将使你能够灵活应对各种数据变更场景。一、Delete API单文档删除1.1 基本用法Delete APIDELETE允许根据文档ID从索引中删除JSON文档DELETEtwitter/_doc/1如果文档存在且被成功删除响应如下{_index:twitter,_type:_doc,_id:1,_version:2,result:deleted,_shards:{total:2,successful:1,failed:0},_seq_no:1,_primary_term:1}1.2 响应码说明状态码含义说明200文档已找到并删除result为deleted404文档不存在result为not_found409版本冲突并发操作时版本不匹配注意即使文档不存在Delete API也会返回_version递增。在Elasticsearch内部旧文档只是被标记为已删除并不会立即从磁盘中移除后台的段合并过程会在适当时机清除这些标记数据。1.3 带版本控制的删除可以使用乐观并发控制来确保删除的是正确版本的文档DELETEtwitter/_doc/1?if_seq_no1if_primary_term1如果序列号不匹配操作将返回版本冲突错误。二、Update API基本用法2.1 工作原理Update API_update允许根据提供的脚本或部分文档来更新已有文档。其内部流程为从索引中获取文档Get操作运行脚本或合并部分文档Modify操作将结果重新索引Reindex操作因此Update API本质上是一次读取-修改-写入的操作。它使用版本控制来确保在Get和Reindex期间没有发生其他更新。注意此操作仍然意味着文档的完全重新索引只是减少了一些网络往返并降低了Get和Reindex操作之间版本冲突的可能性。需要启用_source才能使用此功能。三、使用Painless脚本更新3.1 Painless脚本基础Painless是Elasticsearch默认的脚本语言专门为Elasticsearch设计具有高性能和安全性。在Update API中使用脚本可以实现灵活的更新逻辑。3.2 增加计数器以下脚本将counter字段的值增加指定数量POSTtwitter/_update/1{script:{source:ctx._source.counter params.count,lang:painless,params:{count:4}}}3.3 向列表添加元素向tags列表字段中添加标签如果标签已存在仍会被添加因为这是列表操作POSTtwitter/_update/1{script:{source:ctx._source.tags.add(params.tag),lang:painless,params:{tag:blue}}}3.4 从列表删除元素从标签列表中删除指定标签。需要先定位元素的位置再删除POSTtwitter/_update/1{script:{source:ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)),lang:painless,params:{tag:blue}}}3.5 添加和删除字段添加新字段POSTtwitter/_update/1{script:{source:ctx._source.new_field params.value,lang:painless,params:{value:hello}}}删除字段POSTtwitter/_update/1{script:{source:ctx._source.remove(new_field),lang:painless}}3.6 条件操作根据条件执行不同的操作。以下示例中如果tags字段包含green则删除文档否则不执行任何操作noopPOSTtwitter/_update/1{script:{source:if (ctx._source.tags.contains(green)) { ctx.op delete; } else { ctx.op noop; },lang:painless}}3.7 脚本中可用的上下文变量除了ctx._source外还可以通过ctx映射访问以下变量变量说明_index文档所属索引_type文档类型_id文档ID_version文档版本号_routing路由值_now当前时间戳_op操作类型update/noop/delete四、部分字段更新doc4.1 基本用法Update API支持传递部分文档进行更新在内部完成合并操作简单的递归合并POSTtwitter/_update/1{doc:{name:new_name,age:20}}上述请求会将name和age字段与现有的_source合并其他字段保持不变。4.2 detect_noop避免无效更新默认情况下如果doc中的字段值与原文档完全相同Elasticsearch会跳过更新响应返回result: noopPOSTtwitter/_update/1{doc:{name:new_name}}如果name的值未变响应为{_id:1,result:noop}可以通过设置detect_noop: false来禁用此行为强制执行更新POSTtwitter/_update/1{doc:{name:new_name},detect_noop:false}注意如果同时指定了doc和script则doc会被忽略。建议将部分文档字段放在脚本本身中。五、upsert与doc_as_upsert5.1 upsert操作upsert元素用于处理文档不存在则插入存在则更新的场景POSTtwitter/_update/2{script:{source:ctx._source.counter params.count,lang:painless,params:{count:1}},upsert:{counter:1}}首次执行文档不存在执行创建操作result: created。再次执行文档已存在执行脚本更新result: updated。5.2 scripted_upsert参数如果不管文档是否存在都希望运行脚本可以设置scripted_upsert: truePOSTtwitter/_update/2{scripted_upsert:true,script:{source:if (ctx._source.counter null) { ctx._source.counter 1; } else { ctx._source.counter 1; },lang:painless},upsert:{}}5.3 doc_as_upsert参数doc_as_upsert是一种简写模式当文档不存在时使用doc的内容作为新文档插入POSTtwitter/_update/2{doc:{name:kimchy,counter:1},doc_as_upsert:true}5.4 Update API三种模式对比模式参数文档存在时文档不存在时适用场景脚本更新script执行脚本报错复杂逻辑更新部分更新doc合并字段报错简单字段更新条件插入upsert执行脚本插入upsert内容计数器、状态管理脚本插入scripted_upsert执行脚本执行脚本脚本统一处理初始化文档插入doc_as_upsert合并字段插入doc内容简单的插入或更新六、Delete by Query查询删除6.1 基本用法Delete by Query_delete_by_query对每个与查询匹配的文档执行删除操作POSTtwitter/_delete_by_query{query:{match:{message:some message}}}响应如下{took:438,timed_out:false,total:119,deleted:119,batches:1,version_conflicts:0,noops:0,retries:{bulk:0,search:0},throttled_millis:0,requests_per_second:-1,throttled_until_millis:0,failures:[]}6.2 版本冲突处理_delete_by_query在启动时获取索引快照使用内部版本控制删除匹配内容。如果文档在快照和删除请求之间被修改会出现版本冲突。通过设置conflictsproceed可以让操作在版本冲突时继续执行POSTtwitter/_delete_by_query?conflictsproceed{query:{match_all:{}}}6.3 多索引操作可以同时对多个索引执行查询删除POSTtwitter,blog/_delete_by_query{query:{match_all:{}}}6.4 关键参数说明参数说明默认值refresh完成后刷新所有涉及分片不刷新scroll_size滚动批次大小1000requests_per_second限速-1为不限速-1wait_for_completion是否等待完成false返回Tasktruetimeout每个写请求的超时时间1mscroll搜索上下文保持时间5mslices切片数量并行处理16.5 切片并行处理_delete_by_query支持切片滚动使删除过程可以并行执行。自动切片POSTtwitter/_delete_by_query?conflictsproceedslicesauto{query:{match_all:{}}}auto将为每个分片使用一个切片。手动切片时建议遵循以下准则切片数等于索引分片数时性能最佳删除性能随切片数和可用资源线性扩展切片数不要大于分片数量否则会增加额外开销七、Update by Query查询更新7.1 基本用法Update by Query_update_by_query对索引中的每个文档执行更新操作POSTtwitter/_update_by_query?conflictsproceed上述请求会重新索引所有文档适用于获取新添加的映射字段。7.2 带查询条件的更新结合DSL查询只更新匹配特定条件的文档POSTtwitter/_update_by_query{query:{term:{user:kimchy}}}7.3 使用脚本更新可以在更新中使用脚本修改文档内容POSTtwitter/_update_by_query{query:{term:{user:kimchy}},script:{source:ctx._source.likes,lang:painless}}7.4 脚本中的ctx.op控制在_update_by_query的脚本中可以设置ctx.op来控制对文档的操作ctx.op值行为update默认更新文档noop跳过该文档delete删除该文档7.5 获取新映射属性_update_by_query的一个重要应用场景是获取新添加的映射属性。例如PUTtwitter/_mapping{properties:{flag:{type:keyword}}}POSTtwitter/_update_by_query这样所有文档都会被重新索引新添加的flag字段会被提取出来。八、Task API追踪操作进度8.1 获取任务状态对于长时间运行的查询删除或查询更新操作可以使用Task API追踪进度GET_tasks?detailedtrueactions*/delete/byquery或者使用任务ID直接查询GET_tasks/r1A2WoRbTwKZ516z6NEs5A:36619响应中的status字段包含任务的实际进度信息{completed:false,task:{node:r1A2WoRbTwKZ516z6NEs5A,id:36619,type:transport,action:indices:data/write/delete/byquery,status:{total:119,deleted:60,batches:1,version_conflicts:0,noops:0,retries:{bulk:0,search:0}}}}8.2 异步执行设置wait_for_completionfalse让操作在后台执行立即返回任务IDPOSTtwitter/_delete_by_query?wait_for_completionfalse{query:{match_all:{}}}8.3 取消任务使用取消任务API终止正在运行的删除/更新操作POST_tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel8.4 动态调整速率在运行时使用_rethrottleAPI动态调整操作速率POST_delete_by_query/r1A2WoRbTwKZ516z6NEs5A:36619/_rethrottle?requests_per_second-1九、总结与最佳实践9.1 核心要点回顾Delete API提供精确的单文档删除能力支持乐观并发控制Update API支持三种模式脚本更新灵活复杂、doc部分更新简单直接、upsert条件插入Painless脚本是更新的核心工具通过ctx._source操作文档字段Delete/Update by Query实现批量删除和更新支持切片并行处理Task API提供长时间操作的进度追踪、取消和速率调整能力detect_noop和conflictsproceed是优化更新性能的重要手段9.2 生产环境最佳实践优先使用doc部分更新简单的字段修改优先使用doc而非脚本性能更好合理使用upsert计数器、状态管理等场景使用upsert避免先查后写的竞态条件批量操作切片化大数据量的_delete_by_query和_update_by_query务必使用切片并行速率控制在业务低峰期执行大批量删除/更新并使用requests_per_second限制对集群的影响异步执行大批量操作使用wait_for_completionfalse异步执行通过Task API监控进度上一篇【第14篇】Elasticsearch文档检索API——GET、MGet与字段选择下一篇【第16篇】Elasticsearch批量操作API——Bulk、Reindex与跨集群索引