Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析

老年人教程:MyBatis拦截器动态修改SQL(更新与插入)语句 注:本文编写与 2019年12月17日, 内容可能存在时效性问题。 数据库使用MySQL5.7 集成于SpringBoot 2.0.X , 引用国产的开源工具类Hutool 本教程建议显示大纲视图 配合食用 一 、简介 设定使用场景 任意 insert 、update 语句,都需要记录下语句的操作用户(CREATOR), 但由于系统早期设计不规范,大量SQL语句中,部分有写入CREAT... 阅读详情

1. 前言

使用MySQL数据库时,使用事务与不使用事务相比,出现问题时排查更复杂。

不使用事务时,客户端只需要请求MySQL服务一次(只考虑显式执行的SQL语句);使用事务时,客户端至少需要请求MySQL服务四次(开启事务、执行SQL语句、提交/回滚事务、恢复自动提交)。

在Java中存在一些用法会导致事务失效,有的问题比较明显可以较快定位,有的问题隐藏较深可能需要较长时间排查。

因此需要对MySQL的事务执行原理进行分析,并整理用于排查事务相关问题的快速有效的方法。

在Java应用中访问MySQL服务时,涉及Java应用、网络传输、MySQL服务这三层,在每一层都可以对执行的SQL语句与事务操作进行监控与观测,涉及的内容如下图所示:

在这里插入图片描述

在分析使用事务执行SQL语句之前,需要先分析相对简单的场景,以下对Java应用使用Spring、MyBatis、Druid、MySQL Connector,不使用事务执行SQL语句的情况进行分析。

可参考以下相关内容:

内容链接
MySQL SQL语句与事务执行及日志分析https://blog.youkuaiyun.com/a82514921/article/details/126563449
tcpdump、Wireshark抓包分析MySQL SQL语句与事务执行https://blog.youkuaiyun.com/a82514921/article/details/126563471
Spring、MyBatis、Druid、MySQL不使用事务执行SQL语句分析https://blog.youkuaiyun.com/a82514921/article/details/126563515
Spring、MyBatis、Druid、MySQL使用事务执行SQL语句分析https://blog.youkuaiyun.com/a82514921/article/details/126563542
Spring、MyBatis、Druid、MySQL执行SQL语句与事务监控https://blog.youkuaiyun.com/a82514921/article/details/126563558
数据源使用错误导致MySQL事务失效分析https://blog.youkuaiyun.com/a82514921/article/details/126563573
TiDB乐观事务、悲观事务模型验证https://blog.youkuaiyun.com/a82514921/article/details/126563502

2. 不使用事务执行SQL语句大致阶段

不使用事务执行SQL语句,每次执行SQL语句时的大致阶段如下:

从连接池借出连接(可能需要创建新连接)
执行SQL语句
归还连接至连接池

如下图所示:

在这里插入图片描述

不使用事务执行SQL语句时,主要由MyBatis完成,与Spring关系不大。

3. 示例项目

以下使用的示例项目下载地址为:https://github.com/Adrninistrator/DB-Transaction-test,使用说明可参考“README.md”,

相关的执行日志保存在DB-Transaction-test-log目录中,log-print_stack_trace_off目录中是未打印调用堆栈的日志,log-print_stack_trace_on目录中是打印调用堆栈的日志。

4. 验证环境

  • JDK版本

1.8

  • Spring版本

5.3.20

  • Mybatis版本

org.mybatis:mybatis:3.2.8
org.mybatis:mybatis-spring:1.2.2

  • Druid版本

1.2.10

  • MySQL Connector版本

8.0.29

  • MySQL版本

MariaDB 10.0.36

  • Spring事务传播机制

默认值REQUIRED

5. 不使用事务执行SQL语句详细过程

不使用事务执行SQL语句,每次执行SQL语句时的详细过程如下:

5.1. 创建连接

Druid创建连接包含以下两种情况:

5.1.1. 应用启动时创建连接

在Java应用启动时,会初始化Druid数据源,按照配置的initialSize参数,在初始化时创建指定数量的连接。该参数值可以为0,即初始化时可以不创建连接。

应用启动时创建连接,是在执行应用启动的线程中创建连接。

