对es搜索使用fork/join优化搜索

需求:查询某个日期区间关于吉利相关舆情

es索引库有12个,吉利相关词有50+个
查询12个es索引库,查询出日期区间的结果 且 满足这50+词中其中一个词则将对应的文章查询出来

    1个索引  
        查询 
            一个月区间日期 2019-10-01 2019-10-31
            多个词(吉利、帝豪....)包含这50个词的都查出来
    思路:按索引、日期、词维度来拆分
    如:按5天时间 和 10个词 一组去es查询返回,将日期区间缩短和查询的词量减少
    
    5天日期区间查询 + 10个词,
    第一组日期分组
        2019-10-01、2019-10-02、2019-10-03、2019-10-04、2019-10-05
        吉利、帝豪、Geely、博越、帝豪GL、帝豪GS、帝豪RS、帝豪吉利、帝豪汽车、远景X1
        2019-10-01、2019-10-02、2019-10-03、2019-10-04、2019-10-05
        远景SUV、远景X3、吉利自由舰、吉利熊猫、吉利金刚、吉利博瑞、吉利新博瑞、吉利英伦、英伦C5、英伦两厢
        2019-10-01、2019-10-02、2019-10-03、2019-10-04、2019-10-05
        吉利海景.......
        2019-10-01、2019-10-02、2019-10-03、2019-10-04、2019-10-05
        新美日汽车......
        2019-10-01、2019-10-02、2019-10-03、2019-10-04、2019-10-05
        吉利新美日.....
    第二组日期分组
        2019-10-06、2019-10-07、2019-10-08、2019-10-09、2019-10-10
        吉利、帝豪、Geely、博越、帝豪GL、帝豪GS、帝豪RS、帝豪吉利、帝豪汽车、远景X1
        2019-10-06、2019-10-07、2019-10-08、2019-10-09、2019-10-10
        远景SUV、远景X3、吉利自由舰、吉利熊猫、吉利金刚、吉利博瑞、吉利新博瑞、吉利英伦、英伦C5、英伦两厢
        2019-10-06、2019-10-07、2019-10-08、2019-10-09、2019-10-10
        吉利海景.......
        2019-10-06、2019-10-07、2019-10-08、2019-10-09、2019-10-10
        新美日汽车......
        2019-10-06、2019-10-07、2019-10-08、2019-10-09、2019-10-10
        吉利新美日.....
    第三组日期分组.......
        ......
        ......
    第四组日期分组.......
        ......
        ......
    第五组日期分组.......
        ......
        ......
    (30/5)(日期组) * 5(词组) * 12(索引) = 360(一个月区组成360次查询es)
    

使用fork/join 对这360条数据做分解,以30个为基准提交搜索到es,360/30=12,需要查询12次es,如果每一次需要花费2秒,最终执行时间不会超过3秒。 

本地验证时,发现使用fork/join比单次慢,原因是因为本地cpu大多都是保持在60%使用,fork/join是cpu密集型,当cpu被暂满时就会出现线程之间的竞争等待,
所以移到支持cpu密集型的服务器测试,是会比较快。 

未优化前

优化后

public BucketHit getBrand(SentimentParam param, QueryBuilder qb, String... fieldNames) {

        List<String> dateTimes = resolveDate(param.getStartDate(),param.getEndDate());
        String startDate = param.getStartDate();
        //搜索条件list,根据日期分解搜索次数
        List<SearchSourceBuilder> searchBuilders = Lists.newArrayListWithExpectedSize(dateTimes.size() * 4);
        for (String date : dateTimes){
            //每一个查询 日期间隔5天 且 查询 10个词
            searchBuilders.add(getSearchBuilder(startDate,date,TitleTemplateUtil.geelyOrTitle().subList(0,10),param));
            searchBuilders.add(getSearchBuilder(startDate,date,TitleTemplateUtil.geelyOrTitle().subList(10,20),param));
            searchBuilders.add(getSearchBuilder(startDate,date,TitleTemplateUtil.geelyOrTitle().subList(20,30),param));
            searchBuilders.add(getSearchBuilder(startDate,date,TitleTemplateUtil.geelyOrTitle().subList(30,40),param));
            searchBuilders.add(getSearchBuilder(startDate,date,TitleTemplateUtil.geelyOrTitle().subList(40,TitleTemplateUtil.geelyOrs.size()),param));
            startDate = date;
        }

        /*
        循环索引列表,每一个索引都对应 searchBuilders.size()次 搜索
            如 索引 12 个, 时间区间 36 条
            总共查询次数 12 * 36
         */
        List<SearchRequest> srs = Lists.newArrayList();
        for (SentimentIndexEnum index : SentimentIndexEnum.values()) {
            for (SearchSourceBuilder ssb : searchBuilders) {
                SearchRequest searchRequest = new SearchRequest(index.getIndex());
                searchRequest.source(ssb);
                srs.add(searchRequest);
            }
        }
        ForkJoinPool forkJoinPool = new ForkJoinPool(20);
        long st3 = System.currentTimeMillis();   //获取开始时间

        //使用forkJoin分解任务
        List<SearchResponse> responses = forkJoinPool.invoke(new IndexForkRecursiveTask(srs));

        //同步调用
        /*
        MultiSearchRequest request = new MultiSearchRequest();
        for (SearchRequest sr : srs) {
            request.add(sr);
        }
        List<SearchResponse> responses = getMultiSearchResponse(request);
         */

        forkJoinPool.shutdown();
        long et3 = System.currentTimeMillis(); //获取结束时间
        LOGGER.info("-------总耗时--------"+Thread.currentThread().getName() + "-耗时:" + (st3 - et3) + "ms");

        BucketHit bucketHit = doGetResponse(responses, fieldNames);

        return bucketHit;
    }

    /**
     * 分解日期,按 5天维度
     * 如 sd=2019-10-01,ed=2019-10-31
     *      返回 [2019-10-06, 2019-10-11, 2019-10-16, 2019-10-21, 2019-10-26, 2019-10-31]
     * @param sd
     * @param ed
     * @return
     */
    private static List<String> resolveDate(String sd, String ed )  {
        Set<String> dateTimes = Sets.newHashSet();
        try {
            Date afterFiveDate = DateUtil.DAY.parse(sd);

            while (afterFiveDate.before(DateUtil.DAY.parse(ed))){
                afterFiveDate = DateUtil.getFetureDate(afterFiveDate,5);
                dateTimes.add(DateUtil.DAY.format(afterFiveDate));
            }
            dateTimes.add(ed);
            return dateTimes.stream().sorted().collect(Collectors.toList());
        }catch (Exception e){

        }


        return null;
    }
