据说发生死锁,我说赶忙jstack搞个javacore分析下。java
用IBM的jca打开,看线程,不少是在等待获取链接。mysql
"ConsumeMessageThread_15" daemon prio=10 tid=0x00007fdbc04a4000 nid=0x552d in Object.wait() [0x00007fdbe1bdd000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007847a8ac0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) at java.lang.Object.wait(Object.java:503) at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118) - locked <0x00000007847a8ac0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106) at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:83) at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:69) at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:279) at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:72) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:47) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:105) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:71) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:152) at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:141)
若是是等待获取链接,为何获取不到呢。spring
查看monitor details。ConsumeMessageThread_9拥有锁0x00000007834414c0,而有其余七个线程等待这个锁。sql
"ConsumeMessageThread_9" daemon prio=10 tid=0x00007fdbdc00d000 nid=0x5523 in Object.wait() [0x00007fdbe25e7000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007847a8af0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) at java.lang.Object.wait(Object.java:503) at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118) - locked <0x00000007847a8af0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106) at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:280) at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:320) at org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes(SQLErrorCodesFactory.java:214) - locked <0x00000007846b7450> (a java.util.WeakHashMap) at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.setDataSource(SQLErrorCodeSQLExceptionTranslator.java:140) at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.<init>(SQLErrorCodeSQLExceptionTranslator.java:103) at org.mybatis.spring.MyBatisExceptionTranslator.initExceptionTranslator(MyBatisExceptionTranslator.java:85) - locked <0x00000007834414c0> (a org.mybatis.spring.MyBatisExceptionTranslator) at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:72) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368) at com.sun.proxy.$Proxy12.insert(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:240)
某个正在等待锁的堆栈。数据库
"ConsumeMessageThread_16" daemon prio=10 tid=0x00007fdc00005800 nid=0x552c waiting for monitor entry [0x00007fdbe1cdf000] java.lang.Thread.State: BLOCKED (on object monitor) at org.mybatis.spring.MyBatisExceptionTranslator.initExceptionTranslator(MyBatisExceptionTranslator.java:84) - waiting to lock <0x00000007834414c0> (a org.mybatis.spring.MyBatisExceptionTranslator) at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:72) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368) at com.sun.proxy.$Proxy12.insert(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:240)
经过上面的堆栈,很明显,是insert失败的时候,myBaits要将数据库的异常作一次转换,转换成统一的错误码。转换的时候,要获取数据库链接,经过数据库的元信息,获取当前链接的是什么类型的数据库。apache
问题来了,获取链接时,业务出错的链接应该没有释放掉。若是当前链接数已经到达上限,此处就须要等待获取新的链接。session
可是,有两个问题。mybatis
通常等待获取链接,都会设置最低超时时间。若是有超时,这个地方就在到达超时,解除互锁。
url
从分析得没错,8个数据库链接就达到链接池的上限了。通常,不至于设置这么少的吧,怎么咱们也是有千万用户的啊。线程
看看链接池的配置:
<bean id="XXX class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="username"> <value>${mysql.w.username}</value> </property> <property name="password"> <value>${mysql.w.password}</value> </property> <property name="url"> <value>${mysql.w.url}</value> </property> <property name="validationQuery"> <value>SELECT 1</value> </property> <property name="testOnBorrow"> <value>true</value> </property> </bean>
没有设置链接次的,最大最小值和等待获取链接的最长时间。。。那就是使用默认值了。。。
org.apache.commons.pool.impl.GenericObjectPool<T> /** * The default cap on the total number of active instances from the pool. * @see #getMaxActive */ public static final int DEFAULT_MAX_ACTIVE = 8; /** * The default maximum amount of time (in milliseconds) the * {@link #borrowObject} method should block before throwing * an exception when the pool is exhausted and the * {@link #getWhenExhaustedAction "when exhausted" action} is * {@link #WHEN_EXHAUSTED_BLOCK}. * @see #getMaxWait * @see #setMaxWait */ public static final long DEFAULT_MAX_WAIT = -1L;
哦。。原来默认值,最大就是8,而且无限等待。。。
不合理啊。这个配置不合理。。。
这个死锁问题,把不合理的无限等待改掉,就天然解开了。