从Druid的DruidDataSource类的初始化方法init(),到MySQL Connector与MySQL服务进行Socket连接的调用堆栈如下:

com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource:924)
com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource:1733)
com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource:1669)
com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl:150)
com.alibaba.druid.filter.FilterEventAdapter.connection_connect(FilterEventAdapter:38)
com.alibaba.druid.filter.FilterAdapter.connection_connect(FilterAdapter:787)
com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl:156)
com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver:198)
com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl:241)
com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl:448)
com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl:818)
com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl:948)
com.mysql.cj.NativeSession.connect(NativeSession:120)
com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection:63)
com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory:153)
java.net.Socket.connect(Socket:559)

5.1.2. 连接不够时创建连接

Druid在初始化时会创建名称前缀为“Druid-ConnectionPool-Create-”的线程,该线程会等待信号;当Java应用尝试从连接池借出连接,且连接不够需要创建时,会唤醒以上线程,使其执行创建连接的操作。

  • 创建用于创建连接的线程

在DruidDataSource类的初始化方法init()中,会调用createAndStartCreatorThread()方法,在该方法中会创建名称前缀为“Druid-ConnectionPool-Create-”的线程。

以上线程对应的类为DruidDataSource$CreateConnectionThread,在run()方法中会一直循环执行,假如当前不需要创建连接,则会调用Condition对象的await()方法,等待直到被唤醒。

  • 唤醒创建连接的线程

当Java应用尝试从连接池借出连接,但连接池中的连接不够,且还未达到最大允许的连接数maxActive时,会唤醒以上线程。

从DruidDataSource类的getConnection()方法,到调用Condition对象的signal()方法以唤醒线程,调用堆栈如下:

com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:100)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:1399)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:1407)
com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl:5055)
test.db.druid_filter.DruidMonitorFilter.dataSource_getConnection(DruidMonitorFilter:59)
com.alibaba.druid.filter.FilterAdapter.dataSource_getConnection(FilterAdapter:2756)
com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl:5059)
com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource:1429)
com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource:1690)
com.alibaba.druid.pool.DruidDataSource.pollLast(DruidDataSource:2253)
com.alibaba.druid.pool.DruidDataSource.emptySignal(DruidDataSource:3890)
  • 执行创建连接操作

当名称前缀为“Druid-ConnectionPool-Create-”的线程被唤醒后,会执行创建连接的操作。

以上线程对应的类为DruidDataSource$CreateConnectionThread,其run()方法会调用DruidAbstractDataSource.createPhysicalConnection()方法,执行创建连接的操作。

后续的方法调用信息,与“应用启动时创建连接”部分相同,即创建连接的后续过程。

5.2. 从调用MyBatis的Mapper接口到SimpleExecutor

项目代码在调用MyBatis的Mapper接口时,实际调用的是MyBatis生成的代理类,后续的从连接池借出连接、执行SQL语句、归还连接至连接池阶段,均通过MyBatis完成。

参考“Configuration”https://mybatis.org/mybatis-3/configuration.html,MyBatis的defaultExecutorType参数用于设置执行器,默认值为SIMPLE,默认情况下MyBatis使用SimpleExecutor作为执行器,即没有特殊处理的执行器。

在MyBatis的SqlSessionTemplate$SqlSessionInterceptor.invoke()方法中会调用执行SQL语句的方法,该方法中后续还会执行归还连接至连接池操作。

5.2.1. 执行查询操作

执行查询操作时,会执行SimpleExecutor.doQuery()方法,如示例项目的TransactionService类的doOperate()方法在调用Ds1TaskLockMapper接口的selectForUpdate()方法时,调用堆栈如下:

test.db.transaction.service.TransactionService.doOperate(TransactionService:111)
com.sun.proxy.$Proxy40.selectForUpdate(Unknown Source)
org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy:52)
org.apache.ibatis.binding.MapperMethod.execute(MapperMethod:68)
org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate:163)
com.sun.proxy.$Proxy38.selectOne(Unknown Source)
org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate:358)
java.lang.reflect.Method.invoke(Method:498)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl:43)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl:62)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession:66)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession:102)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession:108)
org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor:77)
org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor:96)
org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor:137)
org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor:267)
org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor:59)

