镇海住房和建设交通局网站,跨境电商就是忽悠人的,竞价单页网站制作,公司注册名称大全之前分享过一篇文档,也是关于聚合分组后再分页的具体实现,当时只想着怎么实现,没有去主要探索ES性能优化的问题, 这篇我会换一种方式,重新实现这个聚合分组后再分页的操作,并且指出能优化性能点,可能我们再使用的时候,并没有注意过的点,希望对你有帮助!大佬的话,请忽略! 上源码…之前分享过一篇文档,也是关于聚合分组后再分页的具体实现,当时只想着怎么实现,没有去主要探索ES性能优化的问题, 这篇我会换一种方式,重新实现这个聚合分组后再分页的操作,并且指出能优化性能点,可能我们再使用的时候,并没有注意过的点,希望对你有帮助!大佬的话,请忽略! 上源码
public SearchResultVo searchSupplier(SearchPageVo searchPageVo) {// 创建搜索结果对象SearchResultVo searchResultVo new SearchResultVo();// 获取基础的搜索请求对象SearchRequest searchRequest CloudBaseQueryBuilder.getBaseSubOrderIndexRequest();// 创建搜索源构建器SearchSourceBuilder sourceBuilder new SearchSourceBuilder();// 创建机会页面对象并设置目录IDOpportunityPageVo vo new OpportunityPageVo();vo.setCatalogIds(searchPageVo.getCatalogId());// 根据搜索类型设置供应商名称或单位名称if (PlatformConstant.COMMON_TYPE_YES.equals(searchPageVo.getType())) {vo.setSupplierName(searchPageVo.getName());} else {vo.setUnitName(searchPageVo.getName());}// 获取基础的条件查询BoolQueryBuilder boolQuery CloudBaseQueryBuilder.getBaseOpportunityBoolQuery(vo);// 设置分组聚合int termsSize (searchPageVo.getPageNo() - 1) * searchPageVo.getPageSize() searchPageVo.getPageSize();TermsAggregationBuilder termsAggregationBuilder AggregationBuilders.terms(aggregation).field(PlatformConstant.COMMON_TYPE_YES.equals(searchPageVo.getType()) ? supplierName.keyword : unitName.keyword).size(termsSize).shardSize(termsSize * 2 10).subAggregation(AggregationBuilders.filters(orderTypeFilters,new FiltersAggregator.KeyedFilter(Agreement, QueryBuilders.termQuery(orderType, 3)),new FiltersAggregator.KeyedFilter(Estore, QueryBuilders.termQuery(orderType, 1))).subAggregation(AggregationBuilders.sum(totalPriceSum).field(totalPrice)) // 聚合总金额).subAggregation(AggregationBuilders.cardinality(orderCount).field(id.keyword));// 设置总唯一值聚合CardinalityAggregationBuilder totel AggregationBuilders.cardinality(totel).field(PlatformConstant.COMMON_TYPE_YES.equals(searchPageVo.getType()) ? supplierName.keyword : unitName.keyword)// 精确度越高 内存消耗越大.precisionThreshold(40000);// 设置过滤器聚合FilterAggregationBuilder filter AggregationBuilders.filter(bool_filter, boolQuery);ListObject objectList new ArrayList();objectList.add(orderCount);objectList.add(SortOrder.DESC);// 设置桶排序聚合termsAggregationBuilder.subAggregation(new BucketSortPipelineAggregationBuilder(bucket_sort,Arrays.asList(new FieldSortBuilder((String) objectList.get(0)).order((SortOrder) objectList.get(1)))).from((searchPageVo.getPageNo() - 1) * searchPageVo.getPageSize()).size(searchPageVo.getPageSize()));// 将分组聚合和总唯一值聚合添加到过滤器聚合中filter.subAggregation(termsAggregationBuilder);filter.subAggregation(totel);// 将过滤器聚合添加到搜索源构建器中sourceBuilder.aggregation(filter);// 设置搜索请求的源searchRequest.source(sourceBuilder);// 记录搜索请求日志log.info(searchRequest: sourceBuilder.toString());try {// 执行搜索请求并获取响应SearchResponse searchResponse elasticsearchClient.search(searchRequest, RequestOptions.DEFAULT);// 获取过滤器聚合结果Filter boolFilter searchResponse.getAggregations().get(bool_filter);// 获取总唯一值聚合结果Cardinality cardinality boolFilter.getAggregations().get(totel);// 获取分组聚合结果Terms unitNameAggregation boolFilter.getAggregations().get(aggregation);List? extends Terms.Bucket buckets unitNameAggregation.getBuckets();ListSearchListVo searchList new ArrayList();// 遍历每个分组聚合桶for (Terms.Bucket bucket : buckets) {SearchListVo searchListVo new SearchListVo();// 设置供应商名称或单位名称if (PlatformConstant.COMMON_TYPE_YES.equals(searchPageVo.getType())) {searchListVo.setSupplierName(bucket.getKeyAsString());} else {searchListVo.setUnitName(bucket.getKeyAsString());}// 获取订单类型过滤器聚合结果Filters filters bucket.getAggregations().get(orderTypeFilters);// 设置电商订单金额searchListVo.setEstoreAmount(BigDecimal.valueOf(((Sum) filters.getBucketByKey(Estore).getAggregations().get(totalPriceSum)).getValue()));// 设置协议订单金额searchListVo.setAgreementAmount(BigDecimal.valueOf(((Sum) filters.getBucketByKey(Agreement).getAggregations().get(totalPriceSum)).getValue()));// 获取订单数量聚合结果Cardinality orderCount bucket.getAggregations().get(orderCount);searchListVo.setSum((int) orderCount.getValue());searchList.add(searchListVo);}// 设置搜索结果的总数和列表searchResultVo.setTotal(Long.valueOf(cardinality.getValue()).intValue());searchResultVo.setList(searchList);} catch (IOException e) {// 记录搜索错误日志log.error(Error searching supplier, e);}return searchResultVo;
}代码中的注释,方便我的代码逻辑,其中分页我还是用到了BucketSortPipelineAggregationBuilder这个类 BucketSortPipelineAggregationBuilder 是 Elasticsearch 中用于对聚合桶进行排序和分页的类。下面是对该类的作用、意义、使用场景、优点以及对性能的影响的详细解释
作用和意义 排序允许你对聚合结果中的桶进行排序。例如可以按某个聚合值如订单数量、总金额等对供应商或单位进行排序。 分页通过设置 from 和 size 参数可以实现对聚合结果的分页从而支持分页查询。 为什么使用这个类复杂排序需求当需要对聚合结果进行复杂的排序例如按多个字段排序时BucketSortPipelineAggregationBuilder 提供了灵活的排序选项。 分页支持在处理大量聚合结果时分页是非常重要的可以减少每次查询返回的数据量提高查询效率和用户体验。优点 灵活性支持多种排序方式包括按字段排序、按脚本排序等。 分页能力内置分页功能方便实现分页查询。 性能优化通过减少每次查询返回的数据量可以显著提高查询性能尤其是在处理大量数据时。 内存使用优化通过分页和排序可以减少内存占用避免一次性加载大量数据导致的内存问题。对性能的影响 减少数据传输通过分页每次查询只返回所需的数据量减少了网络传输的数据量。 减少内存占用避免一次性加载大量数据到内存中减少了内存压力。 提高查询速度通过排序和分页可以更快地定位和返回所需的数据提高查询速度。
再构建聚合时,使用了TermsAggregationBuilder类,其中两个参数size和shardSize,这两个参数可以优化性能,如果不设置的话,ES默认size10,shardSize也是10,在 Elasticsearch 的 terms 聚合中size 和 shard_size 参数用于控制聚合结果的数量和分片级别的聚合结果数量 两个参数的区别: size: 作用: 控制最终返回的分组数量。 示例: size: 20 表示最终返回最多 20 个供应商分组。 shard_size: 作用: 控制每个分片返回的分组数量。 示例: shard_size: 10 表示每个分片最多返回 10 个供应商分组。 size 和 shard_size 的关系 shard_size: 每个分片独立地对数据进行聚合并返回最多 shard_size 个分组。 例如如果有 5 个分片每个分片返回 10 个分组那么协调节点将收到 50 个分组。 size: 协调节点从所有分片收集到的分组中选择前 size 个分组。 例如如果 size: 20协调节点将从 50 个分组中选择前 20 个分组。 性能优化: 减少网络传输: 通过设置较小的 shard_size可以减少每个分片返回的数据量从而减少网络传输开销。 提高效率: 协调节点只需处理较少的中间结果从而提高整体查询效率。 准确性: 避免遗漏: 通过设置较大的 shard_size可以确保每个分片返回足够的分组从而避免在协调节点合并时遗漏重要的分组。 精确排序: 确保最终返回的分组是全局排序后的结果而不是每个分片排序后的结果。 示例 假设有一个索引包含 100 个供应商每个分片包含 20 个文档。配置如下 size: 20: 最终返回 20 个供应商分组。 shard_size: 10: 每个分片返回 10 个供应商分组。 分片聚合 分片 1: 返回供应商 A, B, C, D, E, F, G, H, I, J。 分片 2: 返回供应商 K, L, M, N, O, P, Q, R, S, T。 分片 3: 返回供应商 U, V, W, X, Y, Z, A1, B1, C1, D1。 分片 4: 返回供应商 E1, F1, G1, H1, I1, J1, K1, L1, M1, N1。 分片 5: 返回供应商 O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1。 协调节点聚合 合并分组: 协调节点将所有分组合并得到 50 个供应商分组。 排序: 协调节点按文档数量降序排序如果数量相同则按供应商名称升序排序。 选择前 20 个分组: 最终返回前 20 个供应商分组。 冲突问题 冲突: 由于 shard_size 控制每个分片返回的分组数量如果 shard_size 设置得太小可能会导致某些重要的分组被遗漏从而影响最终结果的准确性。 避免冲突: 通过合理设置 shard_size确保每个分片返回足够的分组从而避免在协调节点合并时遗漏重要的分组。 总结 shard_size 和 size 的关系是shard_size 控制每个分片返回的分组数量size 控制最终返回的分组数量。 性能优化: 通过设置较小的 shard_size可以减少网络传输开销提高查询效率。 准确性: 通过设置较大的 shard_size可以确保每个分片返回足够的分组从而避免遗漏重要的分组确保最终结果的准确性。所以shard_size的大小要我们的数据量,业务需求去调整,没有绝对的正确,只有相对的平衡 还有个计算分组的总条数,我使用的是计算唯一的类:CardinalityAggregationBuilder,其中precisionThreshold就是调整的阙值,这个参数决定了Elasticsearch在计算基数时使用的内存大小和精度之间的权衡。较高的阈值会提高精度但增加内存消耗,
那么在ES中还有哪些聚合方式:
在Elasticsearch中除了 TermsAggregationBuilder还有其他几种常用的聚合类。以下是几种常见的聚合类及其特点、优缺点和对性能的影响 Terms Aggregation 作用用于按字段值进行分组统计。 优点 灵活性高支持多种子聚合。 支持排序、过滤等操作。 缺点 对于大基数大量唯一值字段性能较差内存消耗较大。 需要设置合理的 shardSize 和 precisionThreshold 来平衡精度和性能。 性能影响随着唯一值数量增加性能会显著下降。 Histogram Aggregation 作用用于数值字段的区间分组统计。 优点 适合数值型数据的区间分析。 计算简单性能较好。 缺点 不适用于非数值字段。 区间划分需要预先设定。 性能影响性能较好但依赖于区间的合理设置。 Date Histogram Aggregation 作用用于日期字段的时间区间分组统计。 优点 专门针对日期字段优化。 支持灵活的时间间隔如天、月、年。 缺点 仅适用于日期字段。 性能影响性能较好特别是对于时间序列数据。 Range Aggregation 作用用于数值或日期字段的范围分组统计。 优点 支持自定义范围。 适合分析特定范围内的数据分布。 缺点 需要手动定义范围边界。 性能影响性能较好取决于范围的数量和复杂度。 Composite Aggregation 作用用于多字段组合分组统计支持深度分页。 优点 支持多字段组合分组。 支持深度分页避免一次性加载大量数据。 缺点 复杂度较高配置较为繁琐。 性能影响性能较好特别适合大数据集的分页查询。 Significant Terms Aggregation 作用用于发现显著不同的项常用于异常检测。 优点 适合发现异常或显著变化的数据。 自动计算显著性。 缺点 计算复杂性能较低。 性能影响性能较差适合小规模数据集或特定场景。 Cardinality Aggregation 作用用于计算唯一值的数量。 优点 简单易用适合唯一值统计。 缺点 对于大基数字段性能较差内存消耗大。 性能影响性能取决于 precisionThreshold 的设置。 聚合类的选择建议 如果需要按离散值分组使用 Terms Aggregation 或 Composite Aggregation。Composite Aggregation 更适合大数据集的分页查询。 如果需要按数值或日期区间分组使用 Histogram Aggregation 或 Date Histogram Aggregation。 如果需要按范围分组使用 Range Aggregation。 如果需要发现显著变化的数据使用 Significant Terms Aggregation。 如果需要计算唯一值数量使用 Cardinality Aggregation并根据实际需求调整 precisionThreshold。