es如何清空索引数据(es异步删除大量数据)
es如何清空索引数据(es异步删除大量数据)我本地试了几次后,在网上也找了资料,没有找到答案,我判断是因为,每次删除一万条的时候,分多次删除,每次DeleteByQueryAction这个工具类删除应该是es接受命令后就异步执行了,但是并没有真的删除干净(es后台自己删除中,是需要一定时间的),这时候我们的线程已经开始又一次删除一万条,多次删除请求重叠,就出现现在的场景没有删除干净。具体原因可能是es删除每次都从当前节点偏移一万条,所以删除重复或者其他原因,这个我目前还没有确定的答案。经过我多次测试,我发现个问题,我本地跑的时候,测试的都是很少的数据,当时是没问题的,但是测试跑的测试环境,所有数据都很齐全,定的也是一下子删除一万条,当时测试环境es索引数据大概有个100万条,删除的时候就会出现没有删除完全的问题。无奈,我在网上找了个分页删除的方法,一次删除一万条数据的批量删。代码如下 /** * 删除es相关的老化数
声明:这是我工作遇到的问题,解决方式也不一定很好,大家有好的建议希望能留言支持,谢谢啦!
前段时间由于我们项目的mysql和es有些索引因为每天产生的数据量有几万到几十万不等,所有需要弄个数据老化机制,来定时删除太久的老数据。当时主管提醒我,删除mysql数据库要记得分页删除,至于es你自己网上看看有啥需要注意的。
直接无脑删除我当时没有考虑太多,心里想我这是定时任务,大半夜删除,怕什么数量太多超时呢,就直接DeleteByQueryAction搞起来了。根据索引,加上过期时间过滤,进行删除处理了。
/**
* 删除es相关的老化数据
*
* @param esIndexs es索引名称
* @param esIndexEvent es字段
* @param expireDate 过期时间
*/
private void deleteFromEs(String[] esIndexs String esIndexEvent String expireDate) {
for (String esIndex : esIndexs) {
try {
final boolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
final RangeQueryBuilder timeRangeQueryBuilder = QueryBuilders.rangeQuery(esIndexEvent);
timeRangeQueryBuilder.lte(expireDate);
boolQueryBuilder.filter(timeRangeQueryBuilder);
DeleteByQueryAction.INSTANCE
.newRequestBuilder(client)
.filter(boolQueryBuilder)
.source(esIndex)
.get();
} catch (Exception e) {
log.error("deleteFromEs is fail... esIndexs: {}" esIndex);
log.error("Exception" e);
}
}
}
这么写好处是简单,但是被我主管review代码发现后,他把我批斗了,说线上数据量过多,会占用es内存,删除大量数据会导致卡死 选择业务低峰 少批量删除,就算半夜也不要这样删除,半夜也可能会用到,让我优化。
少批量删除无奈,我在网上找了个分页删除的方法,一次删除一万条数据的批量删。代码如下
/**
* 删除es相关的老化数据
*
* @param esIndexs es索引名称
* @param esIndexEvent es字段
* @param expireDate 过期时间
*/
private void deleteFromEs(String[] esIndexs String esIndexEvent String expireDate) {
for (String esIndex : esIndexs) {
try {
final BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
final RangeQueryBuilder timeRangeQueryBuilder = QueryBuilders.rangeQuery(esIndexEvent);
timeRangeQueryBuilder.lte(expireDate);
boolQueryBuilder.filter(timeRangeQueryBuilder);
int total = getTotalFromEs(esIndex esIndexEvent expireDate);
if(total <1) {
continue;
}
for(int i = 0; i <= total / DataAgingConstants.PAGESIZE;) {
DeleteByQueryAction.INSTANCE
.newRequestBuilder(client)
.filter(boolQueryBuilder)
.source(esIndex)
.size(DataAgingConstants.PAGESIZE)
.get();
i ;
}
} catch (Exception e) {
log.error("deleteFromEs is fail... esIndexs: {}" esIndex);
log.error("Exception" e);
}
}
}
private int getTotalFromEs(String esIndex String esIndexEvent String expireDate) {
final BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
final RangeQueryBuilder timeRangeQueryBuilder = QueryBuilders.rangeQuery(esIndexEvent);
timeRangeQueryBuilder.lte(expireDate);
boolQueryBuilder.filter(timeRangeQueryBuilder);
SearchRequestBuilder reqBuilder = client.prepareSearch(esIndex)
.setQuery(boolQueryBuilder);
SearchResponse searchResponse = reqBuilder.execute().actionGet();
if (searchResponse == null || searchResponse.getHits() == null) {
log.error("ES检索失败" esIndex;
}
SearchHits searchHits = searchResponse.getHits();
return (int) searchHits.totalHits;
}
测试妹子发现问题
这次我觉得是没有问题,稍微测试了下,按照一次删除10条,批量删除少点儿的数据,本地测试了下,没问题,就让测试去测了,结果报错到没报错,测试却说了一个奇怪的现像,说删除的数据没删除干净。
我当时时懵逼的,我说一句想必大家工作都会说的一句话,我本地跑没有问题呀,测试说是不是我哪里操作不对,我演示给你看看。我一听测试妹子这样说,我有点慌了,我说我自己看看。
排查问题经过我多次测试,我发现个问题,我本地跑的时候,测试的都是很少的数据,当时是没问题的,但是测试跑的测试环境,所有数据都很齐全,定的也是一下子删除一万条,当时测试环境es索引数据大概有个100万条,删除的时候就会出现没有删除完全的问题。
删除不干净bug原因我本地试了几次后,在网上也找了资料,没有找到答案,我判断是因为,每次删除一万条的时候,分多次删除,每次DeleteByQueryAction这个工具类删除应该是es接受命令后就异步执行了,但是并没有真的删除干净(es后台自己删除中,是需要一定时间的),这时候我们的线程已经开始又一次删除一万条,多次删除请求重叠,就出现现在的场景没有删除干净。具体原因可能是es删除每次都从当前节点偏移一万条,所以删除重复或者其他原因,这个我目前还没有确定的答案。
解决bug方法我在网上找了很多解决方法,终于不负苦心人,找到了个异步删除方法,直接上代码
long starTime = System.currentTimeMillis();
// 第一天上线ES老化数据量可能较大 采用异步执行
DeleteByQueryAction.INSTANCE
.newRequestBuilder(client)
.filter(boolQueryBuilder)
.source(esIndex)
.execute(new ActionListener<BulkByScrollResponse>() {
@Override
public void onResponse(BulkByScrollResponse bulkByScrollResponse) {
long endTime = System.currentTimeMillis();
log.info("deleteFromEs is end. esIndex: {}. deleteCounts: {}. Time consumed: {} millisecond."
esIndex bulkByScrollResponse.getStatus().getTotal() endTime - starTime);
}
@Override
public void onFailure(Exception e) {
log.error("deleteFromEs is fail... esIndexs: {}" esIndex);
log.error("Exception" e);
}
});
代码采用DeleteByQueryAction的异步执行方法,重写ActionListener监听器的onResponse和onFailure方法,很好理解,前者是删除成功的时候的处理逻辑,统计下删除时间,后者是删除失败的处理逻辑,打印相关日志。
异步执行逻辑就是把删除条件代入删除es,然后会启动一个监听进程,es在删除完成时会通知这个监听进程的删除结果,这样也就不会出现删除时间过长堵塞场景了,也不会出现我们的少批量删除不干净的bug了。
- 山高路远,看世界,也找自己。
- 去摆烂吧,去摆不被定义的烂。