蘭陵N梓記

一指流沙,程序年华


  • 首页

  • 归档

  • 关于

  • 搜索
close

Cache设计

时间: 2018-06-24   |   分类: 技术     |   阅读: 1470 字 ~3分钟

一提到Cache,就想到08年我为公司写的消息缓存系统的惨痛教训。当时Redis与Memcached远还没有流行,公司对使用开源项目也是慎重,于是我和另一个同事自己撸了一个系统,但做着做着就变成一个带有强业务逻辑的Cache了。后面又扩大他的使用场景,也导致了一些问题。这个系统的要满足如下场景:

  • 针对消息对象缓存,每个消息都非常小,要高效地使用内存
  • 存在定时消息,当定时到了,需要回到业务系统中去调度
  • 消息有优先级与时序性,要支持按不同的属性来索引(消息ID,发送人,收件人等)
  • 消息量非常大,缓存需要有淘汰机制,支持淘汰的消息本地文件存储(相当于多级缓存,本地文件存储要求高效索引)

从上面的场景来,它比纯Key/Value的缓存复杂,即要高效使用内存,同一个Value缓存,存在多个Key映射,而Value只能缓存一份,Value有优先级与时序性,索引时需排序处理,又有点消息队列的诉求。

今天,我们大量在使用Redis来做缓存,Redis只作为Key/Value存储,上层复杂的缓存相关业务逻辑是在其外来叠加实现。但由于对于业务系统来说,永远都是具体情况具体分析,没有最好,只有最合适,所以也不得不要考虑通用问题:缓存穿透、缓存雪崩,缓存击穿。

缓存穿透

缓存系统一般都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统(如DB)查找。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

解决办法:

  • 对空结果缓存,缓存时间设置较短,当该key对应的数据有抛入时更新
  • 对Key进行过滤,设计Key有一定的规范,当Key满足规范时才去后端查找。
  • 布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统带来很大压力。

解决办法:

  • 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
  • 做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)

缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,导致大并发的请求可能会瞬间把后端DB压垮。这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

解决办法:

  • 使用互斥锁(mutex key):如使用Redis的SETNX(SET if Not eXists,只有不存在的时候才设置,可以利用它来实现锁的效果)
  • “提前"使用互斥锁(mutex key):在value内部设置1个超时值(timeout1), timeout1比实际的cache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。
  • “永远不过期”:对于热点数据的Key,快过期时预先加载,难点在于热点数据如何统计。
  • 过载保护:应用层的过载保护,比如API调用降级,避免对后端系统高并发访问。

参考:

  1. 基于Redis的布隆过滤器的实现
  2. 如何应对缓存穿透
  3. 缓存使用与设计系列文章–目录
#软件设计#
Scala中的符号
自定义扩展Spring Cache注解
微信扫一扫交流

标题:Cache设计
作者:兰陵子
关注:lanlingthink(览聆时刻)
声明:自由转载-非商用-非衍生-保持署名(创作共享3.0许可证)

  • 文章目录
  • 站点概览
兰陵子

兰陵子

Programmer & Architect

164 日志
4 分类
57 标签
GitHub 知乎
      • 缓存穿透
      • 缓存雪崩
      • 缓存击穿
© 2009 - 2022 蘭陵N梓記
Powered by - Hugo v0.101.0
Theme by - NexT
0%