ElasticsearchElasticsearch 7.4的 soft-deletes 是个什么鬼
Posted 九师兄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ElasticsearchElasticsearch 7.4的 soft-deletes 是个什么鬼相关的知识,希望对你有一定的参考价值。
1.概述
转载:Elasticsearch 7.4的 soft-deletes 是个什么鬼
硬删除:【Elasticsearch】Elasticsearch如何物理删除给定期限的历史数据?
2.新概念
从 Elasticsearch 7.4 开始,peer-recovery(副分片的恢复)不再依赖从主分片拉取 translog。在 6.0-7.3的版本中,Elasticsearch默认会保留512M 或12小时的 translog 用于 peer-recovery,副分片进行恢复时,只要待获取的差异数据是在 translog 所保留的数据范围的,就可以只从 trasnlog 复制差异的部分数据,而不用拖取整个分片
。
现在,追踪主分片上的操作历史可以通过 Lucene 的新特性“软删除”来实现,不再依赖 translog。CCR 也会使用这个特性。与 Lucene 中原有的“删除”(或者说硬删除)相比,原有的“删除”会做一些标记, doc 在磁盘中还存在。然后在segment merge 的时候真正的删除这些 doc。现在既然要从 Lucene 获取操作历史,就要避免这种情况,让被删除的数据在 merge 时不被影响,所以“软删除”的概念其实和原来差不多:
- 还是标记删除,让文档看上去被删除了,用户查询不到,但磁盘上存在
- merge 操作不清理被”软删除“的 doc
- 在一定时间后,允许 merge 操作清理被”软删除“的 doc,实现保留一定期限的历史,而非无限期保留。
如果单单从效果来看,软删除和硬删除的区别就是 merge 之后,被软删除的 doc会被保留。而后通过一些其他的接口可以读到被软删除的文档
。
3.Elasticsearch 的软删除原理
soft-deletes 是 Lucene 中实现的特性,本质上是增加一个额外的字段代表doc是否被软删除,执行删除操作的时候,新增了一个 doc,在这个 doc 中将代表软删除的字段标记为1。
3.1 Lucene 的软删除过程
我们用 Lucene 的 API 演示一下删除过程,首先初始化 IndexWriter,并指定哪个字段代表软删除的字段:
String softDeletesField = "soft_delete";
indexWriterConfig.setSoftDeletesField(softDeletesField);
IndexWriter writer = new IndexWriter(dir, indexWriterConfig);
然后新增一个 doc,docId 为1,这与之前的使用方式没有什么区别:
docId = 1;
doc = new Document();
doc.add(new StringField("id", String.valueOf(docId), Field.Store.YES));
writer.updateDocument(new Term("id", String.valueOf(docId)), doc);
接着用软删除的方式删除他,实际就是创建一个新的 doc,将docId设置为要删除的 docId,并将 softDelete 字段值设置为1:
docId = 1;
doc = new Document();
doc.add(new StringField("id", String.valueOf(docId), Field.Store.YES));
doc.add(new NumericDocValuesField(softDeletesField, 1));
writer.softUpdateDocument(new Term("id", String.valueOf(docId)), doc,
new NumericDocValuesField(softDeletesField, 0));
writer.softUpdateDocument
完成了文档的软删除过程,接下来用不同的 reader 就可以读取到,或者过滤掉被软删除的文档。例如搜索分片时应该过滤掉被软删除的 doc,而 recovery 的时候需要读取所有操作历史,包括被软删除的文档。
3.2.Elasticsearch 中关于软删除的变更
现在看一下Elasticsearch中应用软删除之后,涉及到 Lucene读写API 的变更。
3.2.1 删除文档时
在 deleteInLucene 函数删除文档的时候,之前的硬删除使用Lucene deleteDocuments删除文档:
indexWriter.deleteDocuments(delete.uid());
使用软删除方式时,实现改为:
doc.add(softDeletesField);
indexWriter.softUpdateDocument(delete.uid(), doc, softDeletesField);
与我们上一个演示 Lucene 中软删除的例子类似。
3.2.2 搜索时
与之前的读取方式相比没有变化
SearchContext context = createContext(request);
创建的Context为DefaultSearchContext
,其中searcher中的 reader 为ElasticsearchDirectoryReader
,这个 reader 不会读取到被软删除的文档
。
3.2.3 恢复时
peer-recovery 的时候RecoverySourceHandler#recoverToTarget函数中,获取 translog 快照:
final Translog.Snapshot phase2Snapshot = shard.getHistoryOperations("peer-recovery", startingSeqNo);
快照最终通过 LuceneChangesSnapshot类创建,其中会初始化一个indexSearcher负责读取,他的初始化方式如下:
this.indexSearcher = new IndexSearcher(Lucene.wrapAllDocsLive(engineSearcher.getDirectoryReader()));
通过 Lucene.wrapAllDocsLive
返回一个IndexReader,这种方式创建的 reader可以获取到包括被软删除的所有的 doc
。
3.3 目前存在的问题
软删除也带来了一些负面影响。截止目前的版本为止(7.5.2),对于 update 操作,他导致 refresh 变得很慢。以下面这个 UT 为例:
testRefresh(){
while (i++<100000) {
engine.index(indexForDoc(createParsedDoc("1", null)));
}
engine.refresh("test", randomFrom(Engine.SearcherScope.values()), randomBoolean());
}
上面这个10W 条 doc 的 update 之后,engine.refresh函数的运行在我的测试环境中消耗了100多秒的时间。
refresh慢导致了另外一个问题,他确实足够慢,以至于很可能会小于数据写入速度
,indexing buffer 的内存来不及 refresh 到磁盘中。如果对节点执行 update 压测,你会发现indexWriter会暴涨到index_buffer_size配置的阈值,并持续占据这些内存
。
当 indexWriter大于index_buffer_size配置的阈值,Elasticsearch 会对写入操作执行反压,降低分片的写入速度,被反压的分片除了在执行 update 的分片,其他索引的分片也可能会受影响,因此可能会导致整体写入速度下降
。
4.总结
soft-deletes 本质上就是加了一个额外的字段表示文档被删除了
,然后在通过一些其他 api 将soft-deletes的 doc 读出来
。但是目前(v7.5.2)为止对 update 的影响比较大,如果你已经升级到这个版本,可以在写入请求中加上 ?refresh 参数,让每个请求都被 refresh,可以避免对indexWriter内存及后续的影响。
N.参考
https://www.elastic.co/cn/blog/follow-the-leader-an-introduction-to-cross-cluster-replication-in-elasticsearch
https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-7.4.0.html
https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules-history-retention.html
以上是关于ElasticsearchElasticsearch 7.4的 soft-deletes 是个什么鬼的主要内容,如果未能解决你的问题,请参考以下文章