• mysql和redis的同步


    mysql和redis的同步

    1 为什么使用redis做缓存

    1. 数据库链接慢,磁盘IO慢

    2. mysql线程数不够

    3. mybatis缓存,缓存在JVM中

    4. redis缓存时基于内存的,速度快

    若使用redis,如何保证redis和数据库同步问题

    • 配置redis

        redis:
          url: redis://iampw@192.168.198.129:6379
      
      • 1
      • 2

    2 方案一:基于SpringCache注解

    • @cacheable用于查询数据时,先从缓存中查找数据,如果缓存中有数据,则直接返回缓存中的数据,不会再去查询数据库;如果缓存中没有数据,则去数据库中查询数据,并将查询结果存入缓存中。

    • @cacheput用于更新数据时,先更新数据库中的数据,然后再将更新后的数据存入缓存中,以便下次查询时可以直接从缓存中获取更新后的数据,提高查询效率。

     @Override
     @Cacheable(cacheNames = "find",key = "0")
    public JSONResult find() {
         log.info("数据库启动了...");
         List<Parking> list =list();
         //将查询到的小区名称赋给车位表
         list.forEach(parking-> {
             Integer community_id = parking.getCommunity_id();
             String name = communityFegin.findNameById(community_id).getData().toString();
             System.out.println("----------"+name);
             parking.setCommunity_name(name);
         });
         return new JSONResult(true,null,200,list);
     }
     
     @Override
     @CachePut(cacheNames = "find" ,key = "0")
     public JSONResult delete(int id){
             removeById(id);
             return find();
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3 方案二: 利用"栈堆"思想管理缓存

    将索引存放在一个地方(list,zset),具体对象存放在一个地方(string)

    • 具体思路

      判断缓存中是否存在数据

      • 如果不存在数据
        • 从数据库中查询数据,并分别将索引具体对象放到缓存中
      • 如果存在数据
        • 根据索引获取对应具体对象
          • 如果对应对象过期了,根据对应索引查询该条对象重新存放到redis中
          • 如果对应对象没过期,直接从redis中查询该条数据
      @Slf4j
      @Service
      public class ParkingServiceImpl extends ServiceImpl<ParkingMapper, Parking>
              implements ParkingService {
          @Resource
          private CommunityFegin communityFegin;
          @Resource
          private StringRedisTemplate stringRedisTemplate;
          //所有索引的前缀
          private static final String ALL_PARKING_KEY = "ALL_PARKING_KEY";
          //每个索引的前缀
          private static final String PREFIX_PARKING = "PREFIX_PARKING::";
      
          @Override
          public JSONResult find() {
              //索引存放到list中
               ListOperations<String, String> keysOps = stringRedisTemplate.opsForList();
              //具体对象存放到string中
               ValueOperations<String, String> valuesOps = stringRedisTemplate.opsForValue();
      
              //遍历索引的集合
              List<String> keys = keysOps.range(ALL_PARKING_KEY, 0, -1);
              //1.如果redis中没有数据
              if (keys == null || keys.size() == 0) {
                  log.info("数据库启动了...");
                  //1.1从数据库中获取数据
                  List<Parking> list = list();
                  //将查询到的小区名称赋给车位表
                  list.forEach(parking -> {
                      //1.2 将索引存放到list中
                      keysOps.leftPush(ALL_PARKING_KEY, PREFIX_PARKING + parking.getId());
                      //1.3 将具体对象存放到string中,并设置过期时间
                      valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
                  });
                  return new JSONResult<>(true, null, 200, list);
              }
              //2.如果redis中有数据
              List<Parking> parkings = new ArrayList<>();
              keys.forEach(key -> {
                  //2.1 根据索引获得索引对应的对象
                  String value = valuesOps.get(key);
                  //2.1.1 对应对象为空的话
                  if (value == null) {
                      //单独查询该对象并放到redis中
                      //  获取key对应的id,并转为int型
                      int id = Integer.parseInt(key.replace(PREFIX_PARKING, ""));
                      //根据id查询车位
                      Parking parking = baseMapper.selectById(id);
                      //根据小区id查询小区
                      Integer community_id = parking.getCommunity_id();
                      String name = communityFegin.findNameById(community_id).getData().toString();
                      parking.setCommunity_name(name);
                      parkings.add(parking);
                      //存放到redis中
                      valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
                  } else {//2.1.2对应对象不为空的话
                      //直接查询
                      Parking parking = JSONArray.parseObject(value, Parking.class);
                      parkings.add(parking);
                  }
              });
              return new JSONResult<>(true, null, 200, parkings);
          }
      
          @Override
          public JSONResult delete(int id) {
              //索引存放到list中
              ListOperations<String, String> keysOps = stringRedisTemplate.opsForList();
              //具体对象存放到string中
              ValueOperations<String, String> valuesOps = stringRedisTemplate.opsForValue();
              //数据库如果有数据从数据库删除
              boolean flag = removeById(id);
              //从redis中删除
              if (flag) {
                  // 更新redis
                  keysOps.remove(ALL_PARKING_KEY, 1, PREFIX_PARKING + id);
                  valuesOps.getAndDelete(PREFIX_PARKING + id);
              }
              return new JSONResult<>(true, null, 200, "删除成功!");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81

    4 根据方案二进行升级-查询分页操作

    添加以下代码:

    PageHelper.startPage(page, 3);
    
    • 1
    • 此时出现问题1

    问题1:当分页查询时,redis会缓存首次查询的数据,此时出现问题:无论查询第几页都是最先开始缓存到redis中的那一页,该如何解决?

    答: 此问题是因为判断缓存中数据时,缓存中的数据为所有页数据;

    • 解决:判断时,判断查询时的数据为目标页数据即可
        	//本页前面页的数据
            int begin =(page-1)*3;
            //本页数据
            int end =page*3;
            //遍历索引的集合
            List<String> keys = keysOps.range(ALL_PARKING_KEY, begin, end);
    		if (keys == null || keys.size() == 0) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 此时出现问题2

    问题2: 如果当第一次查询的数据从第二页开始,redis会缓存第二页数据为第一页,此时该如何解决?

    初始化一个跟记录总数一样大小的数组,设置对应的值都null,查询某一页时,看该页对应记录数redis的值是否null,如果为null,从数据库查询,并替换为数据对应Id…

    • 具体思路

      • 如果是第一次查询
        • 进行初始化数组操作,只在第一次查询时执行
        • 将初次查询的数据,填充id和具体对象到具体位置
      • 不是第一次查询
        • 判断该页对应的值是否有null
          • 如果有null:连接数据库填充数据
          • 如果没有null:直接查询数据即可

    如果是第一次查询

     //判断是否为第一次查询
     if (keyList == null || keyList.size() == 0) {
         //分页查询
         PageHelper.startPage(page, 3);
         List<Parking> parkings = list();
         PageInfo<Parking> parkingPageInfo = new PageInfo<>(parkings);
         //总条数
         long total = parkingPageInfo.getTotal();
         //填充
         for (int i = 0; i < total; i++) {
             keysOps.leftPush(ALL_PARKING_KEY, "null");
         }
         //将初次查询的数据,填充进具体对象中
         int begin = (page - 1) * 3;
         for (Parking parking: parkings){
             //将查询到的小区名称赋给车位表
             Integer community_id = parking.getCommunity_id();
             String name = communityFegin.findNameById(community_id).getData().toString();
             parking.setCommunity_name(name);
             //改变对应id
             keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
             //填充value
             valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
             begin++;
         }
         return new JSONResult<>(true, null, 200, parkings);
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    如果不是第一次查询

    • 该页有对应值为null
    //不是第一次查询的话,定位到此次查询的位置
    int begin = (page-1)*3;
    List<String> keyList2 = keysOps.range(ALL_PARKING_KEY, begin, begin + 2);
    //查询对应的数据是否为null
    for (String s : keyList2) {
        if ("null".equals(s)) {
            //连接数据库填充数据
            log.info("数据库启动了...");
            //1.1从数据库中获取数据
            PageHelper.startPage(page, 3);
            List<Parking> parkings = list();
            for (Parking parking: parkings){
                //将查询到的小区名称赋给车位表
                Integer community_id = parking.getCommunity_id();
                String name = communityFegin.findNameById(community_id).getData().toString();
                parking.setCommunity_name(name);
                //改变对应id
                keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
                //填充value
               valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
                begin++;
            
            return new JSONResult<>(true, null, 200, parkings);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 该页所有值都不为null
    List<Parking> parkings = new ArrayList<>();
    keyList2.forEach(key -> {
        //2.1 根据索引获得索引对应的对象
        String value = valuesOps.get(key);
        //2.1.1 对应对象为空的话
        if (value == null) {
            //  单独查询该对象并放到redis中
            //  获取key对应的id,并转为int型
            int id = Integer.parseInt(key.replace(PREFIX_PARKING, ""));
            //根据id查询车位
            Parking parking = baseMapper.selectById(id);
            //根据小区id查询小区
            Integer community_id = parking.getCommunity_id();
            String name = communityFegin.findNameById(community_id).getData().toString();
            parking.setCommunity_name(name);
            parkings.add(parking);
            //存放到redis中
            valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
        } else {//2.1.2对应对象不为空的话
            //直接查询
            Parking parking = JSONArray.parseObject(value, Parking.class);
            parkings.add(parking);
        }
    });
    return new JSONResult<>(true, null, 200, parkings);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    总代码

        public JSONResult find(int page) {
            //索引存放到list中
            ListOperations<String, String> keysOps = stringRedisTemplate.opsForList();
            //具体对象存放到string中
            ValueOperations<String, String> valuesOps = stringRedisTemplate.opsForValue();
            //遍历索引
            List<String> keyList = keysOps.range(ALL_PARKING_KEY, 0, -1);
            //判断是否为第一次查询
            if (keyList == null || keyList.size() == 0) {
                PageHelper.startPage(page, 3);
                List<Parking> parkings = list();
                PageInfo<Parking> parkingPageInfo = new PageInfo<>(parkings);
                //总条数
                long total = parkingPageInfo.getTotal();
                //填充
                for (int i = 0; i < total; i++) {
                    keysOps.leftPush(ALL_PARKING_KEY, "null");
                }
                //将初次查询的数据,填充进具体对象中
                int begin = (page - 1) * 3;
                for (Parking parking: parkings){
                    //将查询到的小区名称赋给车位表
                    Integer community_id = parking.getCommunity_id();
                    String name = communityFegin.findNameById(community_id).getData().toString();
                    parking.setCommunity_name(name);
                    //改变对应id
                    keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
                    //填充value
                    valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
                    begin++;
                }
                return new JSONResult<>(true, null, 200, parkings);
            }
            //不是第一次查询的话,定位到此次查询的位置
            int begin = (page-1)*3;
            List<String> keyList2 = keysOps.range(ALL_PARKING_KEY, begin, begin + 2);
            //查询对应的数据是否为null
            for (String s : keyList2) {
                if ("null".equals(s)) {
                    //连接数据库填充数据
                    log.info("数据库启动了...");
                    //1.1从数据库中获取数据
                    PageHelper.startPage(page, 3);
                    List<Parking> parkings = list();
                    for (Parking parking: parkings){
                        //将查询到的小区名称赋给车位表
                        Integer community_id = parking.getCommunity_id();
                        String name = communityFegin.findNameById(community_id).getData().toString();
                        parking.setCommunity_name(name);
                        //改变对应id
                        keysOps.set(ALL_PARKING_KEY,begin,PREFIX_PARKING+parking.getId());
                        //填充value
                        valuesOps.set(PREFIX_PARKING+parking.getId(),JSONArray.toJSONString(parking),1,TimeUnit.DAYS );
                        begin++;
                    }
                    return new JSONResult<>(true, null, 200, parkings);
                }
            }
            List<Parking> parkings = new ArrayList<>();
            keyList2.forEach(key -> {
                //2.1 根据索引获得索引对应的对象
                String value = valuesOps.get(key);
                //2.1.1 对应对象为空的话
                if (value == null) {
                    //单独查询该对象并放到redis中
                    //  获取key对应的id,并转为int型
                    int id = Integer.parseInt(key.replace(PREFIX_PARKING, ""));
                    //根据id查询车位
                    Parking parking = baseMapper.selectById(id);
                    //根据小区id查询小区
                    Integer community_id = parking.getCommunity_id();
                    String name = communityFegin.findNameById(community_id).getData().toString();
                    parking.setCommunity_name(name);
                    parkings.add(parking);
                    //存放到redis中
                    valuesOps.set(PREFIX_PARKING + parking.getId(), JSONArray.toJSONString(parking), 1, TimeUnit.DAYS);
                } else {//2.1.2对应对象不为空的话
                    //直接查询
                    Parking parking = JSONArray.parseObject(value, Parking.class);
                    parkings.add(parking);
                }
            });
            return new JSONResult<>(true, null, 200, parkings);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
  • 相关阅读:
    杰理之手动配对方式【篇】
    [Python进阶] 监听键鼠:Pynput
    31.【Java (基础入门操作-----数据类型)】
    从零开始搭建React+TypeScript+webpack开发环境-性能优化
    R语言 生存分析
    阿里最新丰碑:国内第一本凤凰架构,全面构建可靠大型分布式系统
    一分钟学一个 Linux 命令 - tar
    深圳信用贷款之路:申请了10次都被拒!这三步帮你逆袭银行贷款!
    LeetCode 第8题:字符串转换整数(Python3解法)
    使用Elasticsearch与Java进行全文搜索
  • 原文地址:https://blog.csdn.net/woschengxuyuan/article/details/130857188