黑马店评-04缓存更新策略,保证MySQL数据库中的数据和Redis中缓存的数据一致性

缓存更新策略(数据一致)

更新策略

缓存更新是Redis为了节约内存而设计出来的机制,当我们向Redis插入太多数据时就会导致缓存中的数据过多,所以Redis会对部分数据进行更新即淘汰

  • 低一致性需求(数据长久不发生变化): 使用内存淘汰机制,例如店铺类型信息的查询缓存,因为这部分数据很长一段时间都不需要更新
  • 高一致性需求: 使用主动更新机制,写入缓存的时候需要设置超时时间,例如店铺详情查询的缓存

主动更新实现

主动更新策略一般有如下几种方案

人工编码的三大问题

更新数据库后,删除缓存还是更新缓存?

  • 更新缓存: 每次更新数据库都更新缓存,如果中间没有人执行查询操作(写多读少),那么这些对缓存的更新动做就是无效的(用户根本就不会去缓存中查询数据)
  • 删除缓存: 更新数据库时把缓存删掉,只有用户下次做查询操作时才会更新缓存(用户发现Redis中没有数据就会去查询数据库同时把查询到的数据写入缓存)

如何保证缓存与数据库的操作的同时成功或失败?

  • 在单体系统中将缓存与数据库操作放在一个事务
  • 在分布式系统中利用TCC等分布式事务方案

先操作缓存还是先操作数据库?

  • 虽然这二者都存在线程安全问题,但是由于删除缓存的操作很快,更新数据库的操作相对较慢, 所以先操作数据库再删除缓存出现线程安全问题的概率相对较低

实现商铺缓存与数据库一致

根据店铺Id查询店铺时如果缓存未命中则查询数据库,将查询到的店铺信息写入Redis的同时设置缓存的超时时间

java 复制代码
public static final Long CACHE_SHOP_TTL = 30L;
java 复制代码
@Override
public Result queryById(Long id) {
    // 先从Redis中查询对应的店铺缓存,这里的常量值是固定的前缀+店铺id
    String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
    // 如果在Redis中查询到了店铺信息(String类型的JSON字符串)则转为Shop类型直接返回
    if (StrUtil.isNotBlank(shopJson)) {
        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
        return Result.ok(shop);
    }
    // 在Redis中没查询到对应的店铺缓存信息则根据店铺Id去数据库中查询店铺信息
    Shop shop = getById(id);
    // 在数据库中也查不到就返回一个错误信息或者返回空(根据业务需求)
    if (shop == null){
        return Result.fail("店铺不存在!!");
    }
    // 在数据库中查到了店铺信息,则将shop对象转为json字符串存入redis同时设置缓存的超时时间(如30分钟)
    String jsonStr = JSONUtil.toJsonStr(shop);
    stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, jsonStr,CACHE_SHOP_TTL, TimeUnit.MINUTES);
    // 最终把查询到的店铺信息返回给前端
    return Result.ok(shop);
}

根据店铺Id修改店铺时先修改数据库再删除缓存来解决双写问题,更新和删除通过事务去控制, 业务逻辑我们依旧是写在Service层的实现类中

java 复制代码
// 更新商铺信息
@PutMapping
public Result updateShop(@RequestBody Shop shop) {
    // 直接将请求体中JSON格式的店铺信息写入数据库,这样在缓存有效期内,即使数据库中的店铺信息修改了,下次获取的还是之前缓存中的店铺信息
    shopService.updateById(shop);
    return Result.ok();
}
java 复制代码
// 更新商铺信息
@PutMapping
public Result updateShop(@RequestBody Shop shop) {
    // 先将请求体中JSON格式的店铺信息写入数据库,再删除缓存,保证缓存中的店铺信息和数据库中的店铺信息一致性
    return shopService.update(shop);
}

public interface IShopService extends IService<Shop> {
    Result update(Shop shop);

}

@Override
@Transactional
public Result update(Shop shop) {
    // 首先先判一下店铺Id是否为空
    if (shop.getId() == null){
        return Result.fail("店铺id不能为空!!");
    }
    // 先修改数据库中对应的店铺信息
    updateById(shop);
    // 再将Redis中对应店铺的缓存删除
    stringRedisTemplate.delete(CACHE_SHOP_KEY + shop.getId());
    return Result.ok();
}

重启服务器进行测试,如访问餐厅时会将该餐厅的数据缓存到Redis中,然后使用POSTMAN携带JSON数据发送PUT请求http://localhost:8080/api/shop/更新餐厅信息

  • 店铺的信息修改后就不会再出现在Redis缓存中了, 只有我们再次访问时才会去数据库中查询店铺信息并写入缓存
JSON 复制代码
{
  "area": "大关",
  "openHours": "10:00-22:00",
  "sold": 4215,
  "address": "金华路锦昌文华苑29号",
  "comments": 3035,
  "avgPrice": 80,
  "score": 37,
  "name": "yunqing茶餐厅",
  "typeId": 1,
  "id": 1
}
相关推荐
0xDevNull42 分钟前
MySQL数据冷热分离详解
后端·mysql
科技小花1 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸1 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain1 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希2 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神2 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员2 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java2 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿2 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴2 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存