基于Elasticsearch RestHighLevelClient查询未取消成功交易的方案及效率问询
当然可以直接用Elasticsearch实现,效率还更高!
从你的交易数据和需求来看,你要的是那些交易ID不存在任何CANCELLED状态记录的SUCCESS交易(比如例子里的id=2的SUCCESS交易,而id=1的SUCCESS因为有对应的CANCELLED记录要被排除)。完全不需要先查两个列表再在客户端过滤——把逻辑推到Elasticsearch端处理,不管是性能还是代码简洁度都会更好。
为什么ES端方案效率更高?
你当前的客户端过滤方案有几个明显的性能瓶颈:
- 需要传输所有SUCCESS和CANCELLED的交易数据到客户端,网络开销大
- 客户端要在内存中存储两个完整列表,数据量越大内存压力越大
- 循环过滤是单线程的本地计算,没法利用Elasticsearch的分布式并行处理能力
而ES端处理的话,会利用索引优化(比如status字段的倒排索引、id字段的聚合优化),分片并行计算,最后只返回符合条件的结果,整体效率提升非常明显。
具体实现方案
方案一:先聚合筛选有效ID,再查询对应SUCCESS交易
这种方案分两步,但都在ES端完成,逻辑清晰且高效:
- 第一步:聚合找出所有没有CANCELLED记录的交易ID
// 构建聚合请求:按id分组,过滤每个分组是否有CANCELLED记录 SearchSourceBuilder aggSource = new SearchSourceBuilder(); aggSource.aggregation( AggregationBuilders.terms("group_by_id") .field("id") // 按交易id分组 .subAggregation( AggregationBuilders.filter("has_cancelled") .filter(QueryBuilders.matchQuery("status", "CANCELLED")) ) ); SearchRequest aggRequest = new SearchRequest("你的交易索引名"); aggRequest.source(aggSource); // 执行聚合请求 SearchResponse aggResponse = restHighLevelClient.search(aggRequest, RequestOptions.DEFAULT); // 解析聚合结果,收集没有CANCELLED记录的id Terms groupByIdAgg = aggResponse.getAggregations().get("group_by_id"); List<String> validTradeIds = new ArrayList<>(); for (Terms.Bucket bucket : groupByIdAgg.getBuckets()) { Filter cancelledFilter = bucket.getAggregations().get("has_cancelled"); if (cancelledFilter.getDocCount() == 0) { // 该id下没有任何CANCELLED交易,属于有效id validTradeIds.add(bucket.getKeyAsString()); } }
- 第二步:用有效ID查询SUCCESS交易
// 构建查询请求:只查有效id下的SUCCESS交易 SearchSourceBuilder querySource = new SearchSourceBuilder(); querySource.query( QueryBuilders.boolQuery() .must(QueryBuilders.matchQuery("status", "SUCCESS")) .must(QueryBuilders.termsQuery("id", validTradeIds)) ); SearchRequest queryRequest = new SearchRequest("你的交易索引名"); queryRequest.source(querySource); // 执行查询并解析结果 SearchResponse queryResponse = restHighLevelClient.search(queryRequest, RequestOptions.DEFAULT); // 这里解析queryResponse得到你需要的List<Trade>
方案二:用子查询直接排除已取消的ID(更简洁的单查询方案)
如果你想一步完成,可以结合子查询直接排除那些存在CANCELLED记录的ID:
// 先获取所有CANCELLED交易的id列表 SearchRequest cancelledIdsRequest = new SearchRequest("你的交易索引名"); cancelledIdsRequest.source(new SearchSourceBuilder() .query(QueryBuilders.matchQuery("status", "CANCELLED")) .fetchSource("id", null)); // 只返回id字段,减少数据传输 SearchResponse cancelledIdsResponse = restHighLevelClient.search(cancelledIdsRequest, RequestOptions.DEFAULT); List<String> cancelledIds = Arrays.stream(cancelledIdsResponse.getHits().getHits()) .map(hit -> hit.getSourceAsMap().get("id").toString()) .collect(Collectors.toList()); // 构建主查询:SUCCESS交易,且id不在已取消的id列表中 BoolQueryBuilder finalQuery = QueryBuilders.boolQuery() .must(QueryBuilders.matchQuery("status", "SUCCESS")) .mustNot(QueryBuilders.termsQuery("id", cancelledIds)); SearchRequest finalRequest = new SearchRequest("你的交易索引名"); finalRequest.source(new SearchSourceBuilder().query(finalQuery)); SearchResponse finalResponse = restHighLevelClient.search(finalRequest, RequestOptions.DEFAULT); // 解析结果得到目标交易列表
方案选择
如果你的交易数据量很大(比如百万级以上),方案一的聚合方式会更高效——因为ES的聚合是分布式并行处理的,比先查所有cancelled id再过滤的方式更优化。如果数据量不大,方案二的单查询写法更简洁。
不管选哪种,都比你当前的客户端过滤方案效率高得多。
内容的提问来源于stack exchange,提问作者Shashank Gandham
相关产品推荐
相关产品推荐

