@Transactional 上的 MySQL 套接字超时行为
Posted
技术标签:
【中文标题】@Transactional 上的 MySQL 套接字超时行为【英文标题】:MySQL socket timeout behaviour on @Transactional 【发布时间】:2021-11-11 04:48:28 【问题描述】:我正在使用带有 mysql (8.0.21) 的 spring-boot (2.3.4.RELEASE)。
我在 Spring Boot 中的数据库配置是
spring.datasource.url=jdbc:mysql://localhost:3306/testDB?connectTimeout=2000&socketTimeout=10000&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=toor
spring.datasource.dbcp2.test-while-idle=true
spring.datasource.dbcp2.pool-prepared-statements=true
spring.datasource.dbcp2.validation-query=SELECT 1 from dual where @@innodb_read_only = 0
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
MySQL 连接器
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
Dbcp2
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
我测试了以下代码模板并发现了这种奇怪的行为
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Transactional
public void f1()
jdbcTemplate.queryForObject("SELECT sleep(12)", new HashMap<>(), Integer.class);
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Transactional
public void f2()
Thread.sleep(12000);
jdbcTemplate.queryForObject("SELECT count(*) from my_table", new HashMap<>(), Integer.class);
第一个函数给出了这个错误:
11:10:07.862 [qtp262911569-27] ERROR o.s.t.i.TransactionInterceptor - Application exception overridden by rollback exception
org.springframework.dao.RecoverableDataAccessException: PreparedStatementCallback; SQL [SELECT sleep(12)]; Communications link failure
The last packet successfully received from the server was 10,013 milliseconds ago. The last packet sent successfully to the server was 10,013 milliseconds ago.; nested exception is com.MySQL.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet successfully received from the server was 10,013 milliseconds ago. The last packet sent successfully to the server was 10,013 milliseconds ago.
at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:100)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:669)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:694)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:748)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:236)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:245)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:261)
而第二个工作正常。
我知道这与我在上面提到的配置中在 MySQL URL 中设置为 10 秒的套接字超时有关。但不确定幕后发生了什么。谁能帮我理解这里发生了什么?
【问题讨论】:
第一个函数很奇怪。您已将其标记为事务性,但没有事务发生。你只有一个“SELECT sleep(12)” @KausUntwale 我猜对于一个最小的工作代码示例来说足够了——即使语义是毫无意义的。但你是对的 - 中间有 sleep(12) 的真实交易的行为会很有趣。 我很困惑为什么这不仅有意义...如果您将超时设置为 10 秒(套接字超时)但提交一个需要 12 秒的操作,那么当然该操作将在大约 10 秒后中止,每次断开套接字失败。为什么它会起作用? 【参考方案1】:您可以尝试将 application.properties 更改为:-
spring.datasource.testWhileIdle = true
spring.datasource.timeBetweenEvictionRunsMillis = 60000
spring.datasource.validationQuery = SELECT 1
将执行执行查询“SELECT 1”的每分钟(60000 毫秒)测试连接。通过这种方式,我们可以保持数据库连接,定期执行验证查询,避免到达 MySQL 的 wait_timeout。
【讨论】:
以上是关于@Transactional 上的 MySQL 套接字超时行为的主要内容,如果未能解决你的问题,请参考以下文章
@Transactional 上的 Spring Hibernate LazyInitializationException
关于方法上的 Spring @Transactional 注释的一些说明