一、原因:
在并发请求的情况下事务中异步请求查询在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);
});
}
}
总结:
在使用时如果业务没有在事务内,则监听事务处理器不会运行,也不会有异常。