redis缓存一致 做延时双删
提出现象
- 做数据库更新A
- redis缓存刷新A
- 做据库更新B
- redis缓存更新B
如果正常执行1,2,3,4步骤,一切正常。但是在高并发的情况下, 执行步骤是 1,3,4,2,导致数据库和缓存不一致。
提出解决方法,做延时双删。
// 操作数据库的方法
@PostMapping("/employee/update") @ClearAndReloadCache(name = "employee") public void update(){ Employee employee = new Employee(); employee.setEmployeeId(BigInteger.valueOf(100)); employee.setHireDate(new Date()); employeeService.update(employee); }
// 注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ClearAndReloadCache {
String name() default "";
}
import javafx.concurrent.Task;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Component
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class ClearAndReloadCacheAop {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private ExecutorService executorService;
{
executorService = Executors.newCachedThreadPool();
}
@Pointcut("@annotation(com.jin.redis.ClearAndReloadCache)")
public void pointCut1() {
}
@Around(value = "pointCut1()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
Object proceed = null;
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
System.out.println(methodSignature.getName());
ClearAndReloadCache annotation = methodSignature.getMethod().getAnnotation(ClearAndReloadCache.class);
if (annotation == null) {
try {
proceed = proceedingJoinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
} else {
String keyName = annotation.name();
//模糊定义key
Set<String> keys = stringRedisTemplate.keys("*" + keyName + "*");
assert keys != null;
// 第一次删除
stringRedisTemplate.delete(keys);
// 执行数据库的操作
try {
proceed = proceedingJoinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
// 休眠 1s 是为了让数据库操作完成
TimeUnit.SECONDS.sleep(1);
// 第二次删除
Set<String> keys = stringRedisTemplate.keys("*" + keyName + "*");
assert keys != null;
stringRedisTemplate.delete(keys);
return null;
}
};
executorService.submit(task);
}
return proceed;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署