缓存,是系统性能优化的一种有效手段,通过缓存系统,我们可以弥补某些性能难以提升的场景。 比如:

  • 数据库查询复杂的语句,尽管有索引帮忙,却还是相当耗性能;
  • 业务计算复杂,每次请求都耗费比较大的算力,响应较慢;
  • 读多写少,读取频率远远大于写入。

这些都是典型的可以使用缓存系统来提升系统整体性能的场景。

缓存是什么

基于内存的读写速度远远大于磁盘的前提,缓存将可重复使用的数据放入内存中,避免每次都对存储系统进行访问,使用时从内存中读取。

下面是一个常用的架构模型。

当读取数据时:

程序首先从缓存中获取数据,如果拿到数据,则直接返回给调用方。
如果缓存数据不存在,那么从数据库读出数据,写入到缓存中,同时返回给调用方。


写入更新数据时:

这里涉及到缓存跟数据库的写入时机问题,
图中展示的是应用服务器先将数据写入数据库,然后将缓存设置为失效。

缓存带来的问题

缓存虽好,却给系统引入了复杂性。

缓存失效(雪崩)

什么是雪崩,简单来说,雪崩的瞬间就是当一个请求到来时,系统还没来得及响应回去,后面的请求紧接到来,当系统响应时,请求者已经认为失败而进行重试了,这是一个不断重复放大的过程,最终造成系统连锁反应而服务不可用。

缓存系统的雪崩,就是缓存失效的时候,业务需要重新生成缓存,假设同时要好几百个请求,他们相互之间并不知道对方正在生成缓存,而都去重新生成缓存,造成系统压力过载而形成连锁反应。

这个时候,我们要考虑好的事情就算怎么去维护缓存失效问题。

请求时主动刷新

比如我们正在为一些高频率的API请求做缓存,请求到来时访问缓存系统,如果没有命中缓存,那么自然要去重新生成数据,如果命中缓存,在返回给调用者后(或者之前),检验缓存剩余时间,假设已经到达我们设置的阈值,则主动刷新缓存,避免缓存被动失效。

后台刷新

缓存不由业务进程(线程)维护,而由一个独立的后台服务来维护,定时刷新缓存。
维护时注意缓存空间,如果缓存满了,需要采取策略剔除一些缓存。

锁机制

更新缓存时,采取锁机制,保证同一时间只有一个进程在更新缓存,其他进程等待缓存更新结果,或者提前返回默认值/空值.

注意点在于,现在大多数系统都不是单机单进程系统,使用分布式集群需要引入分布式锁,以保证不同机器之间不会同时生成缓存。

缓存穿透(未命中)

缓存穿透是指,查询的数据不存在,此时缓存也不会有对应的数据,从而造成请求每次都会对存储层进行查询,缓存起不到缓解存储层压力的效果。
另外一种情况也会造成缓存穿透,就是数据是存在的,然而计算耗费时间较久,当缓存失效时,所有流量都会直接进入到存储层查询,造成穿透效果。

当我们可以确定缓存对应的数据是不存在时,将该key对应的值设置为空,那么缓存可以防止查询对存储层造成压力。

缓存热点

缓存的性能很高,但如果突然出现的大量的热点集中式访问,会导致热点数据所在的缓存资源压力大。
这种时候可以考虑将缓存数据做成多份,分散请求到不同的缓存服务器上。

参考

分布式缓存架构基础
极客时间专栏「左耳听风」「从0开始学架构」