实际业务中应该如何正确使用缓存

Posted SpringForAll社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实际业务中应该如何正确使用缓存相关的知识,希望对你有一定的参考价值。

及时获取有趣有料的技术文章

本文来源:https://blog.dogchao.cn/?p=119

后文是根据沈剑的一个文章尝试进行回答。原文:https://mp.weixin.qq.com/s/DM0dKHyD3FweVSHcjabLaw

一、不同业务场景下的缓存选型

穿透型缓存?

缓存穿透是指在旁路型缓存中,db不存在数据,导致cache一直失败,将流量全部压到db上的情况。

旁路型缓存(Cache-Aside)。

在我平时的编码中,平时几乎用到的策略都是旁路策略。

什么时候用进程内缓存,什么时候用缓存服务?

如果是进程内缓存,则该缓存的上下文和生命周期应该是在该进程内。可用于多线程的一些操作。例如,一个批量任务需要用到多线程,且各个线程可能需要访问一个共享的上下文,则可以使用HashMap(如果涉及到的操作有写操作的需求,需要用ConcurrentHashMap)做一个Context,用于多个线程之间的数据共享和缓存。由于生命周期是进程级别的,所以该操作只在单机中执行。相对的,就是分布式的问题。缓存的数据范围小。一个例子是EhCache。

分布式的缓存,需要用到缓存服务。分布式的缓存的生命周期和业务以及数据相关。缓存的数据范围大,控制较进程内缓存更复杂。比如redis。

啥时候用memcache,啥时候用redis?又或者?

redis可以简单的看为强化版的memcache,基本上可以用memcache的场景下都可以使用redis。redis相比较memcache,具备了以下的特性:

  • 提供的操作类型更多。memcache只提供了key等操作,而redis提供了list,setnx 等操作。
  • redis的数据可以落地
  • redis理论上比memcache提供的缓存量更大。redis在缓存数据多的时候,存在一个swap机制。即,只缓存key,不缓存value,当get该key的时候才去磁盘里获取value。所以极端情况下,内存的要求更小。(但如果大量的发生swap则表明机器内存不够用了,在该情况下,redis不比memcache更有优势)

不过,redis的获取数据是通过socket,不如memcached直接,所以速度比memcached慢。并且,redis不支持多核操作(操作都是原子性的),减少了由并发带来的问题,性能上有所牺牲。memcached也可以通过一些手段支持分布式,不过使用的复杂度更大。

另外一种就是类似EhCache。EhCache将数据存在JVM里,速度最快,在小范围的缓存上,比其他两个都有优势。

缓存在微服务体系架构中的位置,以及设计原则?

二、关于缓存的设计折衷

  • 使用情况读>写
  • 知道数据存在不一致性。使用缓存始终会碰到数据不一致的情况。有很多手段可以缓解该情况,但是始终还是存在该情况。
  • 在整个数据流中,存在短板导致整个流程的性能问题,可以考虑在该流程中使用缓存。

引入缓存后,对读写业务流程有什么影响?

提高了性能,增加了复杂性,增加了数据的风险。

到底应该修改缓存,还是淘汰缓存?

大多数情况考虑淘汰缓存。缓存本身就是对原始数据的一个映射,缓存如果发生了更改,则表示该映射失效。可以通过手动删除该缓存或者重建缓存的key来保证缓存的淘汰。不过,还是需要关注雪崩的情况。

应该先操作数据库,还是操作缓存?

分场景。比如spring的几个级别的事务,就是这种情况。如果允许脏读等情况,可以考虑先写缓存,再写库。还是性能和数据安全上的一个折衷。还包括写缓存成功,写库失败,缓存如何淘汰,等等。比如,可以在第一次写缓存的时候将过期时间设置得很短,保证该时间段的请求能够命中缓存。写库成功后,增加该缓存的实效时间来使该缓存生效。这种情况下,写缓存写了两次,但是缓存生效的时间很快。

三、关于缓存的架构设计

通常都是db挂掉。也有应用挂掉的,比如缓存的数据来自于cpu的大量计算得出的结果,缓存实效后重新执行cpu的大量计算,导致段时间内cpu被打满。其实在缓存的后面的每一个环节都可能会挂掉。

缓存是否需要保证高可用?如何保证高可用?

不是都需要。保证高可用可以采用分级缓存。

缓存如何保证与数据库中的数据一致性?

利用分布式锁;也可以参考阿里倡导的柔性事务的思想,采用补偿机制等方式进行。用锁的复杂度低于补偿机制,性能低一些。

缓存如何保证无限量的扩展性?

数据分片。

四、各类典型业务的缓存实践

很多人说,遇到瓶颈,就用缓存。缓存如同万金油一般。缓存是架构设计中非常重要的一环。



 

 

 

 




如果资源对你有帮助的话


       
         
         
       
❤️ 给个 「在看」 ,是最大的支持❤️


以上是关于实际业务中应该如何正确使用缓存的主要内容,如果未能解决你的问题,请参考以下文章

Rails:旧数据与新数据不匹配时如何更新片段缓存

从 Apollo 缓存中读取特定类型的所有片段

MVC如何正确添加业务层

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

如何在 Django 中显式重置模板片段缓存?