4.4 删除文档 (Delete API) Elasticsearch 核心操作:文档管理 - 4.4 删除文档 (Delete API) 详解 Delete API 概述 Delete API 允许你从指定的索引中删除一个 JSON 文档。这个操作是实时的,意味着一旦成功执行,被删除的文档将不再能够被检索到。在 Elasticsearch 内部,文档并不会立即从磁盘上物理删除,而是被标记为已删除。这种机制被称为逻辑删除,Elasticsearch 会在后台的段合并(segment merge)过程中真正地移除这些已删除的文档,回收磁盘空间。 Delete API 的基本请求格式如下: 其中: : 指定要删除文档的索引名称。 : 文档类型。
Delete API 允许你从指定的索引中删除一个 JSON 文档。这个操作是实时的,意味着一旦成功执行,被删除的文档将不再能够被检索到。在 Elasticsearch 内部,文档并不会立即从磁盘上物理删除,而是被标记为已删除。这种机制被称为逻辑删除,Elasticsearch 会在后台的段合并(segment merge)过程中真正地移除这些已删除的文档,回收磁盘空间。
Delete API 的基本请求格式如下:
DELETE /{index_name}/_doc/{_id}
其中:
{index_name}: 指定要删除文档的索引名称。
_doc: 文档类型。在 Elasticsearch 版本及之后,_doc 类型是唯一可用的类型,之前的版本可能使用自定义的类型名称。
{_id}: 要删除文档的唯一 ID。
我们首先来看一个最基本的文档删除操作的示例。假设我们有一个名为 products 的索引,并且想要删除 _id 为 AWgADGz9wBc2gU_z8Vp5 的文档。
使用 Curl 发送 DELETE 请求:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?pretty"
使用 Python Elasticsearch 客户端:
from elasticsearch import Elasticsearch es = Elasticsearch("http://localhost:9200") try: response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5") print(response) except Exception as e: print(f"删除文档时发生错误: {e}")
响应结果分析:
无论是使用 Curl 还是 Python 客户端,成功删除文档后,Elasticsearch 都会返回一个 JSON 响应。以下是一个典型的成功响应示例:
{ "_index" : "products", "_type" : "_doc", "_id" : "AWgADGz9wBc2gU_z8Vp5", "_version" : 2, "result" : "deleted", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 10, "_primary_term" : 1 }
让我们逐一解释响应中的关键字段:
_index: 操作的索引名称,这里是 "products"。
_type: 文档类型,始终为 "_doc"。
_id: 被删除文档的 ID,与请求中指定的 ID 一致。
_version: 文档的版本号。每次文档被修改(包括删除和更新),版本号都会递增。删除操作也会增加版本号,表示文档状态发生了改变。
result: 操作结果,对于成功删除,值为 "deleted"。
_shards: 分片信息,显示操作涉及的总分片数 (total)、成功的分片数 (successful) 和失败的分片数 (failed)。
_seq_no: 序列号,用于实现乐观并发控制。
_primary_term: 主 Term,也用于乐观并发控制。
文档不存在的情况:
如果尝试删除一个不存在的文档,Delete API 仍然会返回 200 OK 状态码,但 result 字段的值会变为 "not_found"。
{ "_index" : "products", "_type" : "_doc", "_id" : "non_existent_id", "_version" : 1, "result" : "not_found", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 11, "_primary_term" : 1 }
这表明 Delete API 操作本身是成功的,只是目标文档未找到。应用程序需要根据 result 字段的值来判断实际的删除结果。
基本删除操作流程图:
Delete API 除了基本用法外,还提供了一系列可选参数,用于更精细地控制删除操作,包括版本控制、路由、刷新策略等。
Elasticsearch 使用版本控制来确保数据的一致性和并发性。Delete API 也支持版本控制,允许你基于文档的当前版本来执行删除操作。
3.1.1 version 参数
version 参数允许你指定要删除文档的预期版本号。只有当文档的当前版本与指定的版本号匹配时,删除操作才会执行成功。这可以防止在并发更新场景中,由于版本冲突导致的数据丢失或不一致。
示例:
假设我们已知文档 AWgADGz9wBc2gU_z8Vp5 的当前版本是 2,我们可以使用 version 参数来删除它:
使用 Curl:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?version=2&pretty"
使用 Python 客户端:
response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", version=2) print(response)
如果文档的当前版本不是 2,例如已经被其他操作更新为版本 3,那么删除操作将会失败,并返回 409 Conflict 错误。
响应示例 (版本冲突):
{ "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[_doc][AWgADGz9wBc2gU_z8Vp5]: version conflict, current version [3] is different than the one provided [2]", "index_uuid": "vQp8Y_n9R_Gg7o9aWqG1jQ", "index_name": "products" } ], "type": "version_conflict_engine_exception", "reason": "[_doc][AWgADGz9wBc2gU_z8Vp5]: version conflict, current version [3] is different than the one provided [2]", "index_uuid": "vQp8Y_n9R_Gg7o9aWqG1jQ", "index_name": "products" }, "status": 409 }
3.1.2 version_type=external 参数
version_type=external 参数允许你使用外部版本号进行版本控制。当使用外部系统作为数据源时,外部系统可能已经维护了数据的版本信息。version_type=external 允许你使用这些外部版本号来管理 Elasticsearch 中的文档版本。
当 version_type 设置为 external 时,Elasticsearch 会比较请求中指定的 version 和文档的当前版本。如果指定的 version 大于文档的当前版本,删除操作才会执行,并且文档的版本号会被更新为指定的 version。这允许外部系统控制 Elasticsearch 中的文档版本,并确保版本号始终递增。
示例:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?version=10&version_type=external&pretty"
response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", version=10, version_type="external") print(response)
如果文档的当前版本大于或等于 10,删除操作将会失败,并返回 409 Conflict 错误。
版本控制流程图:
if_seq_no 和 if_primary_term除了基于版本号的版本控制,Elasticsearch 还提供了基于序列号 (_seq_no) 和主 Term (_primary_term) 的乐观并发控制机制。这是一种更现代且更可靠的并发控制方式,可以替代基于版本号的控制。
if_seq_no: 指定文档的预期序列号。
if_primary_term: 指定文档的预期主 Term。
只有当文档的当前序列号和主 Term 与指定的 if_seq_no 和 if_primary_term 完全匹配时,删除操作才会执行成功。序列号和主 Term 是 Elasticsearch 内部用于跟踪文档变更的机制,它们比版本号更精确,能够更好地处理并发更新和删除操作。
示例:
假设我们已知文档 AWgADGz9wBc2gU_z8Vp5 的当前序列号是 10,主 Term 是 1。
使用 Curl:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?if_seq_no=10&if_primary_term=1&pretty"
使用 Python 客户端:
response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", if_seq_no=10, if_primary_term=1) print(response)
如果文档的序列号或主 Term 不匹配,删除操作将会失败,并返回 409 Conflict 错误。
乐观并发控制流程图:
routing 参数在 Elasticsearch 中,文档可以根据路由值被分配到特定的分片。如果你在索引文档时使用了路由,那么在删除文档时也需要指定相同的路由值,以确保操作被路由到正确的shard。
示例:
假设我们在索引 products 时使用了路由字段 user_id,并且要删除 _id 为 AWgADGz9wBc2gU_z8Vp5,user_id 为 user123 的文档。
使用 Curl:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?routing=user123&pretty"
使用 Python 客户端:
response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", routing="user123") print(response)
如果删除请求没有指定正确的 routing 值,Elasticsearch 可能无法找到目标文档,或者操作会被路由到错误的分片,导致删除失败。
timeout 参数timeout 参数允许你设置删除操作的超时时间。如果在指定的超时时间内操作未能完成,Elasticsearch 将会中断操作并返回错误。超时时间可以使用时间单位,例如 1s (1 秒), 10ms (10 毫秒), 2m (2 分钟) 等。
示例:
设置删除操作超时时间为 1 秒:
使用 Curl:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?timeout=1s&pretty"
使用 Python 客户端:
response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", timeout="1s") print(response)
refresh 参数refresh 参数控制删除操作对搜索可见的时间。Elasticsearch 默认情况下是近实时搜索,文档的索引和删除操作并不会立即对搜索可见,而是需要等待下一次刷新操作。refresh 参数允许你显式地控制刷新行为。
refresh 参数可以设置为以下值:
true: 在删除操作完成后立即刷新相关的分片,使删除操作立即对搜索可见。这会增加索引的负载,但可以保证实时性。
wait_for: 等待刷新操作完成。与 true 类似,但会等待刷新操作完成再返回响应。
false (默认值): 不执行显式刷新。依赖 Elasticsearch 的后台刷新机制。
示例:
在删除操作后立即刷新:
使用 Curl:
curl -X DELETE "localhost:9200/products/_doc/AWgADGz9wBc2gU_z8Vp5?refresh=true&pretty"
使用 Python 客户端:
response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", refresh=True) print(response)
注意: 频繁地使用 refresh=true 或 refresh=wait_for 可能会降低索引性能,特别是在高写入负载的情况下。通常建议使用默认的 refresh=false 策略,让 Elasticsearch 自动管理刷新。
_source 参数 (了解即可)_source 参数在 Delete API 中并不常用,但为了完整性,我们简单介绍一下。_source 参数用于控制是否返回被删除文档的 _source 字段。默认情况下,Delete API 不会返回 _source 字段。你可以设置 _source=true 来返回 _source 字段,但这通常没有实际意义,因为文档已经被删除了。
在进行文档删除操作时,可能会遇到各种错误和异常情况。了解如何处理这些情况对于构建健壮的应用程序至关重要。
常见的错误情况包括:
404 Not Found: 索引或文档不存在。
409 Conflict: 版本冲突或乐观并发控制失败。
408 Request Timeout: 请求超时。
503 Service Unavailable: Elasticsearch 集群暂时不可用。
在 Python Elasticsearch 客户端中,可以使用 try-except 块来捕获异常并进行处理。
示例:
from elasticsearch import Elasticsearch from elasticsearch.exceptions import NotFoundError, ConflictError, ConnectionError es = Elasticsearch("http://localhost:9200") try: response = es.delete(index="products", id="AWgADGz9wBc2gU_z8Vp5", version=2) print(response) except NotFoundError as e: print(f"索引或文档未找到: {e}") except ConflictError as e: print(f"版本冲突或并发控制失败: {e}") except ConnectionError as e: print(f"连接错误: {e}") except Exception as e: print(f"其他错误: {e}")
在实际应用中,应该根据具体的业务需求来处理不同的错误类型。例如,对于版本冲突错误,可以尝试重新获取最新的文档版本并重试删除操作;对于索引或文档未找到错误,可以根据业务逻辑判断是否需要创建索引或文档。
虽然本文主要关注单个文档的删除,但 Elasticsearch 也提供了 Bulk API,用于高效地批量执行删除操作。Bulk API 可以显著减少网络开销,提高删除效率,特别是在需要删除大量文档时。
Bulk Delete 操作的基本格式如下:
POST /_bulk { "delete" : { "_index" : "products", "_id" : "AWgADGz9wBc2gU_z8Vp5" } } { "delete" : { "_index" : "products", "_id" : "another_id" } } ...
使用 Python 客户端进行批量删除:
bulk_actions = [ {"delete": {"_index": "products", "_id": "AWgADGz9wBc2gU_z8Vp5"}}, {"delete": {"_index": "products", "_id": "another_id"}}, # ... more delete actions ] from elasticsearch.helpers import bulk try: response = bulk(es, bulk_actions) print(response) # 返回 (成功操作数, 错误列表) except Exception as e: print(f"批量删除操作发生错误: {e}")
Bulk API 的响应会包含每个操作的执行结果,包括成功和失败的操作。应用程序需要解析响应,处理批量操作中可能出现的错误。
谨慎删除数据: 删除操作是不可逆的,请务必谨慎操作,确保删除的是真正不再需要的数据。
版本控制与并发控制: 在高并发场景下,强烈建议使用版本控制或乐观并发控制,防止数据不一致问题。
批量删除优化性能: 当需要删除大量文档时,优先考虑使用 Bulk API 进行批量删除,以提高性能。
监控删除操作: 监控删除操作的性能和错误率,及时发现和解决问题。
了解逻辑删除: 理解 Elasticsearch 的逻辑删除机制,文档并不会立即物理删除,磁盘空间回收需要等待段合并。
数据保留策略: 根据业务需求制定合理的数据保留策略,定期清理过期或不再需要的数据,维护索引的健康状态。
避免频繁刷新: 除非有严格的实时性要求,否则避免频繁使用 refresh=true 或 refresh=wait_for,以减少索引负载。
测试删除操作: 在生产环境执行删除操作前,务必在测试环境中进行充分的测试,验证删除逻辑和参数设置的正确性。
Elasticsearch 的 Delete API 是文档管理中不可或缺的一部分。本文详细介绍了 Delete API 的基本用法、高级参数、代码实践、错误处理以及最佳实践。掌握 Delete API 的各种功能和选项,可以帮助你更有效地管理 Elasticsearch 中的文档数据,维护索引的性能和健康状态。在实际应用中,请根据具体的业务场景和需求,合理选择 Delete API 的参数和策略,确保数据删除操作的安全、高效和可靠。