龙空技术网

es异步删除大量数据

没得感情的西瓜 38

前言:

今天大家对“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多线程文件清理