分布式缓存原理

为什么要设计缓存?

在高并发的场景下,需要通过缓存来减少数据库的压力,使得大量的访问进来就能够命中缓存只有少量的需要到数据库层。由于缓存基于内存,可支持的并发量远远大于基于硬盘的数据库。所以高并发设计,缓存的设计是必不可少的一环,对于java程序来讲,本地jvm缓存优于集中式的redis缓存

缓存能解决什么问题?

1,提升性能
绝大时候,select是出现性能问题最大的地方。大多数应用都是读多写少,再加上select中很多像jion.group.order.like等这样的丰富的语义都是很消耗性能的
分布式系统中远程调用也是很消耗性能的,网络开销,会导致整体的响应时间下降,为了挽救这样的性能开销,在业务允许的情况下(有些需要实时更新的数据除外),使用缓存是非常必要的。
2,降低数据库压力
当用户请求增多的时候数据库的压力将会大大增加,通过缓存能够大大降低数据库的压力

缓存适用场景:

1, 不需要实时更新但是又及其消耗数据库的数据。比如网站上商品销售排行榜,这种数据一天统计一次就可以了,用户不会关注其是否是实时的。
2, 需要实时更新,但是更新频率不高的数据。比如一个用户的订单列表,他肯定希望能够实时看到自己下的订单,但是大部分用户不会频繁下单。
3, 在某个时刻访问量极大且更新也很频繁的数据,这种数据有一个很典型的例子就是秒杀,在秒杀那一刻,可能有N倍于平时的流量进来,系统压力会很大,但是这种数据使用的缓存不能和普通缓存一样,这种缓存必须保证不丢失,否则会有大问题
什么时候不能使用缓存
实际在一个web站点中,大部分数据都是可以缓存的,反而不能使用缓存的是很小一部分,这类数据包括比如涉及到钱、密钥、业务关键性核心数据等。一个经验之谈,若是在设计web系统的时候,发现大部分数据都不能使用缓存,则说明设计或者架构本身出现了问题,此时需要考虑设计的合理性了。

一般来说,缓存有以下以下三种模式

Cache Aside 更新模式 同时更新缓存和数据库
Read/Write Through 更新模式 先更新缓存,缓存负责同步同步更新数据库
Write Behind Caching 更新模式 先更新缓存,缓存时异步更新数据库

Cache Aside 更新模式(同时更新缓存和数据库)

最常用的缓存模式,具体流程是:
失效:应用程序从cagche取数据,没有得到,则从数据库中取数据,成功后,放到缓存中
命中:应用程序从cache中取数据,取到后返回
更新:先把数据存到数据库中,成功后,再让缓存失效。

更新模式流程图
在这里插入图片描述
上面说的都是 先更新数据库,然后再让缓存失效,那么为什么不直接更新缓存呢?

避坑指南一(两个并发的写操作导致导致脏数据)

先更新数据库,再更新缓存。这种做法最大的问题就是两个并发的写操作导致脏数据。如下图(以Redis和Mysql为例),两个并发更新操作,数据库先更新的反而后更新缓存,数据库后更新的反而先更新缓存,这样就会造成数据库和缓存中的数据不一致,应用程序中读取的都是脏数据
在这里插入图片描述

避坑指南二(两个并发的读和写操作导致脏数据)

先删除缓存,再更新数据库。这个逻辑是错误的,因为两个并发的读和写操作导致导致脏数据。如下图(Redis和Mysql为例)。假设更新操作先删除了缓存,此时正好有一个并发的读操作,没有命中缓存后从数据库中取出老数据并且更新回缓存,这个时候更新操作也完成了数据库的更新,此时数据控和缓存中的数据不一致,应用程序中读取的都是原来的数据(脏数据)
在这里插入图片描述

避坑指南三

先更新数据库,再清楚缓存。这种做法其实不能算作是坑,在实际系统中也推荐使用这种方式。但是这种方式理论上还是可能存在问题。如下图(Redis和Mysql为例),查询操作没有命中缓存,然后查询出数据库的老数据,此时有一个并发的更新操作,更新操作在读操作之后更新了数据库中的数据并且删除了缓存中的数据。然后读操作将数据库中读取出来的老数据更新回了缓存。这样子就会造成数据库和缓存中数据不一致,应用程序中读取的就是原来的数据
在这里插入图片描述

但是,仔细想一想,这种并发的概率极低。因为这个条件需要发生在读缓存时缓存失效,而且有一个并发的写操作,实际上数据库的写操作会比读操作慢的多,而且还要加锁,而读操作必须在写操作前进入数据库操作,又要晚于写操作更新缓存,所有这些条件都具备的概率并不大。但是为了避免这种极端情况造成脏数据脏数据所产生的影响,我们还是要为缓存设置过期时间。

Read/Write Through 更新模式(先更新缓存,缓存负责同步同步更新数据库)

在上面的Cache Aside 更新模式中,应用代码需要维护两个数据库,一个是缓存(Cache),一个是数据库,而Read/Write Through 更新模式中,应用只需要维护缓存,数据库的维护工作由缓存代理了。
在这里插入图片描述
Read Through 模式就是查询操作中华更新缓存,也就是说,当缓存失效的时候,Cache Aside 模式是由调用负责把数据加载入缓存,而Read Through 则由缓存服务自己来加载
Write Through 模式和Read Through相仿,不过是在更新数据库时发生,当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后由缓存自己更新数据库(这是一个同步操作)

Write Behind Caching 更新模式(先更新缓存,缓存时异步更新数据库

)
Write Behind Caching 更新模式就是在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新到数据库,这个设计的好处就是直接操作内存速度快,因为异步,Write Behind Caching 更新模式还可以合并对同一个数据的多次操作到数据库,所以性能的提高是相当。
但是带来的问题,就是数据不是强一致性的,而且可能会丢失,另外,Write Behind Caching 更新模式实现逻辑比较复杂,因为它需要确认有哪些数据是被是被更新了的,哪些数据是需要刷到持久层上,只有在缓存需要失效的时候,才会把它真正的持久起来.
在这里插入图片描述

总结

三种缓存模式的优缺点:
Cache Aside 更新模式(同时更新缓存和数据库),实现起来比较简单,但是需要维护两个数据存储,一个是缓存,一个是数据库
Read/Write Through 更新模式(先更新缓存,缓存负责同步同步更新数据库),只需要维护一个数据存储(缓存),但是实现起来要复杂一些
Write Behind Caching 更新模式(先更新缓存,缓存时异步更新数据库
)
,和Read/Write Through更新模式类似,区别在于Write Behind Caching 更新模式的数据持久化操作是异步的,但是Read/Write Through 更新模式的数据持久化操作是同步的。优点是直接操作内存速度快,多次操作可以合并持久化到数据库,缺点是数据可能丢失,例如系统断电等。
缓存时通过牺牲强一致性来提高性能的,所以使用缓存提升性能,就是就是会有数据更新的延迟。这需要我们在设计时结合业务仔细思考是否适合用缓存。然后缓存一定要设置过期时间,这个时间太短太长都不好,太短的话请求可能会比较多的落在数据库上,这也意味着失去了缓存的优势,太长的话缓存中的脏数据会使系统长时间处于一个延迟的状态,而系统中长时间没有人访问的数据一直存在内存中不过期,浪费内存

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值