redis缓存简介以及缓存的更新策略(代码片段)

爱上口袋的天空 爱上口袋的天空     2023-01-02     410

关键词:

目录

一、什么是缓存

二、为什么要使用缓存

三、如何使用缓存

四、添加商户缓存

1、缓存模型和思路

 2、代码如下

五、缓存更新策略

 1、数据库缓存不一致解决方案:

 2、数据库和缓存不一致采用什么方案

3、Cache Aside Pattern实现

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

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

 1、加入超时时间 queryById()

2、修改更新的逻辑


一、什么是缓存

就像自行车,越野车的避震器

举个例子:越野车,山地自行车,都拥有” 避震器”, 防止车体加速后因惯性,在酷似”U” 字母的地形上飞跃,硬着陆导致的损害 , 像个弹簧一样;

同样,实际开发中,系统也需要” 避震器”, 防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪

这在实际开发中对企业讲,对产品口碑,用户评价都是致命的;所以企业非常重视缓存技术;

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

二、为什么要使用缓存

一句话:因为速度快,好用

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

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

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


三、如何使用缓存

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

浏览器缓存:主要是存在于浏览器端的缓存

应用层缓存:可以分为 tomcat 本地缓存,比如之前提到的 map,或者是使用 redis 作为缓存

数据库缓存:在数据库中有一片空间是 buffer pool,增改查数据都会先加载到 mysql 的缓存中

CPU 缓存:当代计算机最大的问题是 cpu 性能提升了,但内存读写速度没有跟上,所以为了适应当下的情况,增加了 cpu 的 L1,L2,L3 级的缓存

 


四、添加商户缓存

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

@GetMapping("/id")
public Result queryShopById(@PathVariable("id") Long id) 
    //这里是直接查询数据库
    return shopService.queryById(id);

1、缓存模型和思路

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

 2、代码如下

代码思路:如果缓存有,则直接返回,如果缓存不存在,则查询数据库,然后存入 redis。

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService 
 
    @Resource
    private StringRedisTemplate stringRedisTemplate;
 
    @Override
    public Result queryById(Long id) 
 
        // 1.从redis查询商铺缓存
        String key = CACHE_SHOP_KEY + id;
        String shopJson = stringRedisTemplate.opsForValue().get(key);
 
        // 2.判断是否存在
        if (StrUtil.isNotBlank(shopJson)) 
            // 3.存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            System.out.println("Redis");
            return Result.ok(shop);
        
 
        // 4.不存在,根据id查询数据库
        Shop shop = getById(id);
 
        // 5.不存在,返回错误
        if (shop == null) 
            return Result.fail("店铺不存在!");
        
 
        // 6.存在,写入Redis
        // 把shop转换成为JSON形式写入Redis
        System.out.println("MySQL");
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));
 
        return Result.ok(shop);
    

五、缓存更新策略

         缓存更新是 redis 为了节约内存而设计出来的一个东西,主要是因为内存数据宝贵,当我们向 redis 插入太多数据,此时就可能会导致缓存中的数据过多,所以 redis 会对部分数据进行更新,或者把他叫为淘汰更合适。

  • 内存淘汰:redis 自动进行,当 redis 内存达到咱们设定的 max-memery 的时候,会自动触发淘汰机制,淘汰掉一些不重要的数据 (可以自己设置策略方式)
  • 超时剔除:当我们给 redis 设置了过期时间 ttl 之后,redis 会将超时的数据进行删除,方便咱们继续使用缓存
  • 主动更新:我们可以手动调用方法把缓存删掉,通常用于解决缓存和数据库不一致问题

  • 内存淘汰:redis本身的事情,当redis内存快满了,它自身会部分数据剔除,但是该操作不可控。
  • 超时剔除:设置一个TTL过期时间(expire命令),到期就删除,但是一致性的强弱取决于TTL时间设置的长短
  • 主动更新(重):一改数据库,就更新缓存。
  • 低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存
  • 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存

 1、数据库缓存不一致解决方案:

        由于我们的缓存的数据源来自于数据库 , 而数据库的数据是会发生变化的 , 因此,如果当数据库中数据发生变化,而缓存却没有同步 , 此时就会有一致性问题存在 , 其后果是:

用户使用缓存中的过时数据,就会产生类似多线程数据安全问题,从而影响业务,产品口碑等;怎么解决呢?有如下几种方案

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

 2、数据库和缓存不一致采用什么方案

  • 方案一,代码最复杂,但是可靠性高
  • 方案二,将缓存与数据库看作一个整体(服务)来操作,但是这个服务不太好维护,市面上目前也没有这种东西
  • 方案三,最简单,调用者只操作缓存,缓存持久化到数据库的过程交给一个异步的线程,效率高,但是一致性难以保证!如果出现宕机,数据会丢失,所以可靠性也存在问题

综上所述:首选方案一

