Elasticsearch 删除、更改文档的过程?
参考回答
在 Elasticsearch 中,删除和更改文档不是直接修改原有文档,而是采用一种逻辑上的标记和重建机制。具体过程如下:
- 删除文档:
删除操作并不会立即将文档从磁盘中移除,而是给文档打上一个“删除标记”(deleted flag)。然后在段(segment)合并时,真正从磁盘上移除这些被标记为删除的文档。 -
更改文档:
Elasticsearch 并不支持直接修改文档内容。更改文档的过程实际上是“删除旧文档+添加新文档”的组合。具体步骤如下:- 打上旧文档的删除标记。
- 将修改后的文档作为一个新文档写入索引。
- 背后的实现:
这些操作基于 Lucene 的索引机制。Lucene 是不可变的,因此这种逻辑标记和段合并的机制优化了性能。
详细讲解与拓展
为什么删除和更改文档要采用“标记”方式?
-
Lucene 的不可变性:
Elasticsearch 的底层是基于 Lucene 的,而 Lucene 的段(segment)是不可变的。一旦一个段创建后,不能直接修改其中的数据。因此删除或更新文档时,采用标记的方式是更高效的解决方案。 -
性能优化:
频繁删除或更新文档时,如果每次都立即修改磁盘内容,会导致性能开销巨大。通过打标记的方式,可以将这些操作批量处理,在后续的段合并中一次性清理。
删除文档的过程:
- 用户发出删除请求。
- Elasticsearch 给文档打上“删除标记”。
- 被删除文档的数据仍然存在磁盘上,但对用户不可见。
- 在段合并(segment merge)过程中,删除标记的文档才会被真正清理。
段合并:
段合并是 Elasticsearch 的一项后台任务,会将多个小段合并为一个大段,同时清理掉被标记为删除的文档。段合并的优点是减少段的数量,提升查询效率。
更改文档的过程:
- 用户发出更改请求。
- Elasticsearch 给旧文档打上“删除标记”。
- Elasticsearch 将新文档作为一个独立的文档写入索引中。
- 查询时,只有新文档会被用户看到,旧文档在段合并中会被清理。
举例说明:
假设有一个索引 products,其中有一篇文档如下:
{
"_id": "1",
"name": "Laptop",
"price": 1000
}
删除文档:
当你执行:
DELETE /products/_doc/1
- 文档并未从磁盘立即移除,而是被标记为删除。
- 用户再查询时不会看到该文档。
- 段合并时,该文档才会被物理移除。
更改文档:
当你执行:
POST /products/_update/1
{
"doc": {
"price": 900
}
}
实际上是两步:
1. 旧文档被标记为删除。
2. 新文档被写入,内容如下:
“`json
{
"_id": "1",
"name": "Laptop",
"price": 900
}
“`
拓展:为何如此设计?
- 写操作优先:Elasticsearch 优化写入性能,将删除/更改变成写操作(写入新文档),而不是修改已有文档。
- 段合并是异步的:段合并的异步设计保证了用户操作时的低延迟,同时让清理工作在后台完成。
注意点:
- 删除和更改频繁时,段合并的开销会增加,可能会影响性能。
- 可以通过调整
merge.policy参数优化段合并行为,减少资源消耗。
总结
在 Elasticsearch 中,删除和更改文档并不是直接修改原文档,而是通过“删除标记”和“新增文档”完成。这种机制利用了 Lucene 的不可变性特点,确保了性能和效率。段合并是删除和更改的重要组成部分,可以通过调整配置优化其行为。理解这一过程,有助于更高效地设计和使用 Elasticsearch。