tddl连接池获取和释放流程疑问

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 最近遇到线上机器连接池满的情况,排查发现tddl里面在连接池获取和释放流程中有些问题

问题现象:

线上报HSF线程池满,CPU100%。


排查过程:

1 线程dump下来,分析等待线程

Hsf等待线程1:大约400个线程等待获取连接池

at sun.misc.Unsafe.park(Native Method)
        -  waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@22630f9b
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:233)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at com.alibaba.druid.pool.DruidDataSource.pollLast(DruidDataSource.java:1609)
        at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1172)
        at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1045)
        at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4544)
        at com.alibaba.intl.commons.monitor.druid.ConnectionMonitorDruidFilter.dataSource_getConnection(ConnectionMonitorDruidFilter.java:60)
        at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4540)
        at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:662)
        at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4540)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1023)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1015)
        at com.taobao.tddl.atom.jdbc.TDataSourceWrapper.getConnectionByTargetDataSource(TDataSourceWrapper.java:349)
        at com.taobao.tddl.atom.jdbc.TDataSourceWrapper.getConnectionWrapper(TDataSourceWrapper.java:319)
        at com.taobao.tddl.atom.jdbc.TDataSourceWrapper.getConnection0(TDataSourceWrapper.java:290)
        at com.taobao.tddl.atom.jdbc.TDataSourceWrapper.getConnection(TDataSourceWrapper.java:264)
        at com.taobao.tddl.atom.jdbc.TDataSourceWrapper.getConnection(TDataSourceWrapper.java:231)
        at com.taobao.tddl.atom.AbstractTAtomDataSource.getConnection(AbstractTAtomDataSource.java:32)
        at com.taobao.tddl.group.jdbc.DataSourceWrapper.getConnection(DataSourceWrapper.java:133)
        at com.taobao.tddl.group.jdbc.TGroupConnection.createNewConnection(TGroupConnection.java:210)
        at com.taobao.tddl.group.jdbc.TGroupStatement$4.tryOnDataSource(TGroupStatement.java:640)
        at com.taobao.tddl.group.jdbc.TGroupStatement$4.tryOnDataSource(TGroupStatement.java:632)
        at com.taobao.tddl.group.dbselector.AbstractDBSelector.tryOnDataSourceHolderWithIndex(AbstractDBSelector.java:279)
        at com.taobao.tddl.group.dbselector.AbstractDBSelector.tryExecute(AbstractDBSelector.java:434)
        at com.taobao.tddl.group.dbselector.AbstractDBSelector.tryExecute(AbstractDBSelector.java:449)
        at com.taobao.tddl.group.jdbc.TGroupStatement.executeQuery(TGroupStatement.java:505)
        at com.taobao.tddl.group.jdbc.TGroupStatement.executeInternal(TGroupStatement.java:144)
        at com.taobao.tddl.group.jdbc.TGroupStatement.execute(TGroupStatement.java:103)
        at com.taobao.tddl.group.jdbc.TGroupPreparedStatement.execute(TGroupPreparedStatement.java:92)
        at com.taobao.tddl.repo.mysql.spi.My_JdbcHandler.executeQuery(My_JdbcHandler.java:695)
        at com.taobao.tddl.repo.mysql.spi.My_Cursor.init(My_Cursor.java:106)
        at com.taobao.tddl.repo.mysql.handler.QueryMyHandler.handle(QueryMyHandler.java:89)
        at com.taobao.tddl.repo.mysql.handler.QueryMyHandlerWithMemcached.handleBySql(QueryMyHandlerWithMemcached.java:169)
        at com.taobao.tddl.repo.mysql.handler.QueryMyHandlerWithMemcached.handle(QueryMyHandlerWithMemcached.java:54)
        at com.taobao.tddl.executor.AbstractGroupExecutor.executeInner(AbstractGroupExecutor.java:47)
        at com.taobao.tddl.executor.AbstractGroupExecutor.execByExecPlanNode(AbstractGroupExecutor.java:36)
        at com.taobao.tddl.executor.TopologyExecutor.execByExecPlanNode(TopologyExecutor.java:74)
        at com.taobao.tddl.executor.MatrixExecutor.execByExecPlanNodeByOne(MatrixExecutor.java:800)
        at com.taobao.tddl.executor.MatrixExecutor.execByExecPlanNode(MatrixExecutor.java:789)
        at com.taobao.tddl.executor.MatrixExecutor.execute(MatrixExecutor.java:145)
        at com.taobao.tddl.matrix.jdbc.TConnection.executeSQL(TConnection.java:275)


Hsf等待线程2: 大约80个线程在等待获取createSqlStat的并发锁。

