for循环查询数据库优化(挺好用的)

前段时间在完成一个列表功能的时候发现我for循环里嵌套了2个查询语句,然后就感觉太影响性能了,而且也体现一个人的水平, 这里我举下例子(方便回忆)。

实体

//项目用户实体首先这是和用户实体一对多的关系,既一个项目下有多个用户这样我们查询这个数据库时候就会出现list列表传个projectId进来
public class ProjectUser {

    private  String userId;

    private String projectId;
}
//用户实体
public class User {

    private  String id;

    private String imageId;

	private String userName;

}
//头像实体类
public class Image {

    private  String id;

    private String imageUrl;
}
//出参
public class UserInfoVO{

	private String projectId;
	//这里返回给前端一个列表数组
	private List<UserInfo> userInfoList;

}
public class UserInfo{
	private Sring userName;
	
	private Sring userId;
	
	private Sring imageUrl
}
    

实际碰到的问题(优化前)

//需求:我们需要返回一个列表给前端,这个列表包含的数据有用户id,用户头像,用户姓名,前端传个项目id进来
//菜鸟的我是这样写的
//1.我们需要通过项目id去获取这个项目下有多少个用户
//2.然后循环这个列表,获取这个集合下的userId
//3.拿到userId需要去查user表拿到用户名,还有imageid
//4.这时再拿imageId去查image表拿imageUrl获取头像
UserInfoVO userInfoVO=new UserInfoVO();
List<UserInfo> userInfoList=new ArraryList<>();
List<ProjectUser > projectList=projectUserDao.selectByProjectId(projectId);
//这里列表可能为空的如果一些坑的项目没有会报错所以可以做非空判断
if(CollectionUtils.isNotEmpty(projectList)){
for(ProjectUser projectuser:projectList){
	UserInfo userinfo =new UserInfo();
	User user=userDao.selectByUserId(projectuser.getUserId());
	Image image=imageDao.selectByImageId(user.getImageId());
	userinfo.setUserName(user.getUserName());
	userinfo.setUserId(user.getId());
	userinfo.setImageUrl(image.getImageUrl());
	userInfoList.add(userinfo);
}
userInfoVO.setProjectId(projectId);
userInfoVO.setUserInfoList(userInfoList);
//从这里可以看出我这个for循环里嵌套了查询语句而且还是两条,这样如果数据量过大的话,每次循环都会去连接数据库,这样是容易影响性能和导致数据库崩溃的。
}

实际碰到的问题(优化后)

//这里我的优化思路是利用stream流结合sql语句进行优化
//1.首先还是查询出list集合
List<ProjectUser > projectList=projectUserDao.selectByProjectId(projectId);
//2.然后用stream流对这个集合的关键字段进行转换筛选处理,这里收集所有的userId,待会让sqlfor循环。
List<String> userIdList=projectList.stream().map(ProjectUser::getUserId).collect(Collectors.toList());
//3.写一条sql语句让sql做循环(效果比你for循环里查询数据库好)
List<UserInfoDTO> userInfoDTOList=projectUserDao.selectByUserIds(userIdList);
//4.拿到list集合利用stream流把他转换成map类型
Map<String,UserInfoDTO> userInfoMap=userInfoDTOList.stream().collect(Collectors.toMap(UserInfoDTO::getUserId,Function.identity()))
//5.接下来就是填充数据了,
for(String userId:userIdList){
	UserInfo userinfo =new UserInfo();
	UserInfoDTO userInfoDTO=userInfoMap.get(userId);
	userinfo.setUserName(userInfoDTO.getUserName());
	userinfo.setUserId(userInfoDTO.getId());
	userinfo.setImageUrl(userInfoDTO.getImageUrl());
	userInfoList.add(userinfo);
}
userInfoVO.setProjectId(projectId);
userInfoVO.setUserInfoList(userInfoList);
//从这里可以看出我优化成功,for循环里没有查询sql,所以stream流结合sql语句YYDS,只能说stream流真的太强了。
}

SQL语句

//这个类可以用来接受我们需要接受的数据
public class UserInfoDTO{
	private Sring userName;
	
	private Sring userId;
	
	private Sring imageUrl

}

	//这里是dao层
	List<UserInfoDTO> selectByUserIds(@Param("userIdList")List<String> userIdList);

//sql
	<select id="selectByUserIds" resultType="com.chenguangzhao.entities.UserInfoDTO" parameterType="java.util.List">
		select
		a.id AS userId,
		a.user_name AS userName,
		b.image_url AS imageUrl
		from t_user a
		LEFT JOIN t_image b on a.image_id=b.id
		<where>
			<if test="userIdList.size()>0">
			//这里需要标明那个表的id不然会分不清楚
				a.id in
				<foreach collection="userIdList" item="id" index="index" open="(" close=")" separator=",">
					#{id}
				</foreach>
			</if>
		</where>
	</select>

性能提高总结

如果性能还是没有得到解决的话,建议结合定时器以及检查sql是否失效,或者走索引。
1.sql优化
sql优化
2.数据库做主从复制,主库写数据,从库读数据(这一步我感觉大部分项目都是做了的)

3.定时器+reids(当然要实时查询的话还是得websocket(实时传输数据给前端)结合消息队列(观察者模式、redis发布订阅模式都可以替代消息队列)
大数据下,如果数据量比较大的话,可以尝试结合redis缓存和定时器来进行提高性能
定时器加redis优化

4.如果业务比较多的话,需要查很多表的话,建议一次性把所有的数据查出来,然后对数组进行处理以及组装过滤等操作(可以采用stream流,这个很好用这样可以减少很多for循环)

5.如果性能还得不到提高建议结合多线程对数据进行处理(能尽量用sql把所有数据查出来就查出来)
多线程提高性能

6.上面的几种情况下还不能解决问题的话考虑使用设计模式比如单例模式,享元模式(目前我也没学),策略模式+工厂模式
设计模式优化

7.通过消息队列提前把数据推到队列中,然后消费者消费队列中的数据

8.关于LEFT JOIN 语句的优化,当你数据量比较大的时候关联表比较多可能会影响到性能
LEFT JOIN 优化

9.mysql联合查询

联合查询-减少查询

对于使用 for 循环调用接口进行优化,有几个常见的方法可以考虑: 1. 批量处理:如果接口支持批量操作,可以将多个请求合并为一个批量请求,减少网络传输和接口调用的开销。这样可以减少循环次数,提高效率。 2. 并发请求:使用多线程、多进程或异步操作来并发发送多个请求,以减少等待时间。可以使用线程池或协程来管理并发操作,但要注意控制并发量,避免对接口服务器造成过大负荷。 3. 数据缓存:如果接口返回的数据可以缓存,可以考虑使用缓存来避免重复调用接口。可以使用内存缓存、数据库或其他缓存技术,根据数据的更新频率和实时性需求选择合适的缓存策略。 4. 分页查询:如果接口支持分页查询,可以通过设定合适的每页数据量和页码参数来获取特定范围的数据,避免一次性获取全部数据。这样可以减少接口调用次数和数据传输量。 5. 请求参数优化:根据接口的特性和需求,优化请求参数的设置,避免不必要的参数传递和数据处理。可以根据实际情况调整参数格式、过滤条件、排序方式等,减少数据传输和处理的开销。 需要根据具体情况选择合适的优化方法,并综合考虑接口性能、数据实时性、系统资源等因素进行权衡。同时,也要注意遵守接口的使用规范和限制,避免对接口服务端造成过大压力或违反服务协议。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码阿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值