可以看到TaskLockMapper接口对应的代理类为com.sun.proxy.$Proxy40,还存在对代理类com.sun.proxy.$Proxy38的调用。

可以使用Arthas https://arthas.aliyun.com/zh-cn/ 的dump命令 https://arthas.aliyun.com/doc/dump.html ,对以上代理类的class文件进行dump。

  • com.sun.proxy.$Proxy40

以上Proxy40类继承了java.lang.reflect.Proxy类,实现了项目中的test.db.dao.ds1.Ds1TaskLockMapper接口。

Proxy40类在实现Ds1TaskLockMapper接口的方法时,会调用成员变量InvocationHandler h的invoke()方法。

通过调试可知,Proxy40类的成员变量InvocationHandler h类型为org.apache.ibatis.binding.MapperProxy。

在以上调用堆栈中,Proxy40.selectForUpdate()方法调用了MapperProxy.invoke()方法,与以上分析相符。

  • com.sun.proxy.$Proxy38

以上Proxy38类继承了java.lang.reflect.Proxy类,实现了org.apache.ibatis.session.SqlSession接口。

Proxy38类在实现SqlSession接口的方法时,会调用成员变量InvocationHandler h的invoke()方法。

通过调试可知,Proxy38类的成员变量InvocationHandler h类型为org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor。

在以上调用堆栈中,Proxy38.selectOne()方法调用了SqlSessionTemplate$SqlSessionInterceptor.invoke()方法方法,与以上分析相符。

5.2.2. 执行更新操作

执行更新操作时,会执行SimpleExecutor.doUpdate()方法,如示例项目的TransactionService类的doOperate()方法在调用Ds1TaskLockMapper接口的updateLockFlagNoCheck()方法时,调用堆栈如下:

test.db.transaction.service.TransactionService.doOperate(TransactionService:124)
com.sun.proxy.$Proxy40.updateLockFlagNoCheck(Unknown Source)
org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy:52)
org.apache.ibatis.binding.MapperMethod.execute(MapperMethod:54)
org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate:254)
com.sun.proxy.$Proxy38.update(Unknown Source)
org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate:358)
java.lang.reflect.Method.invoke(Method:498)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl:43)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl:62)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession:152)
org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor:71)
org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor:105)
org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor:48)

在执行更新操作时,Proxy38类的update()方法会被调用,该类为实现了org.apache.ibatis.session.SqlSession接口的代理类。

5.3. 从连接池借出连接

不使用事务执行SQL语句时,执行SQL语句之前,MyBatis从连接池获取一个可用的连接即可,对于连接没有特殊的要求。

执行查询操作时,从MyBatis的SimpleExecutor.doQuery()方法开始,到Druid从连接池借出连接操作,调用堆栈如下:

org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor:59)
org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor:72)
org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor:279)
org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction:67)
org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction:81)
org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils:80)
org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils:117)
org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils:159)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:100)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:1399)
com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource:1407)
com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl:5055)
test.db.druid_filter.DruidMonitorFilter.dataSource_getConnection(DruidMonitorFilter:59)
com.alibaba.druid.filter.FilterAdapter.dataSource_getConnection(FilterAdapter:2756)
com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl:5059)
com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource:1429)
com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource:1783)
com.alibaba.druid.pool.DruidPooledConnection.<init>(DruidPooledConnection:79)

项目代码通过MyBatis的Mapper接口到调用SimpleExecutor类部分的调用堆栈已省略。

执行更新操作时,SimpleExecutor.doUpdate()方法中也会调用prepareStatement()方法,后续的调用堆栈与以上相同。

5.3.1. 不使用事务执行SQL语句时获取连接处理

在MyBatis的SimpleExecutor类中,查询操作对应的doQuery()方法、更新操作对应的doUpdate()方法,都会调用prepareStatement()方法,再调用父类BaseExecutor的getConnection()方法获取连接,该方法会调用SpringManagedTransaction类的getConnection()方法获取连接。

