一則死鎖分析小記

據說發生死鎖,我說趕忙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

  1. 通常等待獲取鏈接,都會設置最低超時時間。若是有超時,這個地方就在到達超時,解除互鎖。
    url

  2. 從分析得沒錯,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,而且無限等待。。。

不合理啊。這個配置不合理。。。


這個死鎖問題,把不合理的無限等待改掉,就天然解開了。

相關文章
相關標籤/搜索