Rumah >pangkalan data >Redis >Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda
Dalam kes konkurensi berbilang benang, dengan mengandaikan terdapat dua permintaan pengubahsuaian pangkalan data, untuk memastikan ketekalan data antara pangkalan data dan redis,
Dalam pelaksanaan permintaan pengubahsuaian, anda perlu melata mengubah suai data dalam Redis selepas mengubah suai pangkalan data.
Permintaan 1: A mengubah suai data pangkalan data B mengubah suai data Redis
Permintaan 2: C mengubah suai data pangkalan data D mengubah suai data Redis
Dalam situasi serentak, akan ada A —> ;> D — > Situasi B
(Pastikan anda memahami bahawa susunan pelaksanaan berbilang set operasi atom secara serentak oleh benang mungkin bertindih)
Nota: Anda mesti tahu bahawa jadual data yang kerap diubah suai tidak sesuai untuk menggunakan Redis, kerana hasil daripada strategi pemadaman berganda adalah untuk memadamkan data yang disimpan dalam Redis, dan pertanyaan seterusnya akan menanyakan pangkalan data. Oleh itu, Redis menggunakan cache data yang membaca jauh lebih banyak daripada perubahan.
Langkah pelaksanaan penyelesaian pemadaman berganda tertunda
1> Padamkan cache2> Kemas kini pangkalan data
3. Mengapakah terdapat kelewatan 500 milisaat?
3> 🎜>4> Padamkan cache
4. Mengapa anda perlu memadam cache dua kali?
2. Amalan kod
<!-- redis使用 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
/** *延时双删 **/ @Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.METHOD) public @interface ClearAndReloadCache { String name() default ""; }
ClearAndReloadCacheAspect menangguhkan pemadaman berganda bagi aspek
@Aspect @Component public class ClearAndReloadCacheAspect { @Autowired private StringRedisTemplate stringRedisTemplate; /** * 切入点 *切入点,基于注解实现的切入点 加上该注解的都是Aop切面的切入点 * */ @Pointcut("@annotation(com.pdh.cache.ClearAndReloadCache)") public void pointCut(){ } /** * 环绕通知 * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 * @param proceedingJoinPoint */ @Around("pointCut()") public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ System.out.println("----------- 环绕通知 -----------"); System.out.println("环绕通知的目标方法名:" + proceedingJoinPoint.getSignature().getName()); Signature signature1 = proceedingJoinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature)signature1; Method targetMethod = methodSignature.getMethod();//方法对象 ClearAndReloadCache annotation = targetMethod.getAnnotation(ClearAndReloadCache.class);//反射得到自定义注解的方法对象 String name = annotation.name();//获取自定义注解的方法对象的参数即name Set<String> keys = stringRedisTemplate.keys("*" + name + "*");//模糊定义key stringRedisTemplate.delete(keys);//模糊删除redis的key值 //执行加入双删注解的改动数据库的业务 即controller中的方法业务 Object proceed = null; try { proceed = proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } //开一个线程 延迟1秒(此处是1秒举例,可以改成自己的业务) // 在线程中延迟删除 同时将业务代码的结果返回 这样不影响业务代码的执行 new Thread(() -> { try { Thread.sleep(1000); Set<String> keys1 = stringRedisTemplate.keys("*" + name + "*");//模糊删除 stringRedisTemplate.delete(keys1); System.out.println("-----------1秒钟后,在线程中延迟删除完毕 -----------"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); return proceed;//返回业务代码的值 } }
3, application.yml
server: port: 8082 spring: # redis setting redis: host: localhost port: 6379 # cache setting cache: redis: time-to-live: 60000 # 60s datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: root password: 1234 # mp setting mybatis-plus: mapper-locations: classpath*:com/pdh/mapper/*.xml global-config: db-config: table-prefix: configuration: # log of sql log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # hump map-underscore-to-camel-case: true
DROP TABLE IF EXISTS `user_db`; CREATE TABLE `user_db` ( `id` int(4) NOT NULL AUTO_INCREMENT, `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user_db -- ---------------------------- INSERT INTO `user_db` VALUES (1, '张三'); INSERT INTO `user_db` VALUES (2, '李四'); INSERT INTO `user_db` VALUES (3, '王二'); INSERT INTO `user_db` VALUES (4, '麻子'); INSERT INTO `user_db` VALUES (5, '王三'); INSERT INTO `user_db` VALUES (6, '李三');
/** * 用户控制层 */ @RequestMapping("/user") @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/get/{id}") @Cache(name = "get method") //@Cacheable(cacheNames = {"get"}) public Result get(@PathVariable("id") Integer id){ return userService.get(id); } @PostMapping("/updateData") @ClearAndReloadCache(name = "get method") public Result updateData(@RequestBody User user){ return userService.update(user); } @PostMapping("/insert") public Result insert(@RequestBody User user){ return userService.insert(user); } @DeleteMapping("/delete/{id}") public Result delete(@PathVariable("id") Integer id){ return userService.delete(id); } }6, UserService
/** * service层 */ @Service public class UserService { @Resource private UserMapper userMapper; public Result get(Integer id){ LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getId,id); User user = userMapper.selectOne(wrapper); return Result.success(user); } public Result insert(User user){ int line = userMapper.insert(user); if(line > 0) return Result.success(line); return Result.fail(888,"操作数据库失败"); } public Result delete(Integer id) { LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getId, id); int line = userMapper.delete(wrapper); if (line > 0) return Result.success(line); return Result.fail(888, "操作数据库失败"); } public Result update(User user){ int i = userMapper.updateById(user); if(i > 0) return Result.success(i); return Result.fail(888,"操作数据库失败"); } }
Letakkan titik putus dan simulasikan A thread Selepas pemadaman pertama dilakukan, sebelum A selesai mengemas kini pangkalan data, satu lagi thread B mengakses ID=10 dan membaca data lama.
Gunakan pemadaman kedua, selepas menetapkan masa tunda yang sesuai mengikut senario perniagaan, selepas pemadaman cache berjaya dua kali, The output Redis akan kosong. Apa yang dibaca adalah data sebenar pangkalan data, dan tidak akan ada ketidakkonsistenan antara cache baca dan pangkalan data. 4 Kejuruteraan KodKod teras ditunjukkan dalam kotak merah
Atas ialah kandungan terperinci Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!