处理 wait millis 60009, active 50 ,maxactive 200 异常 过程

先说明一下项目特点:业务需要,需要进行相当多的数据库查询和插入、更新操作。生产环境30万数据,本地为了开发方便,只准备了两万数据。

之前的开发过程中测试项目,启动项目后 执行sql操作正常,日志一直再打印hibernate的sql。过一段时间以后 ,就突然停止打印了,等待一段时间(就是wait millis 配置的大小)后,控制台日志打印这句话  。当时第一次遇到这个问题,检查发现在spring的application.xml中配置了数据源druid。里面的配置有这几个参数。当时maxactive   是 50。意思很明显,活动的连接数与最大连接数相同,连接用完了,在等待新的连接,却没有新连接可用,然后超时了。当时我没有意识到这点,只意识到数据库连接不够用,而且网上也有说连接不够用的,就把maxactive  调大,就没有出现这个问题了。

近日,在开发环境部署后,发现数据量太大,这个问题又出现了,maxactive  调的越大,这个问题出现的越晚,但是肯定会出现,说明问题没有根本解决。之后我就详细了解下druid的配置参数都是什么意思,又参考此文:https://www.cnblogs.com/netcorner/p/4380949.html  。终于解决了问题。

说白了,无论数据库连接多大,都会用完,说明用完的数据库连接没有释放掉。为什么没有释放掉?怎么定位?上文中给出了答案。

添加druid的配置:

  1. <!-- 超过时间限制是否回收 -->  
  2. <property name="removeAbandoned" value="true" />  
  3. <!-- 超时时间;单位为秒。180秒=3分钟 -->  
  4. <property name="removeAbandonedTimeout" value="180" />  
  5. <!-- 关闭abanded连接时输出错误日志 -->  
  6. <property name="logAbandoned" value="true" />   

超过180s,强制回收数据库连接,回收的时候打印日志,日志中会显示代码中没有释放数据库连接的代码具体位置。

我把上述配置添加后,运行项目,果然发现出现异常日志:

2018-04-18 16:08:40,692 ERROR [com.alibaba.druid.pool.DruidDataSource] 2021 - [abandon connection, open stackTrace
	at java.lang.Thread.getStackTrace(Thread.java:1589)
	at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1014)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:940)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:930)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:102)
	at org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:141)
	at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:301)
	at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:214)
	at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:157)
	at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:56)
	at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$2.doPrepare(StatementPreparerImpl.java:117)
	at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:183)
	at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:115)
	at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.prepare(IdentityGenerator.java:89)
	at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:55)
	at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2987)
	at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3492)
	at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81)
	at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:395)
	at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:229)
	at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:209)
	at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:193)
	at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:321)
	at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:286)
	at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192)
	at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:206)
	at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:191)
	at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
	at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:683)
	at org.hibernate.internal.SessionImpl.save(SessionImpl.java:675)
	at org.hibernate.internal.SessionImpl.save(SessionImpl.java:671)
	at com.xxxx.iframework.orm.hibernate.HibernateGenericDao.save(HibernateGenericDao.java:119)
	at com.xxxx.zhejiang.hangzhou.xxxx.dao.MaterialChangeDao.addRowGuid(MaterialChangeDao.java:45)

注意最后两行显示的方法名。搜索日志,发现只要出现这个异常,都是这个方法  addRowGuid(),,这就意味着代码有bug,这个方法每一次调用,都不会关闭数据库连接。

排查代码,发现此方法中位于dao中,内容很简单,就是用hibernate的save方法,往数据库插入一个字段值。this.save(xxx);

之前的代码用到这个save方法时,我都是手动提交事务的,只有这一处没有手动提交,漏了,于是更改代码为如下:

Session sess = this.getSession();
		Transaction transaction=null;  
		try {
		    transaction = sess.beginTransaction();
			sess.save(materialChange);
			transaction.commit();
		}catch(Exception e) {
			logger.error(String.format("执行RowGuid=%s 存储数据到change表  失败  )", rowGuid));
			transaction.rollback();
		}finally{
			sess.close();
		}

而且我特意把maxActive参数降低改为200,远远小于之前的值,测试项目,此问题再也没有出现。程序运行一夜没有问题。

整个项目均为自己开发的,这是标准的自己给自己挖个坑,(●´∀`●)


猜你喜欢

转载自blog.csdn.net/xiaoanzi123/article/details/80007378