导航
导航

7s到65ms!记一次代码优化

几个月之前的事情,记录下。

测试反馈商品展示页偶尔加载缓慢,不加缓存第一次查询耗时达到7s左右,
通过排查发现商品详情的规格参数查询拖了后腿,这一块是调用同事写的接口,遂开始尝试代码级别优化。

原型图:

与前端约定的数据结构如下:

{
"data": [
{
"specifications": "钢 银白",
"stylePropretyGroupList": [
{
"groupName": "机芯",
"stylePropertiesList": [
{
"codeName": "机芯类型",
"valueName": "机械"
}
]
}
]
}
]
}

然后进入实现方法代码,需优化代码如下:


for (Integer productCode : productList) {
DetailSpecificationInfo detailSpecificationInfo = new DetailSpecificationInfo();
Set<Integer> groupCodeSet = new HashSet<>();
ProductEntity productEntity = dao.find(ProductEntity.class, productCode);
detailSpecificationInfo.setSpecifications(productEntity.getSpecifications());
StyleEntity styleEntity = dao.find(StyleEntity.class, productEntity.getStyleCode());
Integer styleCode = styleEntity.getStyleCode();
List<StylePropertyGroupInfo> stylePropretyGroupList = new ArrayList<>();
List<StylePropertiesEntity> list = jinq.stylePros().where(p -> p.getStyleCode() == styleCode).toList();
for (StylePropertiesEntity stylePropertiesEntity : list) {
Integer propertyCode = stylePropertiesEntity.getPropertyCode();
ProductPropertyEntity productPropertyEntity = dao.find(ProductPropertyEntity.class, propertyCode);
Integer groupCode = productPropertyEntity.getGroupCode();
groupCodeSet.add(groupCode);
}
for (Integer groupCode : groupCodeSet) {
StylePropertyGroupInfo StylePropertyGroupInfo = new StylePropertyGroupInfo();
List<StyleProListByCodeInfo> stylePropertiesList = new ArrayList<>();
ProductPropertyGroupEntity productPropertyGroupEntity = dao.find(ProductPropertyGroupEntity.class,
groupCode);
StylePropertyGroupInfo.setGroupName(productPropertyGroupEntity.getGroupName());
for (StylePropertiesEntity stylePropertiesEntity : list) {
Integer propertyCode = stylePropertiesEntity.getPropertyCode();
ProductPropertyEntity productPropertyEntity = dao.find(ProductPropertyEntity.class, propertyCode);
Integer groupCode1 = productPropertyEntity.getGroupCode();
if (groupCode1 == groupCode) {
StyleProListByCodeInfo styleProListByCodeInfo = new StyleProListByCodeInfo();
styleProListByCodeInfo.setCodeName(productPropertyEntity.getPropertyName());
styleProListByCodeInfo.setValueName(stylePropertiesEntity.getPropertyValue());
stylePropertiesList.add(styleProListByCodeInfo);
}
}
StylePropertyGroupInfo.setStylePropertiesList(stylePropertiesList);
stylePropretyGroupList.add(StylePropertyGroupInfo);
}
detailSpecificationInfo.setStylePropretyGroupList(stylePropretyGroupList);
detailSpecificationList.add(detailSpecificationInfo);
}

都没看完,密密麻麻头晕 (╯‵□′)╯︵┴─┴
在同事讲解原需求下开始修改代码。
约定的数据结构导致开发拼接数据难免多层循环,在循环里面调用sql查询。常见的商品属性规格表设计有2种方式,一是目前的使用方法 用关联表、二是保存json数据。目前这个方法不能盲目的改数据库表结构,而且不能破坏与前端对接的约定。

改进方案:在循环外,使用SQL一次性查询出所有必须要用到的数据,循环内不进行远程调用和SQL查询

sql部分如下
查询数据如下

然后代码优化,部分如下:

/**
* 重构获取 获取详情页---规格属性参数信息
*/

List<DetailSpecificationInfo> listInfo = new ArrayList<>();

List<GoodsPropertyExtEntity> list = dao.getPropertyByGoodsCode(goodsCode); //sql语句查询结果

Map<String, DetailSpecificationInfo> map = new HashMap<>();
DetailSpecificationInfo detailSpecificationInfo;
String specification;

for (GoodsPropertyExtEntity goodsPropertyExtEntity : list) {
specification = goodsPropertyExtEntity.getSpecifications();
detailSpecificationInfo = map.get(specification);
if (detailSpecificationInfo == null) {
detailSpecificationInfo = new DetailSpecificationInfo();
detailSpecificationInfo.setSpecifications(specification);
map.put(specification, detailSpecificationInfo);
}

StylePropertyGroupInfo stylePropertyGroupInfo = new StylePropertyGroupInfo();
String groupName = goodsPropertyExtEntity.getGroupName();
stylePropertyGroupInfo.setGroupName(groupName);

List<StyleProListByCodeInfo> stylePropertiesList = new ArrayList<>();

//避免双层for循环获取属性与属性值, 拼接属性名称与属性值名称
StringBuilder tmp = new StringBuilder();
tmp.append(goodsPropertyExtEntity.getCodeName());
tmp.append(",");
tmp.append(goodsPropertyExtEntity.getValueName());
StyleProListByCodeInfo styleProListByCodeInfo = null;
String[] splitTmp = StringUtils.split(tmp.toString(), ",");

for (int i = 0; i <= splitTmp.length / 2 - 1; i++) {
styleProListByCodeInfo = new StyleProListByCodeInfo();
styleProListByCodeInfo.setCodeName(splitTmp[i]);
styleProListByCodeInfo.setValueName(splitTmp[splitTmp.length / 2 + i]);
stylePropertiesList.add(styleProListByCodeInfo);
}
stylePropertyGroupInfo.setStylePropertiesList(stylePropertiesList);

detailSpecificationInfo.getStylePropretyGroupList().add(stylePropertyGroupInfo);
}

listInfo.addAll(map.values());
return new ServiceResultT<>(listInfo);

在第二个注释处这里玩了一个小心思,codeName与valueName数据一一对应,合并起来然后二分赋值,避免了多一次循环。

最后无缓存条件下测试响应时间平均下来65ms 大功告成,哈哈哈