"HSFBizProcessor-DEFAULT-8-thread-719" Id=1604 WAITING on java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync@7cc0ca97
        at sun.misc.Unsafe.park(Native Method)
        -  waiting on java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync@7cc0ca97
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:189)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
        at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:943)
        at com.alibaba.druid.stat.JdbcDataSourceStat.createSqlStat(JdbcDataSourceStat.java:410)
        at com.alibaba.druid.filter.stat.StatFilter.createSqlStat(StatFilter.java:631)
        at com.alibaba.druid.filter.stat.StatFilter.statementPrepareAfter(StatFilter.java:305)
        at com.alibaba.druid.filter.FilterEventAdapter.connection_prepareStatement(FilterEventAdapter.java:146)
        at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:471)
        at com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.prepareStatement(ConnectionProxyImpl.java:375)
        at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:369)
        at com.taobao.tddl.atom.jdbc.TConnectionWrapper.prepareStatement(TConnectionWrapper.java:304)
        at com.taobao.tddl.group.jdbc.TGroupPreparedStatement.createPreparedStatementInternal(TGroupPreparedStatement.java:68)
        at com.taobao.tddl.group.jdbc.TGroupPreparedStatement.executeQueryOnConnection(TGroupPreparedStatement.java:108)
        at com.taobao.tddl.group.jdbc.TGroupStatement$4.tryOnDataSource(TGroupStatement.java:650)
        at com.taobao.tddl.group.jdbc.TGroupStatement$4.tryOnDataSource(TGroupStatement.java:632)
        at com.taobao.tddl.group.dbselector.AbstractDBSelector.tryOnDataSourceHolderWithIndex(AbstractDBSelector.java:279)
        at com.taobao.tddl.group.dbselector.AbstractDBSelector.tryExecute(AbstractDBSelector.java:434)
        at com.taobao.tddl.group.dbselector.AbstractDBSelector.tryExecute(AbstractDBSelector.java:449)
        at com.taobao.tddl.group.jdbc.TGroupStatement.executeQuery(TGroupStatement.java:505)
        at com.taobao.tddl.group.jdbc.TGroupStatement.executeInternal(TGroupStatement.java:144)
        at com.taobao.tddl.group.jdbc.TGroupStatement.execute(TGroupStatement.java:103)
        at com.taobao.tddl.group.jdbc.TGroupPreparedStatement.execute(TGroupPreparedStatement.java:92)
        at com.taobao.tddl.repo.mysql.spi.My_JdbcHandler.executeQuery(My_JdbcHandler.java:695)


代码分析

根据以上线程堆栈信息找到代码位置:

image.png

tddl使用druid连接池,下面这段是核心代码,总体流程分为四步

获取DB连接池->创建缓存sql->执行sql->释放DB连接

    protected DataSourceTryer<ResultSet> executeQueryTryer = new AbstractDataSourceTryer<ResultSet>() {
        public ResultSet tryOnDataSource(TGroupStatement stmt, DataSourceWrapper dsw, Object... args) throws SQLException {
            String sql = (String)args[0];
            Connection conn = TGroupStatement.this.tGroupConnection.createNewConnection(dsw, true);
            int lastSocketTimeout = -1;
            if (TGroupStatement.this.tGroupConnection.getNetworkTimeout() >= 0) {
                lastSocketTimeout = NetworkTimeoutUtils.getNetworkTimeout(conn);
                NetworkTimeoutUtils.setNetworkTimeout(conn, TGroupConnection.executor, TGroupStatement.this.tGroupConnection.getNetworkTimeout());
            }
            ResultSet var7;
            try {
                var7 = TGroupStatement.this.executeQueryOnConnection(conn, sql);
            } finally {
                if (lastSocketTimeout >= 0 && TGroupStatement.this.tGroupConnection.getNetworkTimeout() >= 0) {
                    try {
                        NetworkTimeoutUtils.setNetworkTimeout(conn, TGroupConnection.executor, lastSocketTimeout);
                    } catch (Throwable var14) {
                        ;
                    }
                }
            }
            return var7;

观察DB没有发现慢sql,sql执行时间在1ms以下,但是tddl日志里报了很多慢sql,并且连接池用完。

image.png

继续监控线上,发现很多sql执行时间耗在创建sql这一步

image.png

耗时位置和上午hsf线程卡住位置一致,这个地方druid做了一个并发缓存,并发请求会在此排队等待,并发高的情况下会导致耗时增加,从而导致释放连接的速度变慢

image.png

建议优化

tddl把sql缓存的逻辑放在获取连接之后会拖慢DB连接池的释放速度,可以考虑使用以下流程, sql创建缓存流程不占用DB连接池的资源。

创建缓存sql->获取DB连接池->执行sql->释放DB连接



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
存储 Java
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析(上)
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析
392 0
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析(上)
|
21天前
|
Java 数据库连接 数据库
Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销
本文深入探讨了Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销,显著提升了系统的响应速度和吞吐量。文章介绍了连接池的工作原理,并以HikariCP为例,展示了如何在Java应用中使用连接池。通过合理配置和优化,连接池技术能够有效提升应用性能。
34 1
|
4月前
|
SQL 关系型数据库 Serverless
PolarDB产品使用问题之如何控制队列中排队的SQL的等待时间
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
4月前
|
算法 编译器 C++
开发与运维线程问题之在C++的原子操作中memory_order如何解决
开发与运维线程问题之在C++的原子操作中memory_order如何解决
46 2
|
4月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
69 2
|
6月前
|
Java Spring 容器
【Spring源码】单例创建期间进行同步可能会导致死锁?
通过这个标题我们就可以思考本次的阅读线索了,看起来可以学到不少东西。1. 旧代码的死锁是怎么产生的。2. 贡献者通过改变什么来解决本次PR的问题呢?而阅读线索2的答案也显而易见,就是上文提到的通过后台线程来创建Micrometer单例...
|
安全 Java
架构系列——你用的线程是安全的吗?怎么解决?
架构系列——你用的线程是安全的吗?怎么解决?
|
SQL 关系型数据库 MySQL
数据库的锁——代码实战篇
数据库的锁——代码实战篇
|
SQL 运维 监控
MySQL死锁系列-线上死锁问题排查思路
本篇文章会讲解一下如果线上发生了死锁异常,如何去排查和处理。除了系列前文讲解的有关加锁和锁冲突的原理还,还需要对 MySQl 死锁日志和 binlog 日志进行分析。
MySQL死锁系列-线上死锁问题排查思路
|
SQL 消息中间件 监控
MyBatis引起的线程池线程打满问题排查过程
MyBatis引起的线程池线程打满问题排查过程