type
status
date
Apr 21, 2024 09:50 AM
slug
summary
category
tags
password
icon
什么是缓存?缓存的作用缓存的成本Redis缓存模型根据id查询商铺缓存的流程缓存首页的店铺类型缓存更新策略主动更新的三种策略操作缓存和数据库时需要考虑三个问题删除缓存还是更新缓存?如何保证缓存与数据库的操作的同时成功或失败?先操作缓存还是先操作数据库?缓存穿透定义解决方案缓存空对象布隆过滤解决缓存穿透流程图缓存雪崩定义解决方案缓存击穿定义解决方案互斥锁逻辑过期两种方案的优缺点总结RedisTemplate封装工具类缓存解决数据不一致
什么是缓存?
缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高。
缓存的作用
- 降低后端负载
- 提高读写效率,降低响应时间
缓存的成本
- 数据一致性成本
- 代码维护成本
- 运维成本
Redis缓存模型
根据id查询商铺缓存的流程
缓存首页的店铺类型
缓存的原理与前面根据 id 查询店铺信息相同。
缓存更新策略
- 内存淘汰
- 超时剔除
- 主动更新
业务场景:
- 低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存
- 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存
主动更新的三种策略
- Cache Aside Pattern(胜出)
由缓存的调用者,在更新数据库的同时更新缓存
- Read/Write Through Pattern 缓存与数据 库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性问题。
- Write Behind Caching Pattern 调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致。
操作缓存和数据库时需要考虑三个问题
虽然在实际工作中使用Cache Aside Pattern(胜出),但是还需要考虑下面的三个问题。
删除缓存还是更新缓存?
- 更新缓存:每次更新数据库都更新缓存,无效写操作较多(pass)
- 删除缓存:更新数据库时让缓存失效,查询时再更新缓存(胜出)
如何保证缓存与数据库的操作的同时成功或失败?
- 单体系统, 将缓存与数据库操作放在一个事务
- 分布式系统,利用TCC等分布式事务方案
先操作缓存还是先操作数据库?
- 先删除缓存,再操作数据库(pass)
- 先操作数据库,再删除缓存(胜出)
总结——缓存更新策略的最佳实践方案:
- 低一致性需求:使用Redis自带的内存淘汰机制
- 高一致性需求:主动更新,并以超时剔除作为兜底方案
- 读操作:
- 缓存命中则直接返回
- 缓存未命中则查询数据库,并写入缓存,同时设定超时时间
- 写操作:
- 先写数据库,然后再删除缓存
- 要确保数据库与缓存操作的原子性
- 解决商铺缓存与数据库的写一致问题
缓存穿透
定义
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
解决方案
常见的解决方案有两种:缓存空对象和布隆过滤
缓存空对象
- 优点:
- 实现简单,维护方便
- 缺点:
- 额外的内存消耗
- 可能造成短期的不致
布隆过滤
- 优点:
- 内存占用较少,没有多余key
- 缺点:
- 实现复杂
- 存在误判可能
解决缓存穿透流程图
总结:
- 缓存穿透产生的原因是什么?
用户请求的数据在缓存中和数据库中都不存在,不断发起这样的请求,给数据库带来巨大压力
- 缓存穿透的解决方案有哪些?
- 缓存
null
值 - 布隆过滤
- 增强
id
的复杂度,避免被猜测id
规律 - 做好数据的基础格式校验
- 加强用户权限校验
- 做好热点参数的限流
缓存雪崩
定义
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
缓存击穿
定义
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了, 无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案
互斥锁
- 基于互斥锁解决缓存击穿问题流程图
- 这里使用Redis中的
setnx
指令实现互斥锁,只有当值不存在时才能进行set
操作
- 锁的有效期更具体业务有关,需要灵活变动,一般锁的有效期是业务处理时长10~20倍
- 线程获取锁后,还需要查询缓存(也就是所谓的双检),这样才能够真正有效保障缓存不被击穿
- 测试
通过 Jmeter 进行压测,在5秒内发送2000个请求(理论上QPS可以达到400/sec,也就是每秒钟发送400个请求),最终吞吐量是331.3(也就是每秒钟处理134.8个请求),系统性能还是比较好的
逻辑过期
- 基于逻辑过期解决缓存击穿问题流程图
两种方案的优缺点
总结
RedisTemplate封装工具类
缓存解决数据不一致
- 为了解决数据一致性问题,我们可以选择适当的缓存更新策略:
- 以缓存主动更新(双写方案+删除缓存模式+先操作数据库后操作缓存+事务)为主,超时剔除为辅
- 查询时,先查询缓存,缓存命中直接返回,缓存未命中查询数据库并重建缓存,返回查询结果
- 更新时,先修改数据删除缓存,使用事务保证缓存和数据操作两者的原子性
- 除了会遇到数据一致性问题意外,我们还会遇到缓存穿透、缓存雪崩、缓存击穿等问题
- 对于缓存穿透,我们采用了缓存空对象解决
- 对于缓存击穿,我们分别演示了互斥锁(
setnx
实现方式)和逻辑过期两种方式解决。
- 作者:Frank
- 链接:https://blog.franksteven.me//article/redis-cache
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。