前言:
今天大家对“java多线程文件清理”大约比较讲究,同学们都想要剖析一些“java多线程文件清理”的相关知识。那么小编在网上收集了一些有关“java多线程文件清理””的相关文章,希望你们能喜欢,同学们一起来学习一下吧!声明:这是我工作遇到的问题,解决方式也不一定很好,大家有好的建议希望能留言支持,谢谢啦!
前段时间由于我们项目的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了。
山高路远,看世界,也找自己。去摆烂吧,去摆不被定义的烂。
标签: #java多线程文件清理