3、Cache Aside Pattern实现

 更新缓存 与 删除缓存 

  • 如何使用 “更新缓存” 那么我每修改一次数据库的数据时,Redis就要更新一次值,数据库更新100次,Redis就要更新100次
  • 而如果是“删除缓存” 那么当我修改数据库的数据时,Redis中的值就会被直接删除,数据库更新100次,Redis只要更新1次!!!
  • 而且使用“删除缓存”的方案,当没人来查询该数据时,我们也不用将数据库的值写入缓存redis中

综上所述:必然时选择 “删除缓存” !相当于延迟加载

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

(1)先操作缓存再操作数据库

正常情况下:假设数据库与缓存中的值为10,按下述操作最后缓存与数据库中的值都为20。

异常情况:假设数据库与缓存中的值为10,按下述操作最后缓存的值为10数据库中的值为20

 

2)先操作数据库再操作缓存

正常情况下:假设数据库与缓存中的值为10,按下述操作最后缓存与数据库中的值都为20

异常情况:假设数据库与缓存中的值为10,并且恰好此时缓存失效了(TTL到了,故为空!),先查缓存,未命中,就查询数据库得到值为10并且恰好此时在更新数据库前,线程2抢夺走CPU资源,更新数据库值为20,再删除缓存(本就为空),然后线程切换,写入缓存(这里写入的值是之前查询的,不是在更新数据库后的新值!!!故写入缓存的值为10)。

 

 

综上所述:

        上述两种情况都有可能发生!但是第二种情况发生的可能性非常小,在查询数据库后,我们紧接着是要写缓存,而写缓存一般是微秒级别的,几乎不可能在这一瞬间,数据库更新了,且就算数据库更新,写入数据的速度也比不上redis,所以我们选择第二种方案!选择:先操作数据库再操作缓存 的方式来实现


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

 1、加入超时时间 queryById()

@Override
public Result queryById(Long id) 
 
    // 1.从redis查询商铺缓存
    String key = CACHE_SHOP_KEY + id;
    String shopJson = stringRedisTemplate.opsForValue().get(key);
 
    // 2.判断是否存在
    if (StrUtil.isNotBlank(shopJson)) 
        // 3.存在,直接返回
        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
        System.out.println("Redis");
        return Result.ok(shop);
    
 
    // 4.不存在,根据id查询数据库
    Shop shop = getById(id);
 
    // 5.不存在,返回错误
    if (shop == null) 
        return Result.fail("店铺不存在!");
    
 
    // 6.存在,写入Redis
    // 把shop转换成为JSON形式写入Redis
    System.out.println("MySQL");
    // 同时添加超时时间
    stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
 
    return Result.ok(shop);

2、修改更新的逻辑

代码分析:通过之前的淘汰,我们确定了采用删除策略,来解决双写问题,当我们修改了数据之后,然后把缓存中的数据进行删除,查询时发现缓存中没有数据,则会从 mysql 中加载最新的数据,从而避免数据库和缓存不一致的问题,之后如果在查询该店铺,再会由上面的方法写入 queryById(id)

@Override
@Transactional
public Result update(Shop shop) 
    System.out.println("up");
    Long id = shop.getId();
    if (id == null) 
        return Result.fail("店铺id不能为空!");
    
    // 1.更新数据库
    updateById(shop);
 
    // 2.删除缓存
    stringRedisTemplate.delete(CACHE_SHOP_KEY + id);
 
    return Result.ok();

如何保证redis缓存与数据库的一致性?(代码片段)

目录1、四种同步策略:2、更新缓存还是删除缓存2.1更新缓存2.2删除缓存3、先操作数据库还是缓存3.1先删除缓存再更新数据库3.2先更新数据库再删除缓存4、延时双删4.1采用读写分离的架构怎么办?5、利用消息队列进行删... 查看详情

redis实现缓存一致性的原理深度解析(代码片段)

详细介绍了Redis实现缓存一致性的三种方式,以及他们的优缺点。文章目录1先更新数据库,然后再删除缓存2先删除缓存,然后再更新数据库3采用延时双删策略4为什么是删除缓存首先要明白,缓存和数据库数据之... 查看详情

探讨下如何更好的使用缓存——redis缓存的特殊用法以及与本地缓存一起构建多级缓存的实现(代码片段)

本篇文章,我们就一起聊一聊如何来更好的使用缓存,探寻下如何降低缓存交互过程的性能损耗、如何压缩缓存的存储空间占用、如何保证多个操作命令原子性等问题的解决策略,让缓存在项目中可以发挥出更佳的效果。大家好... 查看详情

redisredis的缓存使用技巧(商户查询缓存)(代码片段)

文章目录1.什么是缓存2.添加Redis缓存2.1缓存工作模型2.2代码实现3.缓存更新策略3.1缓存更新策略类型3.2主动更新策略3.3超时剔除和主动更新缓存实现4.缓存穿透4.1基本介绍4.2通过缓存空对象解决缓存穿透问题5.缓存雪崩6.缓存击穿6... 查看详情

redisredis的缓存使用技巧(商户查询缓存)(代码片段)

文章目录1.什么是缓存2.添加Redis缓存2.1缓存工作模型2.2代码实现3.缓存更新策略3.1缓存更新策略类型3.2主动更新策略3.3超时剔除和主动更新缓存实现4.缓存穿透4.1基本介绍4.2通过缓存空对象解决缓存穿透问题5.缓存雪崩6.缓存击穿6... 查看详情

