作为Elasticsearch(ES)的资深用户,我们深知数据的删除操作并非简单的“一删了之”。标准的物理删除操作会在ES内部留下“tombstone”(删除标记),这些标记只有在后续的段合并(Segment Merge)过程中才会被清理,这会消耗大量的I/O和CPU资源,尤其是在高频删除的场景下,可能导致集群性能急剧下降。为了解决这一问题,我们可以采用软删除(Soft Delete)与定期物理清理相结合的策略。
1. 理解ES的物理删除机制
当我们执行一个标准的删除请求(如DELETE /index/_doc/id)时,ES并不会立即从磁盘上移除数据。它会在倒排索引中标记该文档为已删除。直到Lucene进行段合并时,这些被标记的文档才会被真正物理移除。如果删除操作过于频繁,可能会导致:
- 索引大小(磁盘空间)并不会立即减小。
- 查询效率降低(需要跳过被标记的文档)。
- 段合并任务加重,影响写入和查询性能。
2. 实施软删除(逻辑删除)
软删除是通过在文档中添加一个状态字段来实现的,它不会触发ES底层的删除标记和段合并操作。这是优化高频删除场景的关键。
步骤 2.1:定义软删除字段
我们在索引映射中添加一个布尔字段,例如 is_deleted。
PUT /my_data_index
{
"mappings": {
"properties": {
"content": { "type": "text" },
"created_at": { "type": "date" },
"is_deleted": { "type": "boolean" }
}
}
}
步骤 2.2:执行软删除操作
当用户或系统请求删除文档时,我们不使用 DELETE API,而是使用 UPDATE API来更新 is_deleted 字段。
# 假设文档ID为1
POST /my_data_index/_update/1
{
"doc": {
"is_deleted": true
}
}
步骤 2.3:修改查询逻辑
所有的查询请求都必须排除掉 is_deleted: true 的文档。这确保了业务层看不到已删除数据。
GET /my_data_index/_search
{
"query": {
"bool": {
"must_not": [
{
"term": { "is_deleted": true }
}
]
}
}
}
软删除的优势: 快速,不会立即产生ES段合并开销;保留了数据审计轨迹;容易实现数据回滚。
3. 定期执行物理清理:优化数据释放
虽然软删除避免了即时性能冲击,但长期累积的软删除数据会浪费磁盘空间和查询资源。因此,我们需要定期(例如每天凌晨或每周一次)对这些标记为删除的数据进行批量物理清理。
我们使用高效的 _delete_by_query API来批量删除所有 is_deleted: true 的文档。
物理清理操作示例
POST /my_data_index/_delete_by_query
{
"query": {
"term": {
"is_deleted": true
}
},
"conflicts": "proceed"
}
建议和优化:
- 控制并发: 如果数据量巨大,可以使用 slices 参数将任务切片,以避免长时间占用ES资源。
- 流量控制: 使用 requests_per_second 参数限制删除速度,避免对实时查询产生太大影响。
- 段合并控制: 如果物理删除后索引文件大小变化巨大,ES可能会自动触发段合并。可以考虑在低峰期手动执行 forcemerge (如果索引是只读的,且数据量适中) 来控制合并时机,进一步释放磁盘空间。
通过将频繁的业务删除转换为轻量级的软删除,并将高开销的物理删除操作集中到系统低谷期进行批量处理,可以显著提高Elasticsearch索引的数据清理效率和集群的整体稳定性。
汤不热吧