SpringBoot中Mysql和Redis的数据一致性操作

193 阅读3分钟

一. 搭建一个maven工程:

  • 主体框架: SpringBoot-2.7.17, Mybatis-Plus,Druid连接池;
  • 工具: IDEA2023;
  • Java版本: 1.8;

1. pom.xml所用依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2. 配置Application.yml:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/数据库名
    username: 账号
    password: 密码

  data:
    redis:
      host: 127.0.0.1
      port: 6379
      username: *zhanghao*
      password: *mima1*
      database: 0
      lettuce:
        pool:
          max-active: 20
          max-wait: -1
          min-idle: 0
          max-idle: 5

mybatis:
  configuration:
    log-impl: org.apache.log4j.Logger
  mapper-locations: classpath:mapper/*.xml

3. 创建&配置App启动类:

@SpringBootApplication
@EnableCaching
public class testApp {
    public static void main(String[] args) {
        SpringApplication.run(testApp.class, args);
    }
}

二. 搭建大体框架:

1. Controller层:

@RestController
@RequestMapping("/test")
public class UserController
    @Resource
    private RedisService redisService;
​
    @GetMapping("/readRedis")
    public R<User> redisTest(){
        return new R<>(2000, "缓存读取成功!", redisService.rmCache());
    }
​
}

2. Service层:

public interface RedisService extends IService<User> {
    User readCache();
}

3. ServiceImpl层:

  • 做一个假数据用于Api调用测试;
@Service("redisServiceImpl")
public class RedisServiceImpl extends ServiceImpl<RedisServiceMapper, User> implements RedisService {
    static Logger log = Logger.getLogger(RedisServiceImpl.class);
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedisServiceMapper redisServiceMapper;
​
​
    @Override
    @Cacheable(value = "userCache", key = userId)
    @Transactional
    public User readCache() {
        User user = new User();
        user.setId(3);
        user.setUserName("strong");
        user.setPassWord("777777");
        user.setCreateTime(new DateTime());
        user.setUpdateTime(new DateTime());
        try {
            saveOrUpdate(user);
            redisTemplate.opsForHash().put("user", user.getId(), user);
            log.info("待更新缓存" + redisTemplate.opsForHash().get("user", user.getId()));
            log.info("缓存更新成功!");
            return (User) redisTemplate.opsForHash().get("user", 3);
        } catch (Exception e) {
            log.error("缓存更新失败: " + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }
​
}

4. Mapper层:

@Mapper
public interface RedisServiceMapper extends BaseMapper<User> {
}

5. Domain实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User implements Serializable {
    
    private static final long serialVersionUID = -6437445357327699086L;
​
    @TableId("id")
    private Integer id;
    @TableField("username")
    private String userName;
    @TableField("password")
    private String passWord;
    
}

三. ApiPost/PostMan调用接口测试:

1. 使用@CacheAble:

  • @Cacheable 对其进行注解,它会将方法返回结果存储在注解指定的缓存中:
  • 使用@Cacheable在这种情况下,如果缓存中包含所需的结果,就会返回该结果,而不会调用该方法。

调用返回数据:

{
    "code": 2000,
    "msg": "缓存读取成功!",
    "data": {
        "id": 3,
        "userName": "strong",
        "passWord": "777777",
    }
}

2. 使用@CacheEvict:

  • 缓存的数据如果不进行清理,会保留大量陈旧或未使用甚至是过期的数据。
  • 使用 @CacheEvict 注解来删除一个、多个或所有的值,以刷新缓存:
1. 新增代码:
  • Controller层:
@GetMapping("/rmCache")
public R<String> rmCache(){
    return new R<>(2000, "缓存删除成功!", redisService.rmCache());
}

  • ServiceImpl层:
@Override
@CacheEvict(value = "user", key = "0")
public String rmCache() {
    return "删除缓存成功!";
}

调用返回结果:

{
    "code": 2000,
    "msg": "缓存删除成功!",
    "data": "删除缓存成功!"
}

3. 使用@CachePut

  • @Cacheable@CachePut 的区别在于,@Cacheable 会跳过运行方法,而 @CachePut 会运行方法,然后将结果放入缓存。
1. 新增代码
  • Controller层:
@GetMapping("/putCache")
public R<User> putCache(){
    return new R<>(2000, "缓存写入成功!", redisService.putCache());
}

  • ServiceImpl层:
@Override
@Transactional
@CachePut(value = "userCache")
public User putCache() {
    User user = new User();
    user.setId(3);
    user.setUserName("strong");
    user.setPassWord("777777");
    user.setCreateTime(new DateTime());
    user.setUpdateTime(new DateTime());
    try {
        saveOrUpdate(user);
        redisTemplate.opsForHash().put("user", user.getId(), user);
        log.info("待更新缓存" + redisTemplate.opsForHash().get("user", user.getId()));
        log.info("缓存更新成功!");
        return (User) Objects.requireNonNull(redisTemplate.opsForHash().get("user", user.getId()));
    } catch (Exception e) {
        log.error("缓存更新失败: " + e.getMessage(), e);
        throw new RuntimeException(e);
    }
}

调用返回数据:

{
    "code": 2000,
    "msg": "缓存读取成功!",
    "data": {
        "id": 3,
        "userName": "strong",
        "passWord": "777777",
    }
}

4.总结:

  • 在SpringBoot工程项目中使用事务控制的方法进行数据一致性操作;
  • 数据先执行插入数据库操作,对数据库更新成功后再更新Redis的缓存数据;
  • 搭配上Spring的Cache依赖,这种方法简单易用。