spring事务方法的同步问题

一、事务方法的同步问题

例如以下这段代码

    @RequestMapping("/test")
    @Transactional
    public int test(){
        Test test = testMapper.selectById(1);
        int max = test.getMax() + 1;
        test.setMax(max);
        testMapper.updateById(test);
        return max;
    }

当用jmeter并发访问1000次这个接口,发现max的值为532,跟预期的1000不一样。

这是因为有些请求查询不到数据库最新的值,导致的同步问题。

 

这里我使用的项目是springboot+mybatisplus+mysql

 

 

二、解决方法

1.使用select for update解决

     @Select("select * from test where id = #{id} for update")
     Test selectForUpdate(@Param("id") long id);

for update是一种行级锁,又叫排它锁,一旦用户对某个行施加了行级加锁,则该用户可以查询也可以更新被加锁的数据行,其它用户只能查询但不能更新被加锁的数据行.如果其它用户想更新该表中的数据行,则也必须对该表施加行级锁.即使多个用户对一个表均使用了共享更新,但也不允许两个事务同时对一个表进行更新,真正对表进行更新时,是以独占方式锁表,一直到提交或复原该事务为止。行锁永远是独占方式锁。

只有当出现如下之一的条件,才会释放共享更新锁:
1、执行提交(COMMIT)语句
2、退出数据库(LOG OFF)
3、程序停止运行

 

 

2.使用手动事务+同步锁synchronized

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;


    @RequestMapping("/test")
    public synchronized int test(){
        int max = 0;
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try{
            Test test = testMapper.selectById(1);
            max = test.getMax() + 1;
            test.setMax(max);
            testMapper.updateById(test);
            platformTransactionManager.commit(transactionStatus);
        }catch (Exception e){
            platformTransactionManager.rollback(transactionStatus);
        }
        return max;
    }

这里使用手动事务的原因是,spring aop中,在方法执行前开启事务,方法执行后提交事务,也就是说事务的开启和提交过程是没有加锁的,因此无法实现同步。

 

 

以下方式是无法实现同步的:

    @RequestMapping("/test")
    @Transactional
    public synchronized int test(){
        Test test = testMapper.selectById(1);
        int max = test.getMax() + 1;
        test.setMax(max);
        testMapper.updateById(test);
        return max;
    }

将数据库值清0,再次使用jmeter测试,发现max的值为647。

说明依然没有解决同步问题。

 

就算把synchronized放到方法体里也是一样无法实现同步的,锁住的只是方法里的代码块,没有锁住事务的开启和提交过程。

    private Object object = new Object();
    
    @RequestMapping("/test")
    @Transactional
    public int test(){
        int max = 0;
        synchronized (object) {
            Test test = testMapper.selectById(1);
            max = test.getMax() + 1;
            test.setMax(max);
            testMapper.updateById(test);
        }
        return max;
    }

 

 

 

3.使用乐观锁

数据库添加version字段。

public class Test {
    private Long id;
    private Integer max;
    @Version
    private Integer version;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getMax() {
        return max;
    }

    public void setMax(Integer max) {
        this.max = max;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }
}

 

    @RequestMapping("/test")
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public int test(){
        int i = 0;
        Test test = null;
        while(i<1){
            test = testMapper.selectById(1);
            test.setMax(test.getMax()+1);
            i = testMapper.updateById(test);
        }

        System.out.println(test.getMax());
        return test.getMax();
    }

这里因为使用了mybatisplus框架,只需要在bean类version属性添加@Version注释,然后使用框架提供的方法查询和修改,默认支持乐观锁。

需要注意的是,这里@Transactional注释中标明了隔离级别为读取已提交。

否则会导致在事务中查询不到数据库最新的数据,导致一直无法更新成功,一直回旋。

原因是@Transactional默认的隔离级别是根据数据库的隔离级别,由于我用的是mysql的innodb引擎,默认的隔离级别是支持可重复读的,导致在事务中,查询数据库的值都是一致的可重复读的,导致无法获取数据库最新的值。

因此将事务隔离级别改为读取已提交就可以解决

 

 

三、总结

事务方法的同步可以使用以下方法解决:

select for update

手动事务+同步锁synchronized(可将锁细化)

③乐观锁

 

并发量不是特别高的话,使用乐观锁的效率最高。

