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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 最近遇到线上机器连接池满的情况,排查发现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连接



相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
存储 Java
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析(上)
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析
321 0
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析(上)
|
1月前
|
Java Spring 容器
【Spring源码】单例创建期间进行同步可能会导致死锁?
通过这个标题我们就可以思考本次的阅读线索了,看起来可以学到不少东西。1. 旧代码的死锁是怎么产生的。2. 贡献者通过改变什么来解决本次PR的问题呢?而阅读线索2的答案也显而易见,就是上文提到的通过后台线程来创建Micrometer单例...
41 3
|
10月前
|
前端开发 关系型数据库 MySQL
用户重复注册分析-多线程事务中加锁引发的bug
用户重复注册分析-多线程事务中加锁引发的bug
120 0
|
11月前
|
SQL 监控 Java
如何避免JDBC池和内存溢出?优化策略大揭秘!
0 目标 生成订单接口的基准场景是什么结果。 SQL的问题定位
425 0
|
12月前
|
SQL 消息中间件 监控
MyBatis引起的线程池线程打满问题排查过程
MyBatis引起的线程池线程打满问题排查过程
|
缓存 NoSQL Java
线程池:第三章:线程池的手写改造和拒绝策略以及线程池配置合理线程数
线程池:第三章:线程池的手写改造和拒绝策略以及线程池配置合理线程数
155 0
线程池:第三章:线程池的手写改造和拒绝策略以及线程池配置合理线程数
|
开发框架
数据连接池的工作机制是什么?
J2EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。
156 0
|
SQL 缓存 负载均衡
面试官:为什么数据库连接很消耗资源?我竟然答不上来。。一下懵了
面试官:为什么数据库连接很消耗资源?我竟然答不上来。。一下懵了
面试官:为什么数据库连接很消耗资源?我竟然答不上来。。一下懵了
|
测试技术
软件测试面试题:什么是并发?在lordrunner中,如何进行并发的测试?集合点失败了会怎么样?
软件测试面试题:什么是并发?在lordrunner中,如何进行并发的测试?集合点失败了会怎么样?
167 0
|
存储 安全 IDE
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析(下)
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析
255 0
2021年了,生产环境的问题你怎么解决呢?快学习下线程Dump分析(下)