对不经常变动的数据集合添加Redis缓存

目录

前言

什么是缓存

如何使用缓存

添加商户缓存

缓存模型和思路

实现代码

问题分析

解决方案

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

实现代码


前言

什么是缓存

缓存( Cache),就是数据交换的缓冲区 ,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码

缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力

实际开发过程中,企业的数据量,少则几十万,多则几千万,这么大数据量,如果没有缓存来作为"避震器",系统是几乎撑不住的,所以企业会大量运用到缓存技术

但是缓存也会增加代码复杂度和运营的成本

以上图片是查询商户列表,我们每点击一次商铺,就会读取一次数据库,但这些数据并不是经常变动的,所以添加缓存后我只需要读取一次数据库

如何使用缓存

实际开发中,会构筑多级缓存来使系统运行速度进一步提升,例如:本地缓存与redis中的缓存并发使用

  • 浏览器缓存:主要是存在于浏览器端的缓存
  • **应用层缓存:**可以分为tomcat本地缓存,比如之前提到的map,或者是使用redis作为缓存
  • **数据库缓存:**在数据库中有一片空间是 buffer pool,增改查数据都会先加载到mysql的缓存中
  • **CPU缓存:**当代计算机最大的问题是 cpu性能提升了,但内存读写速度没有跟上,所以为了适应当下的情况,增加了cpu的L1,L2,L3级的缓存

添加商户缓存

在我们查询商户信息时,是直接操作从数据库中去进行查询的,直接查询数据库那肯定慢,所以我们需要增加缓存

java 复制代码
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {
    //这里是直接查询数据库
    return shopService.queryById(id);
}

缓存模型和思路

标准的操作方式就是查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis。

实现代码

java 复制代码
    @Override
    public Result queryById(Long id) {
        String key = CACHE_SHOP_KEY + id;
        //从redis查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //判断是否存在
        if (StrUtil.isNotBlank(shopJson)) {
            //存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //不存在,根据id查询数据库
        Shop shop = getById(id);
        //不存在,返回错误
        if (shop == null) {
            return Result.fail("店铺不存在! ");
        }
        //存在,写入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));
        //返回
        return Result.ok(shop);
    }

问题分析

虽然我们查询店铺的时候只对数据库查询了一次,然后将数据保存到redis中,纳闷用户再次进入店铺时就在redis中查找而非数据库,如果店铺进行了更新,用户依然获取的是redis中的数据而非最新数据,就造成了数据库与缓存不一致的问题

解决方案

  1. Cache Aside Pattern 人工编码方式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案
  2. Read/Write Through Pattern : 由系统本身完成,数据库与缓存的问题交由系统本身去处理
  3. Write Behind Caching Pattern :调用者只操作缓存,其他线程去异步处理数据库,实现最终一致

如果采用第一个方案,那么假设我们每次操作数据库后,都操作缓存,但是中间如果没有人查询,那么这个更新动作实际上只有最后一次生效,中间的更新动作意义并不大,我们可以把缓存删除,等待再次查询时,将缓存中的数据加载出来

  • 删除缓存还是更新缓存?
    • 更新缓存:每次更新数据库都更新缓存,无效写操作较多
    • 删除缓存:更新数据库时让缓存失效,查询时再更新缓存
  • 如何保证缓存与数据库的操作的同时成功或失败?
    • 单体系统,将缓存与数据库操作放在一个事务
    • 分布式系统,利用TCC等分布式事务方案

应该具体操作缓存还是操作数据库,我们应当是先操作数据库,再删除缓存,**原因在于,**如果你选择第一种方案,在两个线程并发来访问时,假设线程1先来,他先把缓存删了,此时线程2过来,他查询缓存数据并不存在,此时他写入缓存,当他写入缓存后,线程1再执行更新动作时,实际上写入的就是旧的数据,新的数据被旧数据覆盖了。

  • 先操作缓存还是先操作数据库?
    • 先删除缓存,再操作数据库
    • 先操作数据库,再删除缓存

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

实现代码

java 复制代码
    @Override
    public Result queryById(Long id) {
        String key = CACHE_SHOP_KEY + id;
        //从redis查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //判断是否存在
        if (StrUtil.isNotBlank(shopJson)) {
            //存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //不存在,根据id查询数据库
        Shop shop = getById(id);
        //不存在,返回错误
        if (shop == null) {
            return Result.fail("店铺不存在! ");
        }
        //存在,写入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
        //返回
        return Result.ok(shop);
    }

我们确定了采用删除策略, 来解决双写问题, 当我们修改了数据之后,然后把缓存中的数据进行删除,查询时发现缓存中没有数据,则会从mysql中加载最新的数据,从而避免数据库和缓存不一致的问题

java 复制代码
    @Override
    @Transactional
    public Result update(Shop shop) {
        Long id = shop.getId();
        if (id == null) {
            return Result.fail("店铺id不能为空");
        }
        updateById(shop);
        stringRedisTemplate.delete(CACHE_SHOP_KEY + id);
        return Result.ok();
    }
相关推荐
qianshanxue115 分钟前
0-3论软件设计模式及其应用、2016-已写(观察者通知,命令-控制指令,适配器-兼容,工厂-多种数据库)
数据库·设计模式
小许学java16 分钟前
Spring事务和事务传播机制
java·数据库·spring·事务
小无名呀19 分钟前
视图(View)
数据库·mysql
lkbhua莱克瓦2422 分钟前
MySQL介绍
java·开发语言·数据库·笔记·mysql
teacher伟大光荣且正确40 分钟前
关于Qt QReadWriteLock(读写锁) 以及 QSettings 使用的问题
java·数据库·qt
钱彬 (Qian Bin)1 小时前
项目实践13—全球证件智能识别系统(内网离线部署大模型并调用)
数据库·postgresql·fastapi·ubuntu24.04·离线部署·qwen3大模型
hgz07101 小时前
索引的原理
数据库
尋有緣1 小时前
力扣614-二级关注者
大数据·数据库·sql·oracle
TG:@yunlaoda360 云老大1 小时前
华为云国际站代理商的DDM支持哪些拆分算法?
数据库·算法·华为云
@淡 定2 小时前
主流缓存中间件对比:Redis vs Memcached
redis·缓存·中间件