作者:mjh3804260 | 来源:互联网 | 2023-10-12 13:29
目录
问题起因
排查思路
步骤一:通过jvisualvm查看线程情况
步骤二:在代码中通过打印dbcp可用连接数
步骤三:顺着源代码看 jdbcTemplate.queryForStream
解决方案
思考
问题起因
使用 JdbcTemplate+dbcp连接池进行数据库访问
dbcp配置最大连接为10
@Bean
public BasicDataSource dataSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.cj.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/xxx"); ds.setUsername("root"); ds.setPassword("xxxx"); ds.setInitialSize(5); ds.setMaxTotal(10); return ds;
}
JdbcTemplate配置为
@Bean
public JdbcTemplate jdbcTemplate(javax.sql.DataSource dataSource) { return new JdbcTemplate(dataSource);
}
使用的代码为
public List selectByName() { String sql = "select * from base_sku where sku_name like '%瓜';"; Stream baseSkuDOStream = jdbcTemplate.queryForStream(sql, new BaseSkuDoRowMapper()); List baseSkuDOs = baseSkuDOStream.collect(Collectors.toList()); return baseSkuDOs;
}
代码运行10次后,代码夯住,不能继续运行
排查思路
步骤一:通过jvisualvm查看线程情况
发现一直卡在获取数据库连接处,表明获取不到数据库连接,怀疑连接耗尽了
步骤二:在代码中通过打印dbcp可用连接数
System.out.println("dbcp连接池可用连接:"+basicDataSource.getNumIdle()
打印结果:
dbcp连接池可用连接:0
的确被耗尽,所以推断是连接没有释放
在网上查询JdbcTemplate使用连接是自动释放的,无需手动释放
但这里的确没有归还
步骤三:顺着源代码看 jdbcTemplate.queryForStream
发现调用execute时,stream方法中传的关闭资源是false
也就是说 jdbcTemplate.queryForStream 这个方法不会自动关闭连接
所以应该需要手动close 资源
解决方案
在代码中加入stream的close,运行果然能释放连接了
public List selectByName() { String sql = "select * from base_sku where sku_name like '%瓜';"; Stream baseSkuDOStream = jdbcTemplate.queryForStream(sql, new BaseSkuDoRowMapper()); List baseSkuDOs = baseSkuDOStream.collect(Collectors.toList()); //加入stream关闭 baseSkuDOStream.close();return baseSkuDOs;
}
思考
所以猜这里的stream这种模式,是不会自动关闭的,看一下其他调用jdbcTemplate.execute的地方,果然,除了queryForStream方法
其他的都是可以自动关闭的,看来是遇到坑了,下次一定切记,jdbcTemplate 使用queryForStream方法一定要手动关闭连接。
遇到数据库连接相关问题以后可以按照此思路排查。