redis高级:数据删除与淘汰策略,主从复制,哨兵模式,集群以及企业级解决方案(代码片段)

...ter集群结构设计4.3Cluster集群结构搭建5.企业级解决方案5.1缓存预热5.2缓存雪崩5.3缓存击穿5.4缓存穿透5.4缓存穿透1.数据删除与淘汰策略1.1过期数据1.1.1Redis中的数据特征Redis是一种内存级数据库,所有数据均存放在内存中,... 查看详情

redis与mysql双写一致性(代码片段)

双写一致性时为了保证Redis缓存与MySQL数据库中的数据一样我们对Redis中没有的数据,MySQL怎么回写呢?我们用双检加锁策略这样只要第一个请求发过来,后面的请求就不会发送到MySQL,直接从Redis中获取缓存数据就可以了。 为... 查看详情

redis7高级之缓存双写一致性之更新策略探讨(代码片段)

1.缓存双写一致性如果redis中有数据需要和数据库中的值相同如果redis中无数据数据库中的值是最新值,且准备回写redis缓存按照操作分只读缓存读写缓存同步直写策略写数据库后也同步写redis缓存,缓存中的数据和数据中... 查看详情

redis缓存使用技巧(代码片段)

缓存能够有效加速应用的访问速度,同时可以降低后端负载,在应用架构中起着至关重要的作用,本文主要介绍缓存使用的一些技巧。缓存更新策略LRU/LFU/FIFO算法剔除场景:数据一致性要求较低原理:缓存使用量超过了预设值,... 查看详情

redis之缓存穿透击穿雪崩问题与缓存删除淘汰策略(代码片段)

一、缓存问题与解决缓存穿透缓存穿透是指查询缓存和DB中都不存在的数据。缓存穿透示例:publicStationfindProjectStation(LongstationId)//从缓存中查询Stationstation=(Station)redisTemplate.boundHashOps("project_station").get(stationId);if(station==null)// 查看详情

redis之缓存穿透击穿雪崩问题与缓存删除淘汰策略(代码片段)

一、缓存问题与解决缓存穿透缓存穿透是指查询缓存和DB中都不存在的数据。缓存穿透示例:publicStationfindProjectStation(LongstationId)//从缓存中查询Stationstation=(Station)redisTemplate.boundHashOps("project_station").get(stationId);if(station==null)// 查看详情

redis缓存双写一致性(代码片段)

...is与Mysql双写一致性canal配置流程代码案例双写一致性理解缓存操作细分缓存一致性多种更新策略挂牌报错,凌晨升级先更新数据库,在更新缓存先删除缓存,在更新数据库先更新数据库,在删除缓存延迟双删策略总结双写一致性Redis与... 查看详情

为什么说redis是单线程的以及redis为什么这么快!(代码片段)

一、前言近乎所有与Java相关的面试都会问到缓存的问题,基础一点的会问到什么是“二八定律”、什么是“热数据和冷数据”,复杂一点的会问到缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题,这些看似不常见... 查看详情

动手写一个lru缓存(代码片段)

...RecentlyUsed的简写,字面意思则是最近最少使用。通常用于缓存的淘汰策略实现,由于缓存的内存非常宝贵,所以需要根据某种规则来剔除数据保证内存不被占满。在redis的数据淘汰策略中就包含LRU淘汰算法如何实现一个完整的LRU... 查看详情

flask-web缓存redis——架构缓存模式淘汰策略雪崩穿透(代码片段)

一、缓存的架构计算机体系结构中的缓存:多级缓存构建本地缓存方法:使用全局变量,一般适用于保存非常非常高频的数据项目的方案SQLAlchemy起到一定的本地缓存作用在同一请求中多次相同的查询只查询数据库一... 查看详情

day750.redis缓存淘汰替换策略:redis是如何工作的-redis核心技术与实战(代码片段)

Redis缓存淘汰替换策略Hi,阿昌来也,今天学习记录的是关于Redis缓存淘汰替换策略。Redis缓存使用内存来保存数据,避免业务应用从后端数据库中读取数据,可以提升应用的响应速度。那么,如果把所有要访问... 查看详情

redis缓存穿透击穿雪崩到底是个啥?7张图告诉你(代码片段)

目录一、缓存是什么?二、缓存的作用和成本1、缓存的作用:2、缓存的成本:三、缓存作用模型1、根据id查询数据缓存流程四、缓存更新策略1、内存淘汰2、超时剔除3、主动更新五、缓存穿透解决方法:六、缓... 查看详情

缓存数据库redis之三:内存淘汰策略及优化(代码片段)

目录一、Redis的内存淘汰策略  1.1.概念  1.2.策略一:全局的键空间选择性移除  1.3.策略二:设置过期时间的键空间选择性移除  1.4.LRU、LFU和volatile-ttl都是近似随机算法 1.4.1.LRU算法 1.4.2.LFU算法1.5.过期删除策略1.6.AOF... 查看详情