【问题标题】:Reading thread dump to debug an exhausted database connection pool读取线程转储以调试耗尽的数据库连接池
【发布时间】:2014-06-20 13:21:51
【问题描述】:

我有一个由 jetty + mysql 提供服务的网络应用程序。我遇到了一个问题,我的数据库连接池耗尽,所有线程都开始阻塞等待连接。我尝试了两个数据库连接池库:(1)bonecp(2)hikari。两者都与我的应用程序表现出相同的行为。

当我看到这种状态时,我已经进行了几次线程转储,并且所有被阻塞的线程都处于这种状态(不是选择bonecp,我确定它现在已经结束了):

"qtp1218743501-131" prio=10 tid=0x00007fb858295800 nid=0x669b waiting on condition [0x00007fb8cd5d3000]
  java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x0000000763f42d20> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
    at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
    at com.jolbox.bonecp.DefaultConnectionStrategy.getConnectionInternal(DefaultConnectionStrategy.java:82)
    at com.jolbox.bonecp.AbstractConnectionStrategy.getConnection(AbstractConnectionStrategy.java:90)
    at com.jolbox.bonecp.BoneCP.getConnection(BoneCP.java:553)
    at com.me.Foo.start(Foo.java:30)
    ...

我不知道从这里去哪里。我在想我会在线程转储中看到一些堆栈跟踪,我的代码被卡在执行一些冗长的操作,而不是等待连接。例如,如果我的代码如下所示:

public class Foo {
    public void start() {
        Connection conn = threadPool.getConnection();
        work(conn);
        conn.close();
    }

    public void work(Connection conn) {
        .. something lengthy like scan every row in the database etc ..
    }
}

我希望上面的线程之一有一个堆栈跟踪,显示它在 work() 方法中工作:

...
at com.me.mycode.Foo.work()
at com.me.mycode.Foo.start()

但他们只是在等待连接:

...
at com.jolbox.bonecp.BoneCP.getConnection() // ?
at com.me.mycode.Foo.work()
at com.me.mycode.Foo.start()

任何关于如何继续调试的想法都会很棒。

其他一些背景:应用程序正常运行大约 45 分钟,内存和线程转储显示没有异常。然后触发条件并且线程数激增。我开始认为这可能是应用程序尝试执行的 sql 语句的某种组合,这些语句在 mysql 端变成了某种锁,但我再次希望上面堆栈跟踪中的一些线程向我展示它们是在代码的那部分。

线程转储是使用 visualvm 进行的。

谢谢

【问题讨论】:

  • 您是否曾尝试使用“资源尝试”来尝试您的连接逻辑?

标签: java mysql connection-pooling


【解决方案1】:

利用连接池的配置选项(请参阅BoneCPConfig / HikariCPConfig)。首先,设置连接超时(HikariCP connectionTimeout)和泄漏检测超时(HikariCP leakDetectionThreshold,我在 BoneCP 中找不到对应的)。当某些事情不太正确时,可能会有更多配置选项转储堆栈跟踪。

我的猜测是,您的应用程序并不总是返回到池的连接,并且 45 分钟后池中不再有连接(因此永远阻止尝试从池中获取连接)。将连接视为打开/关闭文件,即始终使用 try/finally:

public void start() {

    Connection conn = null;
    try {
        work(conn = dbPool.getConnection());
    } finally {
        if (conn != null) {
            conn.close();
        }
    }
}

最后,两个连接池都有允许JMX monitoring 的选项。您可以使用它来监控池中的异常行为。

【讨论】:

  • 是的,我为 bonecp 启用了日志记录,它指出了我的连接泄漏:D
【解决方案2】:

我质疑整个设计。

如果您在多线程 netIO 中有等待块,则需要更好地实现连接。

我建议你看看非阻塞 IO(Java.nio,channels 包),或者细化你的锁。

【讨论】:

    猜你喜欢
    • 2017-07-10
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2022-07-24
    • 2019-01-20
    • 1970-01-01
    • 2017-12-12
    • 1970-01-01
    相关资源
    最近更新 更多