<think>嗯,用户问的是关于Spring Boot注册事务同步的内容。我需要先理解什么是事务同步,然后结合Spring Boot来回答。记得用户可能对Spring事务管理不太熟悉,所以需要先解释基本概念。 首先,事务同步应该是指在事务的不同阶段执行自定义逻辑。比如,在事务提交前或提交后做一些操作。Spring中的TransactionSynchronization接口应该就是这个用途的。需要说明如何注册这些同步器,以及它们的使用场景。 然后,用户可能想知道具体的实现步骤。比如,如何实现TransactionSynchronization接口,重写那些方法,比如afterCommit、afterCompletion等。还要提到在编程式事务中如何注册,使用TransactionSynchronizationManager.registerSynchronization()方法。 可能需要举个例子,比如记录日志,发送消息等。同时要提醒用户注意事务的传播机制和线程绑定问题,确保同步器在正确的事务上下文中执行。 另外,用户可能分不清声明式事务和编程式事务的区别,这里需要说明注册事务同步通常是在编程式事务中使用,或者在带有@Transactional注解的方法中结合使用。 还要注意事务同步的执行顺序和可能出现的异常处理,避免影响主事务。最后,可以提一下结合Spring Boot的自动配置,简化事务管理配置,比如使用JPA或JDBC时的自动事务管理器配置。 需要确保回答结构清晰,先介绍概念,再讲实现步骤,然后是示例代码和注意事项。语言要口语化,避免技术术语过多,必要时解释术语。检查是否有遗漏的关键点,比如事务同步的触发时机和使用限制。</think>在 Spring Boot 中,**事务同步(Transaction Synchronization)** 是一种机制,允许你在事务的生命周期关键阶段(如提交、回滚等)插入自定义逻辑。通过注册事务同步器(`TransactionSynchronization`),可以实现事务完成后的回调操作,例如发送消息、清理资源或异步处理等。 --- ### **核心概念** 1. **`TransactionSynchronization` 接口** Spring 提供了此接口,定义了事务事件回调方法,例如: - `afterCommit()`: 事务成功提交后触发。 - `afterCompletion(int status)`: 事务完成后触发(无论提交或回滚)。 - `beforeCommit(boolean readOnly)`: 事务提交前触发。 2. **事务同步的作用** 在事务的边界(提交/回滚)执行与事务相关的副作用操作,例如: - 事务提交后发送消息(确保消息发送与事务一致)。 - 事务回滚后清理临时数据。 - 记录事务审计日志。 --- ### **实现步骤(Spring Boot 中)** #### 1. 实现 `TransactionSynchronization` 接口 ```java public class CustomTransactionSynchronization implements TransactionSynchronization { @Override public void afterCommit() { // 事务提交后的逻辑(例如发送消息) System.out.println("Transaction committed!"); } @Override public void afterCompletion(int status) { if (status == STATUS_COMMITTED) { System.out.println("Transaction completed with commit."); } else if (status == STATUS_ROLLED_BACK) { System.out.println("Transaction rolled back."); } } } ``` #### 2. 注册事务同步器 在事务上下文中,通过 `TransactionSynchronizationManager` 注册同步器: ```java @Service public class MyService { @Autowired private PlatformTransactionManager transactionManager; @Transactional public void doSomething() { // 业务逻辑... // 注册事务同步 TransactionSynchronizationManager.registerSynchronization( new CustomTransactionSynchronization() ); } } ``` --- ### **关键注意事项** 1. **事务绑定线程** 确保在事务活动线程中注册同步器(通常在 `@Transactional` 方法内部)。 2. **事务传播机制** 同步器仅对当前事务有效,嵌套事务需注意传播行为(如 `PROPAGATION_REQUIRES_NEW` 会创建新事务)。 3. **异步处理** 若在 `afterCommit` 中执行耗时操作,建议异步化(如使用 `@Async`)。 4. **异常处理** 同步器中的异常不会影响主事务,但需自行捕获处理。 --- ### **使用场景示例** - **提交后发消息**:保证消息只在事务提交后发送(避免脏数据)。 - **资源清理**:事务回滚后删除临时文件。 - **审计日志**:记录事务提交/回滚的详细信息。 --- ### **结合 Spring Boot 特性** Spring Boot 自动配置事务管理器(如 `DataSourceTransactionManager`),简化了事务同步的集成。你只需: 1. 添加 `@EnableTransactionManagement`(默认已启用)。 2. 使用 `@Transactional` 注解标记事务方法。 3. 在事务方法中注册同步器。 通过这种方式,你可以灵活扩展事务生命周期,实现事务与其他组件的协同工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值