Redis学习:缓存

1. 缓存跟新策略

  1. 缓存失效策略:当数据更新时,直接删除缓存中的对应数据,下次访问时重新加载最新数据到缓存中。这种策略简单直接,但可能会导致缓存雪崩问题。

  2. 定期刷新策略:定期刷新缓存中的数据,保持缓存数据与数据库中的数据同步。这种策略可以减轻缓存失效带来的压力,但可能导致缓存中的数据不是最新的。

  3. 基于事件驱动的主动更新策略:当数据更新时,发布一个事件通知缓存系统,使缓存系统能够及时更新对应的缓存数据。这种策略能够保证缓存数据的及时更新,但需要在系统中引入事件机制。

  4. 读写分离策略:将缓存中的数据与数据库中的数据分开存储,读操作优先从缓存中获取数据,写操作则更新数据库并删除或更新缓存中的数据。这种策略可以有效减轻数据库的压力,但需要保证缓存中的数据与数据库中的数据保持一致。

  5. 淘汰策略:当缓存空间不足时,根据一定的策略淘汰部分缓存数据,以腾出空间存储新的数据。常见的淘汰策略包括最近最少使用(LRU)和最少使用(LFU)等

对于低一致性需求的场景,可以使用Redis自带的内存淘汰机制;而对于高一致性需求的场景,可以结合多种缓存更新策略,以确保数据的一致性和系统的高效运行

在使用Redis缓存时,通常推荐的做法是先更新数据库,再删除缓存。这种方法可以减少数据不一致的风险

先更新数据库,再删除缓存原因如下:

  1. 先更新数据库,再删除缓存

    • 优点:在高并发场景下,虽然会有短暂的数据不一致,但最终数据会保持一致。因为数据库更新完成后,缓存中的旧数据会被删除,新的读取请求会从数据库中获取最新数据并更新缓存。
    • 缺点:在数据库更新和缓存删除之间的短暂时间内,可能会有读取到旧数据的情况。
  2. 先删除缓存,再更新数据库

    • 优点:操作简单,直接删除缓存中的旧数据。
    • 缺点:在高并发场景下,可能会导致数据不一致。例如,在缓存删除后但数据库更新前,有新的读取请求进来,这些请求会从数据库中读取旧数据并重新写入缓存,导致缓存和数据库数据不一致。

 2. 缓存穿透

缓存穿透是指当用户请求的数据在缓存和数据库中都不存在时,每次请求都会直接访问数据库,导致缓存失效,增加数据库的压力。

  1. 布隆过滤器

  2. 缓存空对象

  3. 参数校验

3. 缓存雪崩

 缓存雪崩是指在同一段时间大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库带来巨大压力。

以下是一些常见的解决办法:

  1. 设置不同的过期时间:避免大量缓存数据同时过期,可以为不同的数据设置不同的过期时间,或者在过期时间上加上一个随机数1

  2. 使用互斥锁:当缓存失效时,通过互斥锁保证同一时间只有一个请求去构建缓存,其他请求等待锁释放后再读取缓存1

  3. 服务熔断和请求限流:在Redis故障时,启动服务熔断机制,暂停业务应用对缓存服务的访问,或者启动请求限流机制,只允许少部分请求访问数据库1

  4. 构建高可靠集群:通过主从节点的方式构建Redis高可靠集群,如果主节点故障,从节点可以切换为主节点继续提供服务1

  5. 多级缓存策略:结合本地缓存(如Ehcache)和分布式缓存(如Redis),在分布式缓存失效时,本地缓存可以作为备份2

 4. 缓存击穿

 缓存击穿是指在高并发情况下,某个热点数据在缓存中失效,导致大量请求同时访问数据库,从而对数据库造成巨大压力。

解决方案:

互斥锁、逻辑过期

 

使用 jmeter 压力测试 

基于互斥锁方式解决缓存击穿:

逻辑过期方式解决缓存击穿:

5. 解决缓存穿透

ShopServiceImpl中:

注入编写的cacherClient类调用 queryWithPassThrough 方法(解决缓存穿透)

CacheClient中:

queryWithPassThrough传入参数分别是

key前缀idvalue类型

Function<ID, R> dbFallback :是一个函数式接口,用于定义一个从数据库中查询数据的方法。它接受一个类型为 ID 的参数,并返回一个类型为 R 的结果。传入

Function<ID, R> dbFallback = id -> getById(id);
或者

Function<ID, R> dbFallback = this::getById;

过期时间、时间单位

.apply 是 Function 接口中的一个固定方法。Function 是Java 8引入的一个函数式接口,定义了一个抽象方法 apply,用于将一个输入参数转换为一个输出结果。

6. 解决缓存击穿

互斥锁解决缓存击穿

ShopServiceImpl中:

CacheClient中:

queryWithMutex:参数同上

tryLock和unlock

tryLock 获得锁通过向Redis中通过setIfAbsent存入key,value来维持 若存在则 false 不存在则 true

unlock 释放锁,即删除 Redis 中的这个数据

获得锁失败则休眠然后递归

获得成功后写入 Redis,最终释放锁

逻辑过期解决缓存击穿

ShopServiceImpl中:

CacheClient中:

queryWithLogicalExpire参数同上

创建一个固定大小为10的线程池,用于执行缓存重建任务。

ExecutorService是Java中的一个接口,用于管理和控制一组异步任务的执行。它提供了线程池的功能,避免了手动创建和管理线程的复杂性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值