據說發生死鎖,我說趕忙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,而且無限等待。。。
不合理啊。這個配置不合理。。。
這個死鎖問題,把不合理的無限等待改掉,就天然解開了。