在SpringManagedTransaction类的getConnection()方法中,由于未使用事务,成员变量Connection connection为null,因此需要调用openConnection()方法打开连接;

SpringManagedTransaction类的相关方法代码如下:

public Connection getConnection() throws SQLException {
  if (this.connection == null) {
    openConnection();
  }
  return this.connection;
}

private void openConnection() throws SQLException {
  this.connection = DataSourceUtils.getConnection(this.dataSource);
  this.autoCommit = this.connection.getAutoCommit();
  this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

在openConnection()方法中会调用DataSourceUtils类的getConnection()、doGetConnection()方法,在DataSourceUtils.doGetConnection()方法中,会调用TransactionSynchronizationManager.getResource()方法,由于未使用事务,因此获取到的ConnectionHolder对象为null,后续会调用DataSourceUtils.fetchConnection()方法,以获取连接。

DataSourceUtils类的相关方法代码如下:

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
	try {
		return doGetConnection(dataSource);

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
    Assert.notNull(dataSource, "No DataSource specified");

    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
    if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
        conHolder.requested();
        if (!conHolder.hasConnection()) {
            logger.debug("Fetching resumed JDBC Connection from DataSource");
            conHolder.setConnection(fetchConnection(dataSource));
        }
        return conHolder.getConnection();
    }
    // Else we either got no holder or an empty thread-bound holder here.

    logger.debug("Fetching JDBC Connection from DataSource");
    Connection con = fetchConnection(dataSource);            

5.4. 执行SQL语句

执行查询操作时,从MyBatis的SimpleExecutor.doQuery()方法开始,经过Druid,到MySQL Connector通过输出流向MySQL发送SQL语句,调用堆栈如下:

org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor:60)
org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler:73)
org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler:59)
com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement:497)
com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl:167)
com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl:3459)
com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter:440)
com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl:3461)
com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement:354)
com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement:893)
com.mysql.cj.NativeSession.execSQL(NativeSession:665)
com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol:1051)
com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol:683)
com.mysql.cj.protocol.a.NativeProtocol.send(NativeProtocol:619)
com.mysql.cj.protocol.a.TimeTrackingPacketSender.send(TimeTrackingPacketSender:50)
com.mysql.cj.protocol.a.SimplePacketSender.send(SimplePacketSender:51)
java.io.FilterOutputStream.write(FilterOutputStream:97)
java.io.BufferedOutputStream.write(BufferedOutputStream:117)

参考“Mapper XML Files”https://mybatis.org/mybatis-3/sqlmap-xml.html,MyBatis Mapper XML中,select、insert、update、delete等语句支持statementType属性,该属性可配置为STATEMENT、PREPARED或CALLABLE,分别代表使用Statement、PreparedStatement或CallableStatement,该属性默认值为PREPARED,即默认使用预编译语句PreparedStatement。

示例项目未开启服务端预编译,因此会使用客户端预编译方式执行SQL语句,如以上调用堆栈所示,使用MySQL Connector中的ClientPreparedStatement类,对应客户端预编译方式。

项目代码通过MyBatis的Mapper接口到调用SimpleExecutor类部分的调用堆栈已省略。

执行更新操作时,从SimpleExecutor.doUpdate()方法开始,到DruidPooledPreparedStatement.execute()方法的调用堆栈如下,后续的调用堆栈与以上相同:

org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor:48)
org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler:69)
org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler:44)
com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement:497)

5.5. 归还连接至连接池

不使用事务执行SQL语句时,执行SQL语句之后,MyBatis需要归还连接至连接池。

在MyBatis的SqlSessionTemplate$SqlSessionInterceptor类的invoke()方法中,在调用被代理的方法时会进行异常处理,在catch和finally代码块中会调用closeSqlSession()方法,执行关闭会话操作。

从MyBatis的Mapper接口的代理类开始,到调用Druid的归还连接至连接池,调用堆栈如下:

