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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 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连接



相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
8月前
|
关系型数据库 MySQL 数据库
【MySQL实战笔记】 06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?-01
【4月更文挑战第17天】MySQL的锁分为全局锁、表级锁和行锁。全局锁用于全库备份,可能导致业务暂停或主从延迟。不加锁备份会导致逻辑不一致。推荐使用`FTWRL`而非`readonly=true`因后者可能影响其他逻辑且异常处理不同。表级锁如`lock tables`限制读写并限定操作对象,常用于并发控制。元数据锁(MDL)在访问表时自动加锁,确保读写正确性。
166 31
|
8月前
|
数据库 开发者
Seata调用问题之全局异常捕获没法回滚如何解决
Seata是一款开源的分布式事务解决方案,旨在提供高效且无缝的分布式事务服务;在集成和使用Seata过程中,开发者可能会遇到不同的异常问题,本合集针对Seata常见异常进行系统整理,为开发者提供详细的问题分析和解决方案,助力高效解决分布式事务中的难题。
613 9
|
8月前
|
SQL 存储 数据库
OceanBase数据库常见问题之4.2.2写库过程中总是出现transaction is killed如何解决
OceanBase 是一款由阿里巴巴集团研发的企业级分布式关系型数据库,它具有高可用、高性能、可水平扩展等特点。以下是OceanBase 数据库使用过程中可能遇到的一些常见问题及其解答的汇总,以帮助用户更好地理解和使用这款数据库产品。
|
6月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
84 2
|
8月前
|
存储 关系型数据库 MySQL
Seata常见问题之程序读的nocas的配置如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
116 0
|
8月前
|
Kubernetes 网络协议 Java
Seata常见问题之全局事务处理中的本地会话过多 seata1.7报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
250 0
|
前端开发 关系型数据库 MySQL
用户重复注册分析-多线程事务中加锁引发的bug
用户重复注册分析-多线程事务中加锁引发的bug
153 0
|
SQL 监控 druid
事务未能正确提交导致Druid报错connection holder is null(2)
事务未能正确提交导致Druid报错connection holder is null(2)
890 0
|
SQL 存储 网络协议
事务未能正确提交导致Druid报错connection holder is null(1)
事务未能正确提交导致Druid报错connection holder is null
797 0
|
SQL 监控 Java
如何避免JDBC池和内存溢出?优化策略大揭秘!
0 目标 生成订单接口的基准场景是什么结果。 SQL的问题定位
566 0