日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

環境:Spring5.3.23

Spring事務超時是指一個事務在執行中最長的允許時間。如果事務在超時時間內未能完成,則會自動回滾。超時時間可以通過設置來控制,以確保事務在規定的時間內完成或回滾,避免數據一致性問題。

在工作中你有配置事務的超時時間嗎?如何進行配置事務超時時間?

1. 配置事務超時時間

注解方式:

// 這里單位是s
@Transactional(timeout = 2)
public void save() {
}

編程方式1:

@Resource
private PlatformTransactionManager tm ;


DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setTimeout(2) ;

編程方式2:

@Resource
private PlatformTransactionManager tm ;
public void update() {
  TransactionTemplate template = new TransactionTemplate(tm) ;
  template.setTimeout(2) ;
  template.execute(new TransactionCallback<Object>() {
    @Override
    public Object doInTransaction(TransactionStatus status) {
      // ...
      return null ;
    }
  }) ;
}

以上3種方式讀可以進行事務超時的設置,什么情況下才能算是事務超時呢?

2. 準備環境

準備一張2000w數據的表

Spring事務超時到底是怎么回事?圖片

表字段信息如下:

Spring事務超時到底是怎么回事?圖片

此表除主鍵外沒有任何的索引。

Spring事務超時到底是怎么回事?圖片

3. 模擬事務超時

  • 測試1

統計查詢表的數據量,將該操作放到一個事務中,同時設置事務的超時時間。

// 將超時時間設置為20s
@Transactional(timeout = 20)
public void query() {
  long start = System.currentTimeMillis() ;
  jdbcTemplate.execute("select count(*) from p_user") ;
  System.out.println("耗時:" + (System.currentTimeMillis() - start) + "毫秒") ;
}

執行結果:

耗時:3198毫秒

接下來將超時時間改成3s

執行結果如下:

13:56:01.425 [mAIn] WARN  c.zaxxer.hikari.pool.ProxyConnection - HikariPool-1 - Connection com.MySQL.cj.jdbc.ConnectionImpl@504ecd marked as broken because of SQLSTATE(null), ErrorCode(0)
com.mysql.cj.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request
  at com.mysql.cj.jdbc.exceptions.SQLExceptionsMApping.translateException(SQLExceptionsMapping.JAVA:113)
  at com.mysql.cj.jdbc.StatementImpl.checkCancelTimeout(StatementImpl.java:2167)

從異常信息看到拋出了超時異常。這里的超時是sql執行的超時,是由我們的驅動程序拋出的異常。

  • 測試2

模擬其它非數據庫操作耗時,而這個是在執行數據庫操作之前

@Transactional(timeout = 2)
public void query() {
  long start = System.currentTimeMillis() ;
  try {
    TimeUnit.SECONDS.sleep(3) ;
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  // 執行非常簡單的操作
  jdbcTemplate.execute("select 1") ;
  System.out.println("耗時:" + (System.currentTimeMillis() - start) + "毫秒") ;
}

執行結果:

14:08:44.000 [main] DEBUG o.s.jdbc.core.JdbcTemplate - Executing SQL statement [select 1]
Exception in thread "main" org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Wed Oct 11 14:08:42 CST 2023
  at org.springframework.transaction.support.ResourceHolderSupport.checkTransactionTimeout(ResourceHolderSupport.java:155)

拋出了超時異常,而這個異常是由Spring框架拋出的。

  • 測試3

模擬其它非數據庫操作耗時,而這個是在執行數據庫操作之后,適當調整上面的代碼順序

@Transactional(timeout = 2)
public void query() {
  long start = System.currentTimeMillis() ;
  jdbcTemplate.execute("select 1") ;
  try {
    TimeUnit.SECONDS.sleep(3) ;
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  System.out.println("耗時:" + (System.currentTimeMillis() - start) + "毫秒") ;
}

執行結果:

耗時:3015毫秒

程序正常運行

  • 測試4

在測試3的基礎上,最后再次執行數據庫相關的操作

@Transactional(timeout = 2)
public void query() {
  long start = System.currentTimeMillis() ;
  jdbcTemplate.execute("select 1") ;
  try {
    TimeUnit.SECONDS.sleep(3) ;
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  System.out.println("耗時:" + (System.currentTimeMillis() - start) + "毫秒") ;
  // 再次執行數據庫相關操作
  jdbcTemplate.execute("select 1") ;
}

執行結果:

耗時:3024毫秒
14:14:38.257 [main] DEBUG o.s.jdbc.core.JdbcTemplate - Executing SQL statement [select 1]
Exception in thread "main" org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Wed Oct 11 14:14:37 CST 2023
  at org.springframework.transaction.support.ResourceHolderSupport.checkTransactionTimeout(ResourceHolderSupport.java:155)

第一個數據庫操作,沒有拋出異常,直到第二個執行時拋出了異常。

總結:      事務方法開始執行時就開始計時,在執行到數據庫操作時判斷當前的執行時間點是否已經超過了設置的超時時間,如果是則拋出Timeout異常。

4. 事務超時原理

在開始一個事務時會在DataSourceTransactionManager#doBegin方法中設置超時時間

protected void doBegin(Object transaction, TransactionDefinition definition) {
  int timeout = determ.NETimeout(definition);
  if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    // 如果注解@Transactional中設置了timeout,則設置超時時間
    txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
  }
}
protected int determineTimeout(TransactionDefinition definition) {
  if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
    return definition.getTimeout();
  }
  return getDefaultTimeout();
}

當通過JdbcTemplate操作數據庫時,還會執行如下操作

public class JdbcTemplate {
  private <T> T execute(StatementCallback<T> action, boolean closeResources) {
    Statement stmt = null;
    try {
      stmt = con.createStatement();
      // 配置Statement對象,這其中會設置超時時間
      applyStatementSettings(stmt);
      // ...
      return result;
    }
  }  
  protected void applyStatementSettings(Statement stmt) throws SQLException {
    // ...
    // getQueryTimeout方法返回的是當前JdbcTemplate對象中設置d餓超時時間
    DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
  }
}

DataSourceUtils工具類

public abstract class DataSourceUtils {
  public static void applyTimeout(Statement stmt, @Nullable DataSource dataSource, int timeout) throws SQLException {
    ConnectionHolder holder = null;
    if (dataSource != null) {
      holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
    }
    // 如果當前事務執行配置了超時時間
    if (holder != null && holder.hasTimeout()) {
      // 剩余事務超時將覆蓋指定的值。
      stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
    }
    else if (timeout >= 0) {
      // No current transaction timeout -> apply specified value.
      stmt.setQueryTimeout(timeout);
    }
  }  
}

ResourceHolderSupport類

public abstract class ResourceHolderSupport implements ResourceHolder { 
  public int getTimeToLiveInSeconds() {
    double diff = ((double) getTimeToLiveInMillis()) / 1000;
    int secs = (int) Math.ceil(diff);
    // 檢查超時時間
    checkTransactionTimeout(secs <= 0);
    return secs;
  }
  private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
    if (deadlineReached) {
      // 設置事務回滾
      setRollbackOnly();
      // 拋出異常
      throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
    }
  }
}

分享到:
標簽:Spring
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定