事务中异步查询数据不一致问题

一、原因:

        在并发请求的情况下事务中异步请求查询在mysql事务未提交前将就数据查询出来导致往redis中缓存数据和mysql数据不一致的情况;

一、解决案例(事务扩展)

在事务中添加事务同步管理器,将异步查询更新操作放到事务提交之后

线程池对象(或者异步操作)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class ExecutorsUtil {
    /**
     * 线程池
     */
    public static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));
    @Autowired
    private UserService userService;
    @Autowired
    private RedisTemplate redisTemplate;

    public void ex(int id) {
        threadPoolExecutor.execute(() -> {
            LoginUser user = new LoginUser();
            User byId = userService.getById(id); //查询库数据
            //封装数据
            user.setUser(byId);
            //模拟延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //缓存数据
            redisTemplate.opsForValue().set(String.valueOf(id), user);
        });
    }
}

 业务对象:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Component
public class Server {
    /**
     * 数据操作
     */
    @Autowired
    private UserService userService;
    /**
     * 异步线程池对象
     */
    @Autowired
    private ExecutorsUtil executorsUtil;
    /*
     * 模拟事务
     */
    @Transactional
    public void test(int id, int id2) {
        userService.testJuc(id, id2); //数据库操作
      //添加事务同步管理器,声明异步是在事务提交后执行,解决问题
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                executorsUtil.ex(id); //调用异步更新数据
                executorsUtil.ex(id2);
            }
        });
    }
}
 总结:

事务同步管理器手动添加,如果在没事务下调用抛出异常:

java.lang.IllegalStateException: Transaction synchronization is not active

 因此在事务方法就不能被内部其他方法所调用,否则会导致异常抛出。

二、解决案例(使用事务监听)

封装事务对象:

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    private Integer id;

    public MyEvent(Integer source) {
        super(source);
        this.id = source;
    }

    public Integer getId() {
        return id;
    }

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

 业务对象:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class Server {
    /**
     * 数据操作
     */
    @Autowired
    private UserService userService;
    /**
     * 直接注入事务发布器
     */
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Transactional
    public void test(int id, int id2) {
        userService.testJuc(id, id2);
        applicationEventPublisher.publishEvent(new MyEvent(id)); //发布
        applicationEventPublisher.publishEvent(new MyEvent(id2));
    }
}

 处理器(在事务之后):

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class ExecutorsUtil {

    /**
     * 线程池
     */
    public static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));
    @Autowired
    private UserService userService;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 监听事务(在事务处理之后)
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void MyEventHandler(MyEvent myEvent){
        threadPoolExecutor.execute(() -> {
            Integer id = myEvent.getId();
            LoginUser user = new LoginUser();
            User byId = userService.getById(id); //查询库数据
            //封装数据
            user.setUser(byId);
            //模拟延迟
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //缓存数据
            redisTemplate.opsForValue().set(String.valueOf(id), user);
        });
    }
}
总结: 

在使用时如果业务没有在事务内,则监听事务处理器不会运行,也不会有异常。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何心而为殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值