public class IndexForkRecursiveTask extends RecursiveTask<List<SearchResponse>> {
    private static final Logger LOGGER = LoggerFactory.getLogger(IndexForkRecursiveTask.class);

    private int num = 20;
    private List<SearchRequest> searchRequests;

    private static EsClient esClient;

    List<IndexForkRecursiveTask> tasks;

    public IndexForkRecursiveTask(List<SearchRequest> searchRequests) {
        this.searchRequests = searchRequests;
    }

    @Override
    protected List<SearchResponse> compute() {
        tasks = Lists.newArrayList();
        if (searchRequests.size() <= num) {

            return responses(searchRequests);
        }

        // 将搜索条件分组,将大任务拆分小任务
        List<List<SearchRequest>> ssbs = getList();
        for (List<SearchRequest> l : ssbs) {
            IndexForkRecursiveTask frt = new IndexForkRecursiveTask(l);
            tasks.add(frt);
        }

        /* 执行所有任务 并汇总结果*/
        invokeAll(tasks);


        List<SearchResponse> responses = Lists.newCopyOnWriteArrayList();
        for (IndexForkRecursiveTask fr : tasks) {
            responses.addAll(fr.join());
        }
        return responses;
    }


    private List<SearchResponse> responses(List<SearchRequest> srs) {

        long st3 = System.currentTimeMillis();   //获取开始时间
        MultiSearchRequest request = new MultiSearchRequest();
        for (SearchRequest sr : srs) {
            request.add(sr);
        }
        List<SearchResponse> responses = Lists.newArrayList();
        MultiSearchResponse sr;
        RestHighLevelClient client = getEsClient().getRhlClient();
        try {
            LOGGER.info("client =========="+client);
            sr = client.multiSearch(request);
            for (MultiSearchResponse.Item item : sr.getResponses()) {
                SearchResponse response = item.getResponse();
                responses.add(response);
            }
        } catch (Exception e) {
            LOGGER.error("IndexRecursiveTask compute error ", e);
        } finally {
            esClient.close(client);
        }
        long et3 = System.currentTimeMillis(); //获取结束时间
        LOGGER.info(Thread.currentThread().getName() + "-耗时:" + (st3 - et3) + "ms");
        return responses;
    }

    //将多个查询拆小,每组num条
    private List<List<SearchRequest>> getList() {
        List<List<SearchRequest>> ret = Lists.newArrayList();
        int size = searchRequests.size() - 1;
        if (size <= num) {
            ret.add(searchRequests);
            return ret;
        }
        int start = 0;
        int end = num;
        for (int i = start; i < end; ) {
            if (end <= size) {
                ret.add(searchRequests.subList(start, end));
                start = end;
                end = end + num;
            } else {
                ret.add(searchRequests.subList(start, size));
                break;
            }
        }

        return ret;
    }


    private EsClient getEsClient() {
        if (esClient == null) {
            esClient = (EsClient) SpringBeanUtils.getApplicationContext().getBean("esClient");
        }
        return esClient;
    }
}


使用fork/join问题:
如果分解出A、B、C线程,A先执行完,去窃取C线程的任务,A先执行花费1ms、B、C执行也花费1ms,但是A先执行完去窃取C任务导致A现在多负担了一个任务,最后A执行花费2ms,
需要每个任务对应一个线程解决这问题?但是fork/join是不是失去意义。”任务太少拆解问题“


如果不分解则会查询出4秒。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值