com.sun.proxy.$Proxy40.selectForUpdate(Unknown Source)
org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy:52)
org.apache.ibatis.binding.MapperMethod.execute(MapperMethod:68)
org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate:163)
com.sun.proxy.$Proxy38.selectOne(Unknown Source)
org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate:379)
org.mybatis.spring.SqlSessionUtils.closeSqlSession(SqlSessionUtils:170)
org.apache.ibatis.session.defaults.DefaultSqlSession.close(DefaultSqlSession:210)
org.apache.ibatis.executor.CachingExecutor.close(CachingExecutor:61)
org.apache.ibatis.executor.BaseExecutor.close(BaseExecutor:83)
org.mybatis.spring.transaction.SpringManagedTransaction.close(SpringManagedTransaction:123)
org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(DataSourceUtils:360)
org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils:393)
org.springframework.jdbc.datasource.DataSourceUtils.doCloseConnection(DataSourceUtils:406)
com.alibaba.druid.pool.DruidPooledConnection.close(DruidPooledConnection:286)
com.alibaba.druid.filter.FilterChainImpl.dataSource_recycle(FilterChainImpl:5045)
test.db.druid_filter.DruidMonitorFilter.dataSource_releaseConnection(DruidMonitorFilter:119)
com.alibaba.druid.filter.FilterAdapter.dataSource_releaseConnection(FilterAdapter:2750)
com.alibaba.druid.filter.FilterChainImpl.dataSource_recycle(FilterChainImpl:5049)
com.alibaba.druid.pool.DruidPooledConnection.recycle(DruidPooledConnection:351)
com.alibaba.druid.pool.DruidDataSource.recycle(DruidDataSource:1951)
com.alibaba.druid.pool.DruidConnectionHolder.reset(DruidConnectionHolder:296)

以上为查询操作的调用堆栈示例,更新操作的类似。

5.6. 关闭连接

Druid关闭连接包含以下两种情况:

5.6.1. 应用停止时关闭数据源

在Java应用停止时,会关闭Druid数据源,并在执行应用停止的线程中关闭连接。

从DruidDataSource类的关闭方法close(),到MySQL Connector关闭MySQL服务相关Socket的调用堆栈如下:

com.alibaba.druid.pool.DruidDataSource.close(DruidDataSource:2118)
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.close(ConnectionProxyImpl:114)
com.alibaba.druid.filter.FilterChainImpl.connection_close(FilterChainImpl:181)
test.db.druid_filter.DruidMonitorFilter.connection_close(DruidMonitorFilter:139)
com.alibaba.druid.filter.FilterAdapter.connection_close(FilterAdapter:777)
com.alibaba.druid.filter.FilterChainImpl.connection_close(FilterChainImpl:186)
com.mysql.cj.jdbc.ConnectionImpl.close(ConnectionImpl:713)
com.mysql.cj.jdbc.ConnectionImpl.realClose(ConnectionImpl:1721)
com.mysql.cj.NativeSession.quit(NativeSession:149)
com.mysql.cj.protocol.a.NativeProtocol.quit(NativeProtocol:1338)
com.mysql.cj.protocol.AbstractSocketConnection.forceClose(AbstractSocketConnection:103)
com.mysql.cj.protocol.NetworkResources.forceClose(NetworkResources:94)
java.net.Socket.close(Socket:1491)

5.6.2. 连接长时间空闲时关闭连接

Druid在初始化时,会启动DruidDataSource$DestroyConnectionThread类对应的线程,线程名前缀为“Druid-ConnectionPool-Destroy-”,该线程会查找空闲时间超过minEvictableIdleTimeMillis参数值的连接,将对应的连接关闭,以上操作执行的时间间隔通过timeBetweenEvictionRunsMillis参数控制。

从DruidDataSource$DestroyConnectionThread类的run()方法,到Druid的ConnectionProxyImpl.close()方法的调用堆栈如下:

com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource:2924)
com.alibaba.druid.pool.DruidDataSource$DestroyTask.run(DruidDataSource:2940)
com.alibaba.druid.pool.DruidDataSource.shrink(DruidDataSource:3196)
com.alibaba.druid.util.JdbcUtils.close(JdbcUtils:85)
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.close(ConnectionProxyImpl:114)

以上调用堆栈之后的内容,与“应用停止时关闭数据源”部分相同,即关闭连接的后续过程。

