Rumah > pangkalan data > Redis > Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

PHPz
Lepaskan: 2023-05-29 08:53:08
ke hadapan
2116 orang telah melayarinya

    1. Senario perniagaan

    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)

    1

    A Ubah suai pangkalan data Data akhirnya disimpan ke Redis, dan C juga mengubah suai data pangkalan data selepas A.

    Pada masa ini, terdapat ketidakselarasan antara data dalam Redis dan data dalam pangkalan data Dalam proses pertanyaan seterusnya, Redis akan disemak terlebih dahulu untuk masa yang lama, mengakibatkan situasi yang serius di mana pertanyaan itu. data bukanlah data sebenar dalam pangkalan data.

    2. Penyelesaian

    Apabila menggunakan Redis, anda perlu mengekalkan konsistensi Redis dan data pangkalan data Salah satu penyelesaian yang paling popular ialah strategi pemadaman berganda tertunda.

    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 cache

    2> Kemas kini pangkalan data
    3> 🎜>4> Padamkan cache

    3. Mengapakah terdapat kelewatan 500 milisaat?

    Kami perlu melengkapkan operasi kemas kini pangkalan data sebelum pemadaman Redis kedua. Bayangkan jika tiada langkah ketiga, terdapat kebarangkalian tinggi bahawa selepas dua operasi pemadaman Redis selesai, data dalam pangkalan data belum dikemas kini, jika ada permintaan untuk mengakses data, masalahnya kami sebutkan di awal akan muncul soalan itu.

    4. Mengapa anda perlu memadam cache dua kali?

    Jika kami tidak mempunyai operasi pemadaman kedua dan terdapat permintaan untuk mengakses data pada masa ini, ia mungkin data Redis yang belum diubah suai sebelum ini Selepas operasi pemadaman dilaksanakan, Redis akan kosong. Apabila permintaan masuk, ia akan Pangkalan data akan diakses Pada masa ini, data dalam pangkalan data adalah data yang dikemas kini, memastikan konsistensi data.

    2. Amalan kod

    1 Perkenalkan kebergantungan Redis dan SpringBoot

    <!-- 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>
    Salin selepas log masuk

    2. Tulis anotasi dan aspek aop tersuai

    ClearAndReloadCache delay double Delete annotation

    /**
     *延时双删
     **/
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Target(ElementType.METHOD)
    public @interface ClearAndReloadCache {
        String name() default "";
    }
    Salin selepas log masuk

    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;//返回业务代码的值
        }
    }
    Salin selepas log masuk

    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
    Salin selepas log masuk

    4, skrip user_db.sql

    untuk Data ujian pengeluaran >
    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, &#39;张三&#39;);
    INSERT INTO `user_db` VALUES (2, &#39;李四&#39;);
    INSERT INTO `user_db` VALUES (3, &#39;王二&#39;);
    INSERT INTO `user_db` VALUES (4, &#39;麻子&#39;);
    INSERT INTO `user_db` VALUES (5, &#39;王三&#39;);
    INSERT INTO `user_db` VALUES (6, &#39;李三&#39;);
    Salin selepas log masuk

    5, UserController

    /**
     * 用户控制层
     */
    @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);
        }
    }
    Salin selepas log masuk

    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,"操作数据库失败");
        }
    }
    Salin selepas log masuk
    3 Pengesahan ujian

    1, ID=10, tambah data baharu

    2 Apabila menanyakan pangkalan data untuk kali pertama, Redis akan menyimpan hasil pertanyaan

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

    3

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

    4 ID pangkalan data akses pertama ialah 10, dan simpan hasilnya dalam Redis

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

    5 Untuk nama pengguna yang sepadan dengan 10 (sahkan pangkalan data dan skim ketidakkonsistenan cache)

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

    Skim pengesahan ketidakkonsistenan pangkalan data dan cache:

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertundaLetakkan 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.

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

    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.

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertunda

    4 Kejuruteraan Kod

    Cara SpringBoot AOP Redis melaksanakan fungsi pemadaman berganda tertundaKod 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!

    Label berkaitan:
    sumber:yisu.com
    Kenyataan Laman Web ini
    Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
    Tutorial Popular
    Lagi>
    Muat turun terkini
    Lagi>
    kesan web
    Kod sumber laman web
    Bahan laman web
    Templat hujung hadapan