`

数据库链接长时间无数据交互,发生线程阻塞情况

 
阅读更多

背景:

 在执行双机房部署的时候,因为应用长时间未访问数据库,导致后面访问的数据库的线程都被挂起。

 

现象分析:

"Thread-74" daemon prio=10 tid=0x00007f1840044000 nid=0x387b runnable [0x00007f18bdb27000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(SocketInputStream.java:129)
        at oracle.net.ns.Packet.receive(Unknown Source)
        at oracle.net.ns.DataPacket.receive(Unknown Source)
        at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
        at oracle.net.ns.NetInputStream.read(Unknown Source)
        at oracle.net.ns.NetInputStream.read(Unknown Source)
        at oracle.net.ns.NetInputStream.read(Unknown Source)
        at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1109)
        at oracle.jdbc.driver.T4CMAREngine.unmarshalSB1(T4CMAREngine.java:1080)
        at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:485)
        at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210)
        at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:804)
        at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1051)
        at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:845)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1156)
        at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1315)
        - locked <0x00000000be528248> (a oracle.jdbc.driver.T4CStatement)
        - locked <0x00000000c4d1b000> (a oracle.jdbc.driver.T4CConnection)
        at oracle.jdbc.driver.PhysicalConnection.doPingDatabase(PhysicalConnection.java:4614)
        at oracle.jdbc.driver.PhysicalConnection$1.run(PhysicalConnection.java:4590)
        at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:

DubboServerHandler-172.21.55.25:20883-thread-13" daemon prio=10 tid=0x00007f1828012800 nid=0x3867 in Object.wait() [0x00007f18be305000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000be51e0b0> (a java.lang.Thread)
        at java.lang.Thread.join(Thread.java:1194)
        - locked <0x00000000be51e0b0> (a java.lang.Thread)
        at oracle.jdbc.driver.PhysicalConnection.pingDatabase(PhysicalConnection.java:4596)
        at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.alibaba.druid.pool.vendor.OracleValidConnectionChecker.isValidConnection(OracleValidConnectionChecker.java:94)
        at com.alibaba.druid.pool.DruidAbstractDataSource.testConnectionInternal(DruidAbstractDataSource.java:1252)
        at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:954)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:921)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:911)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:98)
        at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:113)
        at org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy$TransactionAwareInvocationHandler.invoke(TransactionAwareDataSourceProxy.java:210)
        at $Proxy25.toString(Unknown Source)
        at java.lang.String.valueOf(String.java:2826)
        at java.lang.StringBuffer.append(StringBuffer.java:219)
        - locked <0x00000000be4cd210> (a java.lang.StringBuffer)
        at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:194)
        at org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithListResult(SqlMapClientTemplate.java:249)
        at org.springframework.orm.ibatis.SqlMapClientTemplate.queryForList(SqlMapClientTemplate.java:296)
        at com.lianpay.bank.mng.dao.impl.BaseIbatisDAOImpl.queryAll(BaseIbatisDAOImpl.java:123)
        at com.lianpay.bank.mng.service.impl.QueryBankListServiceImpl.queryPayTypeList(QueryBankListServiceImpl.java:319)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

    我们可以发现线程thread-13一直被阻塞在pingDatabase方法调用中,而线程Thread-74一直卡在socketRead0,初步分析是没有设置socket timeout时间。

解决:

参考文档:

http://agapple.iteye.com/blog/772507 

http://www.importnew.com/2466.html

http://agapple.iteye.com/blog/1024508

查看上面两个参考文档之后,发现需要配置validationQueryTimeout,oracle.net.CONNECT_TIMEOUT=3000&oracle.net.READ_TIMEOUT=60000这几个参数。

 

源码分析:

问题1:我们设置了maxWait参数,为什么获取链接的时候未超时,报异常

问题2:我们设置了validationQuery,为什么没有定期发送ping包过去保活

 

   一: maxWait参数主要作用在getConnectionInternal,而我们现在是卡在testConnectionInternal中,表示是已经获取到connection,但是卡在验证connection的有效性中。

  public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;
        for (;;) {
            // handle notFullTimeoutRetry
            DruidPooledConnection poolableConnection;
            try {
                poolableConnection = getConnectionInternal(maxWaitMillis);
            } catch (GetConnectionTimeoutException ex) {
                if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                    notFullTimeoutRetryCnt++;
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("not full timeout retry : " + notFullTimeoutRetryCnt);
                    }
                    continue;
                }
                throw ex;
            }

            if (isTestOnBorrow()) {
                boolean validate = testConnectionInternal(poolableConnection.getConnection());
                if (!validate) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("skip not validate connection.");
                    }

                    Connection realConnection = poolableConnection.getConnection();
                    discardConnection(realConnection);
                    continue;
                }
 }

   

    2. validationQuery不是用来保证socket长链接不断开的。validationQuery的调用有两处,一是创建链接的时候,二是获取到链接的时候,会验证有效性。

 protected boolean testConnectionInternal(Connection conn) {
        String sqlFile = JdbcSqlStat.getContextSqlFile();
        String sqlName = JdbcSqlStat.getContextSqlName();

        if (sqlFile != null) {
            JdbcSqlStat.setContextSqlFile(null);
        }
        if (sqlName != null) {
            JdbcSqlStat.setContextSqlName(null);
        }
        try {
            if (validConnectionChecker != null) {
                return validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
            }

            if (conn.isClosed()) {
                return false;
            }

            if (null == validationQuery) {
                return true;
            }

            Statement stmt = null;
            ResultSet rset = null;
            try {
                stmt = conn.createStatement();
                if (getValidationQueryTimeout() > 0) {
                    stmt.setQueryTimeout(validationQueryTimeout);
                }
                rset = stmt.executeQuery(validationQuery);
                if (!rset.next()) {
                    return false;
                }
            } finally {
                JdbcUtils.close(rset);
                JdbcUtils.close(stmt);
            }

            return true;
        } catch (Exception ex) {
            // skip
            return false;
        } finally {
            if (sqlFile != null) {
                JdbcSqlStat.setContextSqlFile(sqlFile);
            }
            if (sqlName != null) {
                JdbcSqlStat.setContextSqlName(sqlName);
            }
        }
    }

    如果socket链接断开不可用,也不会导致应用异常,druid框架会不断的重试,抛弃不可用链接,直到拿到可用的链接

 public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;
        for (;;) {
            // handle notFullTimeoutRetry
            DruidPooledConnection poolableConnection;
            try {
                poolableConnection = getConnectionInternal(maxWaitMillis);
            } catch (GetConnectionTimeoutException ex) {
                if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                    notFullTimeoutRetryCnt++;
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("not full timeout retry : " + notFullTimeoutRetryCnt);
                    }
                    continue;
                }
                throw ex;
            }

            if (isTestOnBorrow()) {
                boolean validate = testConnectionInternal(poolableConnection.getConnection());
                if (!validate) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("skip not validate connection.");
                    }

                    Connection realConnection = poolableConnection.getConnection();
                    discardConnection(realConnection);
                    continue;
                }
            } else {
                Connection realConnection = poolableConnection.getConnection();
                if (realConnection.isClosed()) {
                    discardConnection(null); // 传入null,避免重复关闭
                    continue;
                }

                if (isTestWhileIdle()) {
                    final long currentTimeMillis = System.currentTimeMillis();
                    final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis();
                    final long idleMillis = currentTimeMillis - lastActiveTimeMillis;
                    long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis();
                    if (timeBetweenEvictionRunsMillis <= 0) {
                        timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
                    }

                    if (idleMillis >= timeBetweenEvictionRunsMillis) {
                        boolean validate = testConnectionInternal(poolableConnection.getConnection());
                        if (!validate) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("skip not validate connection.");
                            }

                            discardConnection(realConnection);
                            continue;
                        }
                    }
                }
            }

            if (isRemoveAbandoned()) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.setConnectStackTrace(stackTrace);
                poolableConnection.setConnectedTimeNano();
                poolableConnection.setTraceEnable(true);

                synchronized (activeConnections) {
                    activeConnections.put(poolableConnection, PRESENT);
                }
            }

            if (!this.isDefaultAutoCommit()) {
                poolableConnection.setAutoCommit(false);
            }

            return poolableConnection;
        }
    }

 

 

 

 

 

分享到:
评论

相关推荐

    010_android 之UI线程阻塞及其优化

    3. IntentService:适合处理那些不需要用户交互且需要长时间运行的任务。 4. 使用线程池:通过ExecutorService管理线程池,避免频繁创建销毁线程的开销。 5. 使用LiveData和ViewModel(Android Jetpack组件):这些...

    Qt数据库利用线程读取MySql数据

    然而,直接在主线程中执行长时间运行的数据库查询会阻塞UI,导致界面无响应。为了解决这个问题,我们可以使用QThread来异步处理查询。首先,创建一个继承自`QThread`的类,比如`DatabaseWorker`,并在其中定义执行...

    多线程GUI界面交互示例

    它允许开发者在另一个线程上执行长时间运行的操作,同时保持UI线程的活动,确保界面的及时更新。 1. 创建和配置`backgroundWorker`:首先,你需要在你的窗体或控件中添加一个`BackgroundWorker`对象,并设置其属性...

    线程阻塞优化

    5. **IntentService**:对于需要长时间运行的后台任务,Android提供了IntentService。它在单独的工作线程中运行,完成任务后自动停止,避免了主线程的阻塞。 6. **线程池**:Java中的ExecutorService和...

    android sqlite多线程和异步加载数据库数据示例

    在Android中,主线程负责UI的更新和交互,而长时间运行的操作,如数据库查询,如果在主线程执行,可能会导致应用无响应(ANR)。因此,我们需要将这些操作放在工作线程或AsyncTask中执行。你可以使用Handler、Thread...

    android连接电脑数据库

    - 长时间的数据库操作应避免在主线程执行,以免阻塞UI。可以使用`AsyncTask`进行异步处理,或者使用`Handler`和`Looper`在后台线程中处理任务。 8. **安全考虑**: - 连接电脑数据库时必须确保数据传输的安全性,...

    使用线程操作SQLite数据库

    默认情况下,如果在主线程(UI线程)中执行数据库操作,可能会导致应用程序无响应(ANR),因为这些操作可能耗时较长。为了解决这个问题,我们需要将这些操作放到工作线程或异步任务中,这样即使数据库操作需要时间...

    数据库访问异步检测

    这使得应用程序能够保持其响应性,而不被长时间的数据库操作阻塞。 6. **异步编程模型**:常见的异步编程模型有回调函数、事件驱动、Promise(或Future)以及基于async/await的异步编程。这些模型帮助开发者编写非...

    Java Swing数据库管理系统

    - 异步处理:长时间运行的任务(如批量更新)可以放在后台线程执行,防止阻塞用户界面。 - 数据验证:在用户输入数据时进行实时验证,以确保数据的完整性和正确性。 总的来说,"Java Swing数据库管理系统"是一个将...

    Android中UI线程与后台线程交互的探讨.pdf

    在Android应用开发中,UI线程(也称为主线程)负责处理用户界面的交互,而后台线程则用于执行耗时操作,如网络请求、数据库操作等,以避免阻塞UI,保证用户界面的流畅性。当后台线程完成耗时操作后,通常需要将结果...

    数据库连接池原理

    - **设置超时时间**:为了避免长时间的阻塞影响业务,获取连接的操作通常会设置超时时间。 - **从资源池获取连接**:如果资源池中没有可用的连接,并且当前连接数未达到最大值,连接池会新建连接。新连接的创建方式...

    C# 数据库备份工具

    大型数据库的备份可能需要较长时间,为了保持UI的响应性,可以使用多线程。`System.Threading`命名空间中的`Thread`或`Task`类可以帮助我们在后台执行备份任务,避免阻塞用户界面。 7. **进度条显示**: 为了提高...

    WinForm C#多线程等待窗体

    2. 在需要启动长时间操作的地方,创建一个新的线程。 3. 在新线程中,实例化`WaitForm`并调用`ShowDialog`,这将阻塞当前线程(新线程)。 4. 在新线程中执行耗时任务。 5. 任务完成后,关闭`WaitForm`,这将自动...

    NET 中实现异步回调访问数据库

    在传统的同步访问中,程序会等待数据库操作完成才能继续执行后续任务,这可能导致应用程序响应速度变慢,特别是在处理大量数据或长时间运行的查询时。而异步访问允许程序在等待数据库操作的同时执行其他任务,提高了...

    sqlite3.30.1 数据库模块+支持库(彻底解决多线程死锁问题)-易语言

    3. **死锁检测与恢复**:当检测到死锁时,SQLite会自动回滚其中一个事务,以解除死锁状态,避免应用程序长时间阻塞。 4. **API接口**:易语言的SQLite3.30.1模块提供了与SQLite交互的接口,允许开发者通过这些接口...

    Silverlight连接数据库简单示例

    由于Silverlight运行在浏览器的安全沙箱环境中,不能直接在UI线程上执行长时间运行的任务,因此数据库操作通常需要异步处理。可以使用BackgroundWorker或Task类来实现异步操作,确保不阻塞UI。 7. **异常处理**: ...

    delphi线程检测sql连接不卡界面

    为了解决这个问题,开发者可以利用多线程技术,将数据库连接检测的任务放到后台线程中执行,这样主线程(通常负责处理用户界面)可以继续运行,不会被长时间的等待操作阻塞。 在Delphi中,可以创建一个TThread子类...

    QT中sqlite多线程操作4个注意问题

    这可以提高应用程序的响应性,并降低因长时间数据库操作导致的用户界面冻结风险。 5. **错误处理**: 在多线程环境中,错误处理更为复杂,因为错误可能在任何线程中发生。确保捕获并正确处理所有异常,记录错误...

    数据库作业_图书馆管理系统

    4. 异步操作:为了防止长时间的数据库操作阻塞用户界面,可以使用异步操作或者多线程技术,确保系统的响应速度。 5. 错误处理:系统应该具有完善的错误处理机制,能够捕获并适当地处理可能出现的异常情况,如数据库...

    用ADO.NET2.0进行数据库应用程序开发(2)

    9. **异步操作**:ADO.NET 2.0引入了异步API,允许在执行长时间运行的数据库操作时避免阻塞UI线程,提升用户体验。 10. **XML集成**:ADO.NET 2.0加强了与XML的集成,允许直接将数据集转换为XML,反之亦然,便于...

Global site tag (gtag.js) - Google Analytics