Druid连接池与Spring AOP监控配置实战:实现业务方法级SQL追踪 在Java企业级应用开发中,数据库连接池和面向切面编程(AOP)是构建高性能、可维护系统的两大核心技术。数据库连接池负责高效管理数据库连接资源,而Spring AOP则通过拦截特定连接点,实现了日志、事务、监控等横切关注点的统一管理。将两者结合,其技术价值在于能够将JDBC层面的SQL执行监控,与业务逻辑层的方法调用进行精准关联,从而构建从业务方法到具体SQL的完整可观测链路。这一融合方案尤其适用于需要深度分析性能瓶颈、审计数据操作行为的应用场景,例如高并发电商系统或复杂的企业级应用。通过配置Druid的` 阅读详情

相关推荐

mysql+Spring + SpringMVC + Druid + MyBatisi源码/框架分析-druid参数

Spring + SpringMVC + Druid + MyBatis ------------- sqlSessionFactory 提前加载找不到${jdbc.username}------------- 参考:https://www.cnblogs.com/java-class/p/6229920.html a.MapperScannerConfigurer 提前初始化导致 spring 注入配置文件失效 <bean class="org.mybatis.spri...

qq_15458763的博客 366

mybatis笔记

复习mybatis学习笔记 第一章:概述与入门 (一)mybatis的环境搭建 1、在MySQL创建一个数据库表用来测试数据 2、在idea用maven创建一个Java工程并导入mybatis依赖 日志可以不加。 3、创建实体类和dao的接口 4、创建mybatis的主配置文件SqlMapConfug.xml url我这里需要加上 ?characterEncoding=utf-8,防止传入的数据库数据乱码。 注意:如果是基于注解来配置的话,此处应该使用clas...

huang_yang_fan的博客 1239

Spring Boot + MyBatis + Druid整合实践项目

本文还有配套的精品资源,点击获取 简介:本示例项目"spring-boot-mybatis-druid.7z"结合了Spring Boot、MyBatis和Druid数据源,演示了在Spring Boot框架中整合这些组件来构建一个基于MySQL 5.6的应用程序。项目包含了完整配置文件、源代码、数据库脚本等,旨在帮助开发者理解和学习如何搭建、运行此类系统。关键知识点涵盖S...

weixin_32311823的博客 1025

spring事务

当某个类或方法上使用注解后,spring会基于该类生成一个代理对象,然后将该代理对象作为bean,当调用该对象的方法时,如果有事务处理,会关闭事务的自动功能,先去处理方法具体的业务逻辑,如果业务逻辑没有异常,则直接提交;如果出现异常,则回滚;

weixin_56146859的博客 533

等保: Postgresql配置ssl链接

等保: postgresql配置ssl链接完整方案和避坑指南

weixin_43557605的博客 4382

【博客汇总】UV打印机博客汇总

UV平板机博客汇总

Print World的专栏 1740

Spring学习历程---Hibernate访问数据库可脱离事务管理器

JdbcTemplate执行的语句马上提交,没有事务,所以没有必要在Spring应用中配置事务管理器,因为即使配置了,也没有用。 对于Hibernate而言,情况便有不同,因为hibernate并不会立刻发送SQL语句,只在commit 或 flush 一级缓存时,才真正的向数据库发送语句。 所以,即使底层数据库不支持事务,Hibernate的事务管理也是有一定好处的,不会对数据操作产生负面影

万豪给给的博客 756

CA51F3系列(国产)+低功耗模式

CA51F351P6芯片是基于1T 8051内核的8位微控制器,通常情况下,运行速度比传统的8051芯片快10倍,性能更加优越。内置8K Flash程序存储器,可多次重复编程的特性给用户开发带来了极大的方便。不仅保留了传统8051芯片的基本特性,还集成了12Bit ADC、LCD/LED驱动、Touch Key、16Bit PWM、 UART、RTC、低电压检测(LVD)等功能模块。支持IDLE、STOP和低速运行三种省电模式以适应不同功耗要求的应用。

WANGJIHUIwang的博客 572

Hibernate的autocommit问题

今天在网上看到文章说使用Hibernatey应该尽可能使用事务,而且说如果不使用事务,则必须在配置文件hibernate.cfg.xml里加上true才行,因为没有这句Hibernate的Session会在获得connection后自动将connection的autocommit设置为false.会导致数据库没有改变。之后看到一篇从Java Persist with Hibernate中节选的

wujianqiao0125的博客 1265

利用featureCounts实现高效转录本定量:从比对到下游分析

本文详细介绍了利用featureCounts工具进行RNA-seq转录本定量的完整流程。从软件安装、输入文件准备,到核心命令参数解析与批量样本处理,再到将生成的计数矩阵无缝衔接至DESeq2进行差异表达分析,提供了从比对到下游分析的一站式实战指南。文章重点阐述了featureCounts在效率与易用性上的优势,并分享了常见问题排查与性能优化技巧,帮助研究者高效完成转录本定量这一关键步骤。

weixin_29069131的博客 318

链路监控pinpoint3.0.3安装部署(2)

本文详细介绍了Pinpoint 3.0.3分布式链路监控系统的安装部署过程。主要内容包括:1)数据库安装(HBase、MySQL、Redis、Pinot)的详细配置步骤;2)中间件Zookeeper和Kafka的安装;3)Pinpoint三大组件的部署:Collector、Web和Batch的配置文件修改和启动脚本。文章特别强调了使用-starter-exec.jar包的重要性,并提供了各组件所需的详细参数配置,包括数据库连接、Zookeeper地址、Kafka配置等关键信息。

zouzou_888的博客 1376

mybatis事务处理

springboot加mybatis, 在方法上添加@Transactional开启事务, @Transactional(rollbackFor=Exception.class) 但是如果方法内部被try catch而又没有抛出异常,事务切面就不会捕获到异常,导致不会触发事务回滚

半吊子丶先森的博客 1148

【WRF-Urban】使用 LCZ 替换 WRF 运行中的 LUCC 数据

使用 LCZ 替换 WRF 运行中的 LUCC 数据

WW、forever的博客 988

Spring、MyBatis、Druid、MySQL执行SQL语句与事务监控

Druid支持使用自定义Filter,可对SQL语句与事务执行按照需要进行监控。使用Druid自定义Filter监控SQL语句与事务执行的效果,可参考“数据源使用错误导致MySQL事务失效分析”。在Spring XML中,可通过以下方式,在“com.alibaba.druid.pool.DruidDataSource”对应的bean中配置Druid自定义Filter。上类FilterEventAdapter为FilterAdapter的子类。...

adrninistrat0r的博客 1886

第七天 Labview进阶(7.2 与汇川PLC通信)

运行环境: Labview 2020 Autoshop v4.10.1.1 该文档中包括Labview程序和汇川PLC程序,可以完美实现Labview与汇川PLC的通信。具体的使用教程可以参考本人的相关文章,讲解详尽,请尽情享用!

weixin_38637405的博客 3527

【ComfyUI】Wan2.2 Animate 背景保留动作迁移视频生成

本文介绍了一个基于ComfyUI的高精度视频生成工作流"Wan2.2 Animate",该系统融合动作捕捉、图像拼接和LoRA加权等技术,能自动识别人脸并完成精准动作迁移,同时保持背景稳定。工作流采用模块化设计,包含输入提取、图像融合、模型加载等关键节点,核心模型Wan2.2-Animate-14B支持fp8精度与BlockSwap控制。流程分为图像输入、动作提取、多图合成等五个阶段,通过文本提示编码器和图像语义编码器实现语义驱动的动作生成,最终输出高质量视频,适用于虚拟形象驱动和

Mr数据杨 2264

Spring、MyBatis、Druid、MySQL使用事务执行SQL语句分析

使用MySQL数据库时,使用事务与不使用事务相比,出现问题时排查更复杂。不使用事务时,客户端只需要请求MySQL服务一次(只考虑显式执行的SQL语句);使用事务时,客户端至少需要请求MySQL服务四次(开启事务、执行SQL语句、提交/回滚事务、恢复自动提交)。在Java中存在一些用法会导致事务失效,有的问题比较明显可以较快定位,有的问题隐藏较深可能需要较长时间排查。因此需要对MySQL的事务执行原理进行分析,并整理用于排查事务相关问题的快速有效的方法。...

adrninistrat0r的博客 1501

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【SpringBoot框架篇】12.使用druid的monitor工具查看sql执行性能

使用druid的monitor工具查看sql执行性能

皓亮君的博客 8456

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

SpringBoot使用Mybatis执行多条SQL

在SpringBoot中实现Mybatis执行多条SQL,先Google了一波,发现只需要在连接Mysql的url后面加上这个就可以了: allowMultiQueries=true 添加后的url是这样滴: jdbc:mysql://...:3306/test?useUnicode=true&amp;amp;characterEncoding=gbk&amp;amp;zeroDateTimeBehavior=c...

biubiu2it的博客 5404

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

解决SpringBoot+Druid+Mybatis Plus 执行MySQL批量插入,更新 报错的问题

项目实际开发中,使用到了MySQL批量插入的语法,在MySQL中执行成功后,移植到项目时发生了问题,语句如下: REPLACE INTO table1( id, works_fusion_id, statistics_date, chapter_number, new_chapter, works_hot, works_like_number, ...

技术博客 6684

Spring Boot 使用p6spy 分析MyBatis-Plus 执行sql性能 出现的问题

问题 Caused by: java.lang.IllegalStateException: dbType not support : null, url jdbc:p6spy:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false at com.alibaba.druid.w...

IceStreamLab 2168

Spring Boot 集成Mybatis Mysql Druid

这次准备使用Spring Boot来集成Mybatis,数据库则使用MySql,数据库连接池采用阿里巴巴的Druid,其实可以用其他连接池的,但是看了好几篇博客都推荐用这个就试了一下。而Mybatis目前有两种配置方式,一种是传统的XML配置,一种是基于注解的配置,在下面我将会简单的介绍一下两种配置方式。

TkmTtT的博客 4786

druid 执行sql_Spring Boot从零入门8_mybatis + druid + mysql + workbench + docker 入门

1 简介2 名词术语3 相关技术用法综述4 环境搭建4.1 开发工具4.2 Docker 搭建 MySQL 环境3 手动 MyBatis 映射器 Demo 实现3.1 实现步骤3.2 实现细节4 小结5 参考资料1 简介这篇文章主要学习关系型数据库主流的技术栈,我们使用 Docker 快速搭建一个 MySQL 数据库学习环境,通过 MySQL 官方提供的 Workbench 可视化工具的...

weixin_30957307的博客 300

Spring Boot整合MyBatis

整合MyBatis之前,先搭建一个基本的Spring Boot项目开启Spring Boot。然后引入mybatis-spring-boot-starter和数据库连接驱动(这里使用关系型数据库mysql5.7.30)。

wno704的博客 728

SpringBoot集成MySQL数据库:tech-pdai-spring-demos中的JPA与MyBatis最佳实践

tech-pdai-spring-demos项目提供了Spring Framework5/SpringBoot 2.5.x的丰富示例,其中SpringBoot集成MySQL数据库的实现尤为全面,涵盖了JPA和MyBatis两大主流持久层框架的最佳实践。本文将带你快速掌握这两种技术在实际项目中的应用方法。 ## 一、环境准备:MySQL数据库配置指南 在SpringBoot项目中集成MySQL的

gitblog_00194的博客 1113

Spring Boot+MyBatis 电商项目 -03- MySQL 连接、DRUID、MyBatis-generato 配置及测试

Spring Boot + MyBatis 电商项目 - 03- 修改端口、配置 MySQL 数据库 上一篇 Spring Boot + MyBatis 电商项目 - 02- 引入 Spring Boot 依赖包 介绍了简单的创建项目,及设置路径映射。 如果你的电脑里有很多项目,有 Eclipse 开发的,有多个 Tomcat ,或者多个版本,可能会需要修改默认的 8080 端口。 一、如果你的 ...

肖朋伟 2898
上一篇: TiDB乐观事务、悲观事务模型验证
下一篇: Spring、MyBatis、Druid、MySQL使用事务执行SQL语句分析
adrninistrat0r
博客等级 码龄18年 395粉丝 136原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值