perf(es): 优化品牌聚合查询和网络连接配置

-增加 connectionRequestTimeout 配置,设置为 1000 毫秒
-调整 maxConnPerRoute 为 50,maxConnTotal为 200
-限制 terms 聚合 size,防止内存压力
- 优化品牌聚合查询逻辑,提高查询效率和准确性
This commit is contained in:
pikachu1995@126.com
2025-08-26 19:33:15 +08:00
parent b57e7813d6
commit 6acc99c7cf
3 changed files with 58 additions and 40 deletions

View File

@@ -15,15 +15,6 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<dependencies> <dependencies>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.4.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId> <artifactId>spring-boot-properties-migrator</artifactId>

View File

@@ -66,14 +66,16 @@ public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
.setRequestConfigCallback(requestConfigBuilder -> .setRequestConfigCallback(requestConfigBuilder ->
requestConfigBuilder requestConfigBuilder
.setConnectTimeout(1000) .setConnectTimeout(1000)
.setSocketTimeout(12 * 1000)); .setSocketTimeout(12 * 1000)
.setConnectionRequestTimeout(1000));
} }
private HttpAsyncClientBuilder configureHttpClientBuilder(HttpAsyncClientBuilder httpClientBuilder, private HttpAsyncClientBuilder configureHttpClientBuilder(HttpAsyncClientBuilder httpClientBuilder,
CredentialsProvider credentialsProvider) { CredentialsProvider credentialsProvider) {
httpClientBuilder httpClientBuilder
.setKeepAliveStrategy(getConnectionKeepAliveStrategy()) .setKeepAliveStrategy(getConnectionKeepAliveStrategy())
.setMaxConnPerRoute(10) .setMaxConnPerRoute(50)
.setMaxConnTotal(200)
.setDefaultIOReactorConfig( .setDefaultIOReactorConfig(
IOReactorConfig IOReactorConfig
.custom() .custom()

View File

@@ -68,6 +68,8 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
private static final String ATTR_BRAND_URL = "brandUrlAgg"; private static final String ATTR_BRAND_URL = "brandUrlAgg";
private static final String ATTR_NAME_KEY = "nameList"; private static final String ATTR_NAME_KEY = "nameList";
private static final String ATTR_VALUE_KEY = "valueList"; private static final String ATTR_VALUE_KEY = "valueList";
// 限制 terms 聚合 size防止内存压力
private static final int MAX_AGG_SIZE = 200;
/** /**
* ES * ES
*/ */
@@ -122,23 +124,24 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
if (!restTemplate.indexOps(EsGoodsIndex.class).exists()) { if (!restTemplate.indexOps(EsGoodsIndex.class).exists()) {
return null; return null;
} }
NativeSearchQueryBuilder builder = createSearchQueryBuilder(goodsSearch, null); NativeSearchQueryBuilder builder = createSearchQueryBuilder(goodsSearch, null);
//分类 //分类
AggregationBuilder categoryNameBuilder = AggregationBuilders.terms("categoryNameAgg").field("categoryNamePath.keyword"); AggregationBuilder categoryNameBuilder = AggregationBuilders.terms("categoryNameAgg").field("categoryNamePath.keyword").size(MAX_AGG_SIZE);
builder.addAggregation(AggregationBuilders.terms("categoryAgg").field("categoryPath").subAggregation(categoryNameBuilder)); builder.addAggregation(AggregationBuilders.terms("categoryAgg").field("categoryPath").size(MAX_AGG_SIZE).subAggregation(categoryNameBuilder));
//品牌 //品牌
AggregationBuilder brandNameBuilder = AggregationBuilders.terms(ATTR_BRAND_NAME).field("brandName.keyword"); AggregationBuilder brandNameBuilder = AggregationBuilders.terms(ATTR_BRAND_NAME).field("brandName.keyword").size(1);
builder.addAggregation(AggregationBuilders.terms("brandIdNameAgg").field(ATTR_BRAND_ID).size(Integer.MAX_VALUE).subAggregation(brandNameBuilder)); builder.addAggregation(AggregationBuilders.terms("brandIdNameAgg").field(ATTR_BRAND_ID).size(MAX_AGG_SIZE).subAggregation(brandNameBuilder));
AggregationBuilder brandUrlBuilder = AggregationBuilders.terms(ATTR_BRAND_URL).field("brandUrl.keyword"); AggregationBuilder brandUrlBuilder = AggregationBuilders.terms(ATTR_BRAND_URL).field("brandUrl.keyword").size(1);
builder.addAggregation(AggregationBuilders.terms("brandIdUrlAgg").field(ATTR_BRAND_ID).size(Integer.MAX_VALUE).subAggregation(brandUrlBuilder)); builder.addAggregation(AggregationBuilders.terms("brandIdUrlAgg").field(ATTR_BRAND_ID).size(MAX_AGG_SIZE).subAggregation(brandUrlBuilder));
//参数 //参数
AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field(ATTR_VALUE); AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field(ATTR_VALUE).size(MAX_AGG_SIZE);
AggregationBuilder sortBuilder = AggregationBuilders.sum("sortAgg").field(ATTR_SORT); AggregationBuilder sortBuilder = AggregationBuilders.sum("sortAgg").field(ATTR_SORT);
AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field(ATTR_NAME).subAggregation(sortBuilder).order(BucketOrder.aggregation("sortAgg", false)).subAggregation(valuesBuilder); AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field(ATTR_NAME).size(MAX_AGG_SIZE)
.subAggregation(sortBuilder).order(BucketOrder.aggregation("sortAgg", false)).subAggregation(valuesBuilder);
builder.addAggregation(AggregationBuilders.nested("attrAgg", ATTR_PATH).subAggregation(paramsNameBuilder)); builder.addAggregation(AggregationBuilders.nested("attrAgg", ATTR_PATH).subAggregation(paramsNameBuilder));
NativeSearchQuery searchQuery = builder.build(); NativeSearchQuery searchQuery = builder.build();
searchQuery.setMaxResults(0); searchQuery.setMaxResults(0);
SearchHits<EsGoodsIndex> search = restTemplate.search(searchQuery, EsGoodsIndex.class); SearchHits<EsGoodsIndex> search = restTemplate.search(searchQuery, EsGoodsIndex.class);
@@ -227,35 +230,57 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
*/ */
private List<SelectorOptions> convertBrandOptions(EsGoodsSearchDTO goodsSearch, List<? extends Terms.Bucket> brandBuckets, List<? extends Terms.Bucket> brandUrlBuckets) { private List<SelectorOptions> convertBrandOptions(EsGoodsSearchDTO goodsSearch, List<? extends Terms.Bucket> brandBuckets, List<? extends Terms.Bucket> brandUrlBuckets) {
List<SelectorOptions> brandOptions = new ArrayList<>(); List<SelectorOptions> brandOptions = new ArrayList<>();
for (int i = 0; i < brandBuckets.size(); i++) { // 以 brandId 为 key 构建 URL 桶索引,避免按下标对齐产生越界/错位
String brandId = brandBuckets.get(i).getKey().toString(); Map<String, Terms.Bucket> brandUrlBucketMap = new HashMap<>();
//当商品品牌id为0时代表商品没有选择品牌所以过滤掉品牌选择器 if (brandUrlBuckets != null) {
//当品牌id为空并且 for (Terms.Bucket urlBucket : brandUrlBuckets) {
if (brandId.equals("0") || if (urlBucket != null && urlBucket.getKey() != null) {
(CharSequenceUtil.isNotEmpty(goodsSearch.getBrandId()) brandUrlBucketMap.put(urlBucket.getKey().toString(), urlBucket);
&& Arrays.asList(goodsSearch.getBrandId().split("@")).contains(brandId))) { }
}
}
Set<String> selectedBrandIds = new HashSet<>();
if (CharSequenceUtil.isNotEmpty(goodsSearch.getBrandId())) {
selectedBrandIds.addAll(Arrays.asList(goodsSearch.getBrandId().split("@")));
}
for (Terms.Bucket bucket : brandBuckets) {
if (bucket == null || bucket.getKey() == null) {
continue;
}
String brandId = bucket.getKey().toString();
// 排除无品牌或已选择品牌
if ("0".equals(brandId) || (!selectedBrandIds.isEmpty() && selectedBrandIds.contains(brandId))) {
continue; continue;
} }
// 品牌名
String brandName = ""; String brandName = "";
if (brandBuckets.get(i).getAggregations() != null && brandBuckets.get(i).getAggregations().get(ATTR_BRAND_NAME) != null) { Aggregations nameAggs = bucket.getAggregations();
ParsedStringTerms aggregation = brandBuckets.get(i).getAggregations().get(ATTR_BRAND_NAME); if (nameAggs != null) {
brandName = this.getAggregationsBrandOptions(aggregation); Aggregation nameAgg = nameAggs.get(ATTR_BRAND_NAME);
if (nameAgg instanceof ParsedStringTerms) {
brandName = this.getAggregationsBrandOptions((ParsedStringTerms) nameAgg);
}
}
if (StringUtils.isEmpty(brandName)) { if (StringUtils.isEmpty(brandName)) {
continue; continue;
} }
}
// 品牌 URL
String brandUrl = ""; String brandUrl = "";
if (brandUrlBuckets != null && !brandUrlBuckets.isEmpty() && Terms.Bucket urlBucket = brandUrlBucketMap.get(brandId);
brandUrlBuckets.get(i).getAggregations() != null && if (urlBucket != null && urlBucket.getAggregations() != null) {
brandUrlBuckets.get(i).getAggregations().get(ATTR_BRAND_URL) != null) { Aggregation urlAgg = urlBucket.getAggregations().get(ATTR_BRAND_URL);
ParsedStringTerms aggregation = brandUrlBuckets.get(i).getAggregations().get(ATTR_BRAND_URL); if (urlAgg instanceof ParsedStringTerms) {
brandUrl = this.getAggregationsBrandOptions(aggregation); brandUrl = this.getAggregationsBrandOptions((ParsedStringTerms) urlAgg);
}
}
if (StringUtils.isEmpty(brandUrl)) { if (StringUtils.isEmpty(brandUrl)) {
continue; continue;
} }
}
SelectorOptions so = new SelectorOptions(); SelectorOptions so = new SelectorOptions();
so.setName(brandName); so.setName(brandName);
so.setValue(brandId); so.setValue(brandId);
@@ -475,7 +500,7 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
//品牌判定 //品牌判定
if (CharSequenceUtil.isNotEmpty(searchDTO.getBrandId())) { if (CharSequenceUtil.isNotEmpty(searchDTO.getBrandId())) {
String[] brands = searchDTO.getBrandId().split("@"); String[] brands = searchDTO.getBrandId().split("@");
filterBuilder.must(QueryBuilders.termsQuery(ATTR_BRAND_ID, brands)); filterBuilder.filter(QueryBuilders.termsQuery(ATTR_BRAND_ID, brands));
} }
if (searchDTO.getRecommend() != null) { if (searchDTO.getRecommend() != null) {
filterBuilder.filter(QueryBuilders.termQuery("recommend", searchDTO.getRecommend())); filterBuilder.filter(QueryBuilders.termQuery("recommend", searchDTO.getRecommend()));