缓存作为高并发解决方案之一,用好缓存的重要性不言而喻. 【分布式缓存】
当然在 内存与CPU交换数据中、前端缓存(vue等) 也存在缓存的使用.
缓存背景
常用的、经常访问的、不经常变化的数据. 使用缓存可以有以下好处:
- 缓存的目的:减少数据库IO,减轻服务器压力,减少网络延迟,加快页面打开速度(业务快速响应)
缓存的本质:用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库读取最新的数据
但引入缓存后,也会有新的问题出现,比如:
- 缓存代价:额外的资源支持(硬件资源、内存资源)
- 高并发缓存失效问题(缓存穿透、缓存雪崩、缓存击穿); 瞬间数据库访问量增大,甚至崩溃可能
- 缓存与数据库数据同步(缓存和数据库无法做到实时同步;redis主从无法做到实时同步)
- 缓存并发竞争:多个redis的客户端同时对一个key进行set值时(由于执行顺序引起的并发问题)
在解决以上问题之前,先梳理下 可以使用缓存的一些业务场景:
- 菜单/权限/类别/数据字典
- 数据服务(给其他业务线提供数据访问的服务;如果都查库,压力太大无法支撑)
- 某些后端服务接口,为了提升接口响应效率, 减轻后端压力
- 热点数据.
Redis应用场景
除了以上缓存场景,redis也可以用在:
- 秒杀
- 分布式锁
- 唯一ID生成
…
缓存各种问题解决
缓存一致性问题
只有在更新数据的时候 存在缓存不一致问题
当数据存入缓存,如果需要更新的时候,往往会带来一些问题:
- 要更新的时候,先更新缓存还是先更新数据库? 如何确保更新缓存和更新数据库这两个操作的原子性?
- 更新缓存的时候该怎么更新?修改还是删除?
1 | // 正常来说,有四种方案 |
a. 先淘汰缓存,再更新数据库:
问题(并发问题):先删除缓存,数据库还没有更新成功,此时如果读取缓存,缓存不存在,去数据库中读取到的是旧值,缓存不一致发生。
- 解决办法
延时双删:a. 先删除缓存; b.更新完数据库后,再定时延迟删除.
b. 先更新数据库,再淘汰缓存
问题(并发问题):更新数据库成功,如果删除缓存失败或者还没有来得及删除,那么,其他线程从缓存中读取到的就是旧值,还是会发生不一致。
- 消息队列
先更新数据库,成功后往消息队列发消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。 - 进阶版消息队列
发送引入的消息队列 —>> 使用binlog
借助消息队列的重试机制来实现,达到最终一致性的效果
问题衍生:cpu缓存、各个存储的缓存一致性问题 都有哪些问题? 怎么解决的?