记一次开发实战中的性能优化,同事看完来了句“我去你个大西瓜” |Java 开发实战

940 阅读4分钟

这是我参与更文挑战的第 2 天,活动详情查看:更文挑战

本文正在参加「Java主题月 - Java 开发实战」,详情查看:活动链接

1. 项目背景

最近在做一个文件管理系统,类似于百度网盘的功能,可以实现文件及文件夹的上传下载功能,以及权限管理功能,由于文件及文件夹的层级关系,无法进行数据分页操作,否则查询出的数据中原有的层级关系将破坏,无法将具体的文件放到对应的文件夹下,所以在查询出数据量大的情况下,进行文件权限赋值(设置文件的组信息)时效率超低,几百条的数据量处理下来都需要四五秒的时间,领导看了下系统忍不住骂了句:"我去你个大西瓜"。听完后我一愣:什么,你要请我吃西瓜?毕竟系统做成这样,整的我还挺不好意思的。我不停的给领导道歉:对不起对不起。领导看了看我疑惑的眼神,来了句:你知道在对不起之间加哪两个字比较悲催吗?我皱了皱眉头:对三,要不起? 领导用那鄙视的眼神看了我一下,用手指着门外:

20210306231051635.png

好了,言归正传,针对系统响应时间的问题,性能调优无疑成为了系统用户体验的重要指标,然后针对项目的具体情况,我们对代码逻辑做了一点点优化。

2. 原因分析

针对加载时间响应的问题,刚开始我们从数据库和表结构层面做了处理,但是效果不大,后来针对代码逻辑和业务做了分析,主要原因在于给文件赋组权限信息时,由于是循环文件列表,根据文件id去查询对应的组信息,然后拼接后赋值给文件对象属性,虽然单条文件查询组信息时只有0.02-0.03秒的时间,但是数据量大了之后效率还是挺低的。后来我们就想法将所有文件的组信息及文件id全部查出,然后和列表文件的id比较,如果匹配上,则将组信息放到集合中,最后统一赋值给文件对象中的属性。方案想好后,就开始对代码逻辑进行修改实施,最后的测试结果居然提高了20~30倍。具体代码优化如下。

3.代码优化

优化前:

//设置文件对应的组信息 (content是对应的文件集合)
public void setFileGroupInfo(List<?> content){
	for(Object object : content){
		UserFile userFile = (UserFile) object;
		//根据文件id查找组文件信息
		List<GroupFile> groupList = groupFileMapper.findGroupFileByFileId(userFile.getId());
		Long[] groupIds = new Long[groupList.size()];
		for (int i = 0; i < groupList.size(); i++) {
			groupIds[i] = groupList.get(i).getGroupId();
		}
		//给文件赋权,如果文件状态公开或者包含组信息,设为已授权
		if(SysConstants.IS_PUBLIC_YES ==userFile.getIsPublic() || groupList.size() > 0){
			userFile.setIsAuthorize(1);
		}
		userFile.setGroupIds(groupIds);
	}
}

优化后:

public void setFileGroupInfo(List<?> content){
	//由于循环判断content内容然后根据每一个content的id进行查询组信息效率较低,所以可以将content的id取出放到一个集合中,
	//然后根据fileIdList使用in一次性查询出所有组信息,然后循环判断content的对象id是否等于组列表的fileId,相等就把对应的组id放到对象的groupIdsList中,
	//然后转换为数组赋值给content中的对象,这样效率可以大大提高
	List<Long> fileIdList = new ArrayList<>();//fileIdList:存放content中对象id的集合
	for(Object object : content){
		UserFile userFile = (UserFile) object;
		fileIdList.add(userFile.getId());
	}
        
	List<GroupFile> groupList = groupFileMapper.findGroupFileByFileIdList(fileIdList);//groupList:一次性查询出的所有组列表信息
	for(Object object : content){
		UserFile userFile = (UserFile) object;
		//根据文件id查找组文件信息
		Long[] groupIds = new Long[]{};//每个文件对应的组信息
		List<Long> groupIdsList = new ArrayList<>();//groupIdsList:每个content对象的组id集合,由于循环时数组存放元素不方便,所以此处声明集合,赋值时再转换为对应的数组即可
		int groupListSize = groupList.size();
		for (int i = 0; i < groupListSize; i++) {
			if(userFile.getId().equals(groupList.get(i).getFileId())){//long类型的值不能直接用==比较,要用equals或者userFile.getId().longValue()方法
				groupIdsList.add(groupList.get(i).getGroupId());
			}
		}
		//将groupIdsList转换为数组并赋值
		groupIds = groupIdsList.toArray(new Long[groupIdsList.size()]);
		userFile.setGroupIds(groupIds);
		//给文件赋权,如果文件状态公开或者包含组信息,设为已授权
		if(SysConstants.IS_PUBLIC_YES ==userFile.getIsPublic() || userFile.getGroupIds().length > 0){
			userFile.setIsAuthorize(1);
		}
	}
}

或许有些网友会问,为什么不改变数据结构,将双层for循环改成map的形式,然后通过遍历map的entryset的方式来提高效率,其实这种构想我们测试过,性能变化不明显。不过此处的for循环代码可以通过jdk8的lambda表达式和流特性进行简化,此处就不再一一赘述。

4. 结尾

一周就这样快要过去了,明天还得坚持最后一天上班模式。关于上班,其实就好比旧时代的婚姻,明明不幸福,还得长相厮守。都说生活中百分之二十的痛苦来自于上班,百分之八十的痛苦来自于没钱,所以上班和没钱之间,我们不得不选择前者。

d8650ae7eabbd87dc5b66cd